diff --git a/CREDITS b/CREDITS index 0bf31eac6dc2..787564bc248b 100644 --- a/CREDITS +++ b/CREDITS @@ -3382,7 +3382,7 @@ S: Germany N: Geert Uytterhoeven E: geert@linux-m68k.org -W: http://home.tvd.be/cr26864/ +W: http://users.telenet.be/geertu/ P: 1024/862678A6 C51D 361C 0BD1 4C90 B275 C553 6EEA 11BA 8626 78A6 D: m68k/Amiga and PPC/CHRP Longtrail coordinator D: Frame buffer device and XF68_FBDev maintainer @@ -3392,8 +3392,8 @@ D: Amiga Buddha and Catweasel chipset IDE D: Atari Falcon chipset IDE D: Amiga Gayle chipset IDE D: mipsel NEC DDB Vrc-5074 -S: Emiel Vlieberghlaan 2A/21 -S: B-3010 Kessel-Lo +S: Haterbeekstraat 55B +S: B-3200 Aarschot S: Belgium N: Chris Vance diff --git a/Documentation/DMA-mapping.txt b/Documentation/DMA-mapping.txt index ee4bb73683cd..10bf4deb96aa 100644 --- a/Documentation/DMA-mapping.txt +++ b/Documentation/DMA-mapping.txt @@ -194,7 +194,7 @@ document for how to handle this case. Finally, if your device can only drive the low 24-bits of address during PCI bus mastering you might do something like: - if (pci_set_dma_mask(pdev, 0x00ffffff)) { + if (pci_set_dma_mask(pdev, DMA_24BIT_MASK)) { printk(KERN_WARNING "mydev: 24-bit DMA addressing not available.\n"); goto ignore_this_device; @@ -212,7 +212,7 @@ functions (for example a sound card provides playback and record functions) and the various different functions have _different_ DMA addressing limitations, you may wish to probe each mask and only provide the functionality which the machine can handle. It -is important that the last call to pci_set_dma_mask() be for the +is important that the last call to pci_set_dma_mask() be for the most specific mask. Here is pseudo-code showing how this might be done: diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile index 7d87dd73cbe4..5a2882d275ba 100644 --- a/Documentation/DocBook/Makefile +++ b/Documentation/DocBook/Makefile @@ -2,7 +2,7 @@ # This makefile is used to generate the kernel documentation, # primarily based on in-line comments in various source files. # See Documentation/kernel-doc-nano-HOWTO.txt for instruction in how -# to ducument the SRC - and how to read it. +# to document the SRC - and how to read it. # To add a new book the only step required is to add the book to the # list of DOCBOOKS. diff --git a/Documentation/DocBook/kernel-api.tmpl b/Documentation/DocBook/kernel-api.tmpl index 8c9c6704e85b..ca02e04a906c 100644 --- a/Documentation/DocBook/kernel-api.tmpl +++ b/Documentation/DocBook/kernel-api.tmpl @@ -322,7 +322,6 @@ X!Earch/i386/kernel/mca.c The Filesystem for Exporting Kernel Objects !Efs/sysfs/file.c -!Efs/sysfs/dir.c !Efs/sysfs/symlink.c !Efs/sysfs/bin.c diff --git a/Documentation/DocBook/libata.tmpl b/Documentation/DocBook/libata.tmpl index 5bcbb6ee3bc0..f869b03929db 100644 --- a/Documentation/DocBook/libata.tmpl +++ b/Documentation/DocBook/libata.tmpl @@ -705,7 +705,7 @@ and other resources, etc. ata_scsi_error() - ata_scsi_error() is the current hostt->eh_strategy_handler() + ata_scsi_error() is the current transportt->eh_strategy_handler() for libata. As discussed above, this will be entered in two cases - timeout and ATAPI error completion. This function calls low level libata driver's eng_timeout() callback, the diff --git a/Documentation/acpi-hotkey.txt b/Documentation/acpi-hotkey.txt index 744f1aec6553..38040fa37649 100644 --- a/Documentation/acpi-hotkey.txt +++ b/Documentation/acpi-hotkey.txt @@ -30,7 +30,7 @@ specific hotkey(event)) echo "event_num:event_type:event_argument" > /proc/acpi/hotkey/action. The result of the execution of this aml method is -attached to /proc/acpi/hotkey/poll_method, which is dnyamically +attached to /proc/acpi/hotkey/poll_method, which is dynamically created. Please use command "cat /proc/acpi/hotkey/polling_method" to retrieve it. diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 495858b236b6..293fed113dff 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -71,14 +71,6 @@ Who: Mauro Carvalho Chehab --------------------------- -What: remove EXPORT_SYMBOL(panic_timeout) -When: April 2006 -Files: kernel/panic.c -Why: No modular usage in the kernel. -Who: Adrian Bunk - ---------------------------- - What: remove EXPORT_SYMBOL(insert_resource) When: April 2006 Files: kernel/resource.c @@ -127,13 +119,6 @@ Who: Christoph Hellwig --------------------------- -What: EXPORT_SYMBOL(lookup_hash) -When: January 2006 -Why: Too low-level interface. Use lookup_one_len or lookup_create instead. -Who: Christoph Hellwig - ---------------------------- - What: CONFIG_FORCED_INLINING When: June 2006 Why: Config option is there to see if gcc is good enough. (in january @@ -241,3 +226,15 @@ Why: The USB subsystem has changed a lot over time, and it has been Who: Greg Kroah-Hartman --------------------------- + +What: find_trylock_page +When: January 2007 +Why: The interface no longer has any callers left in the kernel. It + is an odd interface (compared with other find_*_page functions), in + that it does not take a refcount to the page, only the page lock. + It should be replaced with find_get_page or find_lock_page if possible. + This feature removal can be reevaluated if users of the interface + cannot cleanly use something else. +Who: Nick Piggin + +--------------------------- diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index adaa899e5c90..3a2e5520c1e3 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -694,7 +694,7 @@ struct file_operations ---------------------- This describes how the VFS can manipulate an open file. As of kernel -2.6.13, the following members are defined: +2.6.17, the following members are defined: struct file_operations { loff_t (*llseek) (struct file *, loff_t, int); @@ -723,6 +723,10 @@ struct file_operations { int (*check_flags)(int); int (*dir_notify)(struct file *filp, unsigned long arg); int (*flock) (struct file *, int, struct file_lock *); + ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, size_t, unsigned +int); + ssize_t (*splice_read)(struct file *, struct pipe_inode_info *, size_t, unsigned +int); }; Again, all methods are called without any locks being held, unless @@ -790,6 +794,12 @@ otherwise noted. flock: called by the flock(2) system call + splice_write: called by the VFS to splice data from a pipe to a file. This + method is used by the splice(2) system call + + splice_read: called by the VFS to splice data from file to a pipe. This + method is used by the splice(2) system call + Note that the file operations are implemented by the specific filesystem in which the inode resides. When opening a device node (character or block special) most filesystems will call special diff --git a/Documentation/fujitsu/frv/kernel-ABI.txt b/Documentation/fujitsu/frv/kernel-ABI.txt index 0ed9b0a779bc..8b0a5fc8bfd9 100644 --- a/Documentation/fujitsu/frv/kernel-ABI.txt +++ b/Documentation/fujitsu/frv/kernel-ABI.txt @@ -1,17 +1,19 @@ - ================================= - INTERNAL KERNEL ABI FOR FR-V ARCH - ================================= + ================================= + INTERNAL KERNEL ABI FOR FR-V ARCH + ================================= -The internal FRV kernel ABI is not quite the same as the userspace ABI. A number of the registers -are used for special purposed, and the ABI is not consistent between modules vs core, and MMU vs -no-MMU. +The internal FRV kernel ABI is not quite the same as the userspace ABI. A +number of the registers are used for special purposed, and the ABI is not +consistent between modules vs core, and MMU vs no-MMU. -This partly stems from the fact that FRV CPUs do not have a separate supervisor stack pointer, and -most of them do not have any scratch registers, thus requiring at least one general purpose -register to be clobbered in such an event. Also, within the kernel core, it is possible to simply -jump or call directly between functions using a relative offset. This cannot be extended to modules -for the displacement is likely to be too far. Thus in modules the address of a function to call -must be calculated in a register and then used, requiring two extra instructions. +This partly stems from the fact that FRV CPUs do not have a separate +supervisor stack pointer, and most of them do not have any scratch +registers, thus requiring at least one general purpose register to be +clobbered in such an event. Also, within the kernel core, it is possible to +simply jump or call directly between functions using a relative offset. +This cannot be extended to modules for the displacement is likely to be too +far. Thus in modules the address of a function to call must be calculated +in a register and then used, requiring two extra instructions. This document has the following sections: @@ -39,7 +41,8 @@ When a system call is made, the following registers are effective: CPU OPERATING MODES =================== -The FR-V CPU has three basic operating modes. In order of increasing capability: +The FR-V CPU has three basic operating modes. In order of increasing +capability: (1) User mode. @@ -47,42 +50,46 @@ The FR-V CPU has three basic operating modes. In order of increasing capability: (2) Kernel mode. - Normal kernel mode. There are many additional control registers available that may be - accessed in this mode, in addition to all the stuff available to user mode. This has two - submodes: + Normal kernel mode. There are many additional control registers + available that may be accessed in this mode, in addition to all the + stuff available to user mode. This has two submodes: (a) Exceptions enabled (PSR.T == 1). - Exceptions will invoke the appropriate normal kernel mode handler. On entry to the - handler, the PSR.T bit will be cleared. + Exceptions will invoke the appropriate normal kernel mode + handler. On entry to the handler, the PSR.T bit will be cleared. (b) Exceptions disabled (PSR.T == 0). - No exceptions or interrupts may happen. Any mandatory exceptions will cause the CPU to - halt unless the CPU is told to jump into debug mode instead. + No exceptions or interrupts may happen. Any mandatory exceptions + will cause the CPU to halt unless the CPU is told to jump into + debug mode instead. (3) Debug mode. - No exceptions may happen in this mode. Memory protection and management exceptions will be - flagged for later consideration, but the exception handler won't be invoked. Debugging traps - such as hardware breakpoints and watchpoints will be ignored. This mode is entered only by - debugging events obtained from the other two modes. + No exceptions may happen in this mode. Memory protection and + management exceptions will be flagged for later consideration, but + the exception handler won't be invoked. Debugging traps such as + hardware breakpoints and watchpoints will be ignored. This mode is + entered only by debugging events obtained from the other two modes. - All kernel mode registers may be accessed, plus a few extra debugging specific registers. + All kernel mode registers may be accessed, plus a few extra debugging + specific registers. ================================= INTERNAL KERNEL-MODE REGISTER ABI ================================= -There are a number of permanent register assignments that are set up by entry.S in the exception -prologue. Note that there is a complete set of exception prologues for each of user->kernel -transition and kernel->kernel transition. There are also user->debug and kernel->debug mode -transition prologues. +There are a number of permanent register assignments that are set up by +entry.S in the exception prologue. Note that there is a complete set of +exception prologues for each of user->kernel transition and kernel->kernel +transition. There are also user->debug and kernel->debug mode transition +prologues. REGISTER FLAVOUR USE - =============== ======= ==================================================== + =============== ======= ============================================== GR1 Supervisor stack pointer GR15 Current thread info pointer GR16 GP-Rel base register for small data @@ -92,10 +99,12 @@ transition prologues. GR31 NOMMU Destroyed by debug mode entry GR31 MMU Destroyed by TLB miss kernel mode entry CCR.ICC2 Virtual interrupt disablement tracking - CCCR.CC3 Cleared by exception prologue (atomic op emulation) + CCCR.CC3 Cleared by exception prologue + (atomic op emulation) SCR0 MMU See mmu-layout.txt. SCR1 MMU See mmu-layout.txt. - SCR2 MMU Save for EAR0 (destroyed by icache insns in debug mode) + SCR2 MMU Save for EAR0 (destroyed by icache insns + in debug mode) SCR3 MMU Save for GR31 during debug exceptions DAMR/IAMR NOMMU Fixed memory protection layout. DAMR/IAMR MMU See mmu-layout.txt. @@ -104,18 +113,21 @@ transition prologues. Certain registers are also used or modified across function calls: REGISTER CALL RETURN - =============== =============================== =============================== + =============== =============================== ====================== GR0 Fixed Zero - GR2 Function call frame pointer GR3 Special Preserved GR3-GR7 - Clobbered - GR8 Function call arg #1 Return value (or clobbered) - GR9 Function call arg #2 Return value MSW (or clobbered) + GR8 Function call arg #1 Return value + (or clobbered) + GR9 Function call arg #2 Return value MSW + (or clobbered) GR10-GR13 Function call arg #3-#6 Clobbered GR14 - Clobbered GR15-GR16 Special Preserved GR17-GR27 - Preserved - GR28-GR31 Special Only accessed explicitly + GR28-GR31 Special Only accessed + explicitly LR Return address after CALL Clobbered CCR/CCCR - Mostly Clobbered @@ -124,46 +136,53 @@ Certain registers are also used or modified across function calls: INTERNAL DEBUG-MODE REGISTER ABI ================================ -This is the same as the kernel-mode register ABI for functions calls. The difference is that in -debug-mode there's a different stack and a different exception frame. Almost all the global -registers from kernel-mode (including the stack pointer) may be changed. +This is the same as the kernel-mode register ABI for functions calls. The +difference is that in debug-mode there's a different stack and a different +exception frame. Almost all the global registers from kernel-mode +(including the stack pointer) may be changed. REGISTER FLAVOUR USE - =============== ======= ==================================================== + =============== ======= ============================================== GR1 Debug stack pointer GR16 GP-Rel base register for small data - GR31 Current debug exception frame pointer (__debug_frame) + GR31 Current debug exception frame pointer + (__debug_frame) SCR3 MMU Saved value of GR31 -Note that debug mode is able to interfere with the kernel's emulated atomic ops, so it must be -exceedingly careful not to do any that would interact with the main kernel in this regard. Hence -the debug mode code (gdbstub) is almost completely self-contained. The only external code used is -the sprintf family of functions. +Note that debug mode is able to interfere with the kernel's emulated atomic +ops, so it must be exceedingly careful not to do any that would interact +with the main kernel in this regard. Hence the debug mode code (gdbstub) is +almost completely self-contained. The only external code used is the +sprintf family of functions. -Futhermore, break.S is so complicated because single-step mode does not switch off on entry to an -exception. That means unless manually disabled, single-stepping will blithely go on stepping into -things like interrupts. See gdbstub.txt for more information. +Futhermore, break.S is so complicated because single-step mode does not +switch off on entry to an exception. That means unless manually disabled, +single-stepping will blithely go on stepping into things like interrupts. +See gdbstub.txt for more information. ========================== VIRTUAL INTERRUPT HANDLING ========================== -Because accesses to the PSR is so slow, and to disable interrupts we have to access it twice (once -to read and once to write), we don't actually disable interrupts at all if we don't have to. What -we do instead is use the ICC2 condition code flags to note virtual disablement, such that if we -then do take an interrupt, we note the flag, really disable interrupts, set another flag and resume -execution at the point the interrupt happened. Setting condition flags as a side effect of an -arithmetic or logical instruction is really fast. This use of the ICC2 only occurs within the +Because accesses to the PSR is so slow, and to disable interrupts we have +to access it twice (once to read and once to write), we don't actually +disable interrupts at all if we don't have to. What we do instead is use +the ICC2 condition code flags to note virtual disablement, such that if we +then do take an interrupt, we note the flag, really disable interrupts, set +another flag and resume execution at the point the interrupt happened. +Setting condition flags as a side effect of an arithmetic or logical +instruction is really fast. This use of the ICC2 only occurs within the kernel - it does not affect userspace. The flags we use are: (*) CCR.ICC2.Z [Zero flag] - Set to virtually disable interrupts, clear when interrupts are virtually enabled. Can be - modified by logical instructions without affecting the Carry flag. + Set to virtually disable interrupts, clear when interrupts are + virtually enabled. Can be modified by logical instructions without + affecting the Carry flag. (*) CCR.ICC2.C [Carry flag] @@ -176,8 +195,9 @@ What happens is this: ICC2.Z is 0, ICC2.C is 1. - (2) An interrupt occurs. The exception prologue examines ICC2.Z and determines that nothing needs - doing. This is done simply with an unlikely BEQ instruction. + (2) An interrupt occurs. The exception prologue examines ICC2.Z and + determines that nothing needs doing. This is done simply with an + unlikely BEQ instruction. (3) The interrupts are disabled (local_irq_disable) @@ -187,48 +207,56 @@ What happens is this: ICC2.Z would be set to 0. - A TIHI #2 instruction (trap #2 if condition HI - Z==0 && C==0) would be used to trap if - interrupts were now virtually enabled, but physically disabled - which they're not, so the - trap isn't taken. The kernel would then be back to state (1). + A TIHI #2 instruction (trap #2 if condition HI - Z==0 && C==0) would + be used to trap if interrupts were now virtually enabled, but + physically disabled - which they're not, so the trap isn't taken. The + kernel would then be back to state (1). - (5) An interrupt occurs. The exception prologue examines ICC2.Z and determines that the interrupt - shouldn't actually have happened. It jumps aside, and there disabled interrupts by setting - PSR.PIL to 14 and then it clears ICC2.C. + (5) An interrupt occurs. The exception prologue examines ICC2.Z and + determines that the interrupt shouldn't actually have happened. It + jumps aside, and there disabled interrupts by setting PSR.PIL to 14 + and then it clears ICC2.C. (6) If interrupts were then saved and disabled again (local_irq_save): - ICC2.Z would be shifted into the save variable and masked off (giving a 1). + ICC2.Z would be shifted into the save variable and masked off + (giving a 1). - ICC2.Z would then be set to 1 (thus unchanged), and ICC2.C would be unaffected (ie: 0). + ICC2.Z would then be set to 1 (thus unchanged), and ICC2.C would be + unaffected (ie: 0). (7) If interrupts were then restored from state (6) (local_irq_restore): - ICC2.Z would be set to indicate the result of XOR'ing the saved value (ie: 1) with 1, which - gives a result of 0 - thus leaving ICC2.Z set. + ICC2.Z would be set to indicate the result of XOR'ing the saved + value (ie: 1) with 1, which gives a result of 0 - thus leaving + ICC2.Z set. ICC2.C would remain unaffected (ie: 0). - A TIHI #2 instruction would be used to again assay the current state, but this would do - nothing as Z==1. + A TIHI #2 instruction would be used to again assay the current state, + but this would do nothing as Z==1. (8) If interrupts were then enabled (local_irq_enable): - ICC2.Z would be cleared. ICC2.C would be left unaffected. Both flags would now be 0. + ICC2.Z would be cleared. ICC2.C would be left unaffected. Both + flags would now be 0. - A TIHI #2 instruction again issued to assay the current state would then trap as both Z==0 - [interrupts virtually enabled] and C==0 [interrupts really disabled] would then be true. + A TIHI #2 instruction again issued to assay the current state would + then trap as both Z==0 [interrupts virtually enabled] and C==0 + [interrupts really disabled] would then be true. - (9) The trap #2 handler would simply enable hardware interrupts (set PSR.PIL to 0), set ICC2.C to - 1 and return. + (9) The trap #2 handler would simply enable hardware interrupts + (set PSR.PIL to 0), set ICC2.C to 1 and return. (10) Immediately upon returning, the pending interrupt would be taken. -(11) The interrupt handler would take the path of actually processing the interrupt (ICC2.Z is - clear, BEQ fails as per step (2)). +(11) The interrupt handler would take the path of actually processing the + interrupt (ICC2.Z is clear, BEQ fails as per step (2)). -(12) The interrupt handler would then set ICC2.C to 1 since hardware interrupts are definitely - enabled - or else the kernel wouldn't be here. +(12) The interrupt handler would then set ICC2.C to 1 since hardware + interrupts are definitely enabled - or else the kernel wouldn't be here. (13) On return from the interrupt handler, things would be back to state (1). -This trap (#2) is only available in kernel mode. In user mode it will result in SIGILL. +This trap (#2) is only available in kernel mode. In user mode it will +result in SIGILL. diff --git a/Documentation/input/joystick-parport.txt b/Documentation/input/joystick-parport.txt index 88a011c9f985..d537c48cc6d0 100644 --- a/Documentation/input/joystick-parport.txt +++ b/Documentation/input/joystick-parport.txt @@ -36,12 +36,12 @@ with them. All NES and SNES use the same synchronous serial protocol, clocked from the computer's side (and thus timing insensitive). To allow up to 5 NES -and/or SNES gamepads connected to the parallel port at once, the output -lines of the parallel port are shared, while one of 5 available input lines -is assigned to each gamepad. +and/or SNES gamepads and/or SNES mice connected to the parallel port at once, +the output lines of the parallel port are shared, while one of 5 available +input lines is assigned to each gamepad. This protocol is handled by the gamecon.c driver, so that's the one -you'll use for NES and SNES gamepads. +you'll use for NES, SNES gamepads and SNES mice. The main problem with PC parallel ports is that they don't have +5V power source on any of their pins. So, if you want a reliable source of power @@ -106,7 +106,7 @@ A, Turbo B, Select and Start, and is connected through 5 wires, then it is either a NES or NES clone and will work with this connection. SNES gamepads also use 5 wires, but have more buttons. They will work as well, of course. -Pinout for NES gamepads Pinout for SNES gamepads +Pinout for NES gamepads Pinout for SNES gamepads and mice +----> Power +-----------------------\ | 7 | o o o o | x x o | 1 @@ -454,6 +454,7 @@ uses the following kernel/module command line: 6 | N64 pad 7 | Sony PSX controller 8 | Sony PSX DDR controller + 9 | SNES mouse The exact type of the PSX controller type is autoprobed when used so hot swapping should work (but is not recomended). diff --git a/Documentation/isdn/README.gigaset b/Documentation/isdn/README.gigaset new file mode 100644 index 000000000000..85a64defd385 --- /dev/null +++ b/Documentation/isdn/README.gigaset @@ -0,0 +1,286 @@ +GigaSet 307x Device Driver +========================== + +1. Requirements + ------------ +1.1. Hardware + -------- + This release supports the connection of the Gigaset 307x/417x family of + ISDN DECT bases via Gigaset M101 Data, Gigaset M105 Data or direct USB + connection. The following devices are reported to be compatible: + 307x/417x: + Gigaset SX255isdn + Gigaset SX353isdn + Sinus 45 [AB] isdn (Deutsche Telekom) + Sinus 721X/XA + Vox Chicago 390 ISDN (KPN Telecom) + M101: + Sinus 45 Data 1 (Telekom) + M105: + Gigaset USB Adapter DECT + Sinus 45 Data 2 (Telekom) + Sinus 721 data + Chicago 390 USB (KPN) + See also http://www.erbze.info/sinus_gigaset.htm and + http://gigaset307x.sourceforge.net/ + + We had also reports from users of Gigaset M105 who could use the drivers + with SX 100 and CX 100 ISDN bases (only in unimodem mode, see section 2.4.) + If you have another device that works with our driver, please let us know. + For example, Gigaset SX205isdn/Sinus 721 X SE and Gigaset SX303isdn bases + are just versions without answering machine of models known to work, so + they should work just as well; but so far we are lacking positive reports + on these. + + Chances of getting an USB device to work are good if the output of + lsusb + at the command line contains one of the following: + ID 0681:0001 + ID 0681:0002 + ID 0681:0009 + ID 0681:0021 + ID 0681:0022 + +1.2. Software + -------- + The driver works with ISDN4linux and so can be used with any software + which is able to use ISDN4linux for ISDN connections (voice or data). + CAPI4Linux support is planned but not yet available. + + There are some user space tools available at + http://sourceforge.net/projects/gigaset307x/ + which provide access to additional device specific functions like SMS, + phonebook or call journal. + + +2. How to use the driver + --------------------- +2.1. Modules + ------- + To get the device working, you have to load the proper kernel module. You + can do this using + modprobe modulename + where modulename is usb_gigaset (M105) or bas_gigaset (direct USB + connection to the base). + +2.2. Device nodes for user space programs + ------------------------------------ + The device can be accessed from user space (eg. by the user space tools + mentioned in 1.2.) through the device nodes: + + - /dev/ttyGU0 for M105 (USB data boxes) + - /dev/ttyGB0 for the base driver (direct USB connection) + + You can also select a "default device" which is used by the frontends when + no device node is given as parameter, by creating a symlink /dev/ttyG to + one of them, eg.: + + ln -s /dev/ttyGB0 /dev/ttyG + +2.3. ISDN4linux + ---------- + This is the "normal" mode of operation. After loading the module you can + set up the ISDN system just as you'd do with any ISDN card. + Your distribution should provide some configuration utility. + If not, you can use some HOWTOs like + http://www.linuxhaven.de/dlhp/HOWTO/DE-ISDN-HOWTO-5.html + If this doesn't work, because you have some recent device like SX100 where + debug output (see section 3.2.) shows something like this when dialing + CMD Received: ERROR + Available Params: 0 + Connection State: 0, Response: -1 + gigaset_process_response: resp_code -1 in ConState 0 ! + Timeout occurred + you might need to use unimodem mode: + +2.4. Unimodem mode + ------------- + This is needed for some devices [e.g. SX100] as they have problems with + the "normal" commands. + + If you have installed the command line tool gigacontr, you can enter + unimodem mode using + gigacontr --mode unimodem + You can switch back using + gigacontr --mode isdn + + You can also load the driver using e.g. + modprobe usb_gigaset startmode=0 + to prevent the driver from starting in "isdn4linux mode". + + In this mode the device works like a modem connected to a serial port + (the /dev/ttyGU0, ... mentioned above) which understands the commands + ATZ init, reset + => OK or ERROR + ATD + ATDT dial + => OK, CONNECT, + BUSY, + NO DIAL TONE, + NO CARRIER, + NO ANSWER + +++ change to command mode when connected + ATH hangup + + You can use some configuration tool of your distribution to configure this + "modem" or configure pppd/wvdial manually. There are some example ppp + configuration files and chat scripts in the gigaset-VERSION/ppp directory. + Please note that the USB drivers are not able to change the state of the + control lines (the M105 driver can be configured to use some undocumented + control requests, if you really need the control lines, though). This means + you must use "Stupid Mode" if you are using wvdial or you should use the + nocrtscts option of pppd. + You must also assure that the ppp_async module is loaded with the parameter + flag_time=0. You can do this e.g. by adding a line like + + options ppp_async flag_time=0 + + to /etc/modprobe.conf. If your distribution has some local module + configuration file like /etc/modprobe.conf.local, + using that should be preferred. + +2.5. Call-ID (CID) mode + ------------------ + Call-IDs are numbers used to tag commands to, and responses from, the + Gigaset base in order to support the simultaneous handling of multiple + ISDN calls. Their use can be enabled ("CID mode") or disabled ("Unimodem + mode"). Without Call-IDs (in Unimodem mode), only a very limited set of + functions is available. It allows outgoing data connections only, but + does not signal incoming calls or other base events. + + DECT cordless data devices (M10x) permanently occupy the cordless + connection to the base while Call-IDs are activated. As the Gigaset + bases only support one DECT data connection at a time, this prevents + other DECT cordless data devices from accessing the base. + + During active operation, the driver switches to the necessary mode + automatically. However, for the reasons above, the mode chosen when + the device is not in use (idle) can be selected by the user. + - If you want to receive incoming calls, you can use the default + settings (CID mode). + - If you have several DECT data devices (M10x) which you want to use + in turn, select Unimodem mode by passing the parameter "cidmode=0" to + the driver ("modprobe usb_gigaset cidmode=0" or modprobe.conf). + + If you want both of these at once, you are out of luck. + + You can also use /sys/module//parameters/cidmode for changing + the CID mode setting ( is usb_gigaset or bas_gigaset). + + +3. Troubleshooting + --------------- +3.1. Solutions to frequently reported problems + ----------------------------------------- + Problem: + You have a slow provider and isdn4linux gives up dialing too early. + Solution: + Load the isdn module using the dialtimeout option. You can do this e.g. + by adding a line like + + options isdn dialtimeout=15 + + to /etc/modprobe.conf. If your distribution has some local module + configuration file like /etc/modprobe.conf.local, + using that should be preferred. + + Problem: + Your isdn script aborts with a message about isdnlog. + Solution: + Try deactivating (or commenting out) isdnlog. This driver does not + support it. + + Problem: + You have two or more DECT data adapters (M101/M105) and only the + first one you turn on works. + Solution: + Select Unimodem mode for all DECT data adapters. (see section 2.4.) + +3.2. Telling the driver to provide more information + ---------------------------------------------- + Building the driver with the "Gigaset debugging" kernel configuration + option (CONFIG_GIGASET_DEBUG) gives it the ability to produce additional + information useful for debugging. + + You can control the amount of debugging information the driver produces by + writing an appropriate value to /sys/module/gigaset/parameters/debug, e.g. + echo 0 > /sys/module/gigaset/parameters/debug + switches off debugging output completely, + echo 0x10a020 > /sys/module/gigaset/parameters/debug + enables the standard set of debugging output messages. These values are + bit patterns where every bit controls a certain type of debugging output. + See the constants DEBUG_* in the source file gigaset.h for details. + + The initial value can be set using the debug parameter when loading the + module "gigaset", e.g. by adding a line + options gigaset debug=0 + to /etc/modprobe.conf, ... + + Generated debugging information can be found + - as output of the command + dmesg + - in system log files written by your syslog daemon, usually + in /var/log/, e.g. /var/log/messages. + +3.3. Reporting problems and bugs + --------------------------- + If you can't solve problems with the driver on your own, feel free to + use one of the forums, bug trackers, or mailing lists on + http://sourceforge.net/projects/gigaset307x + or write an electronic mail to the maintainers. + + Try to provide as much information as possible, such as + - distribution + - kernel version (uname -r) + - gcc version (gcc --version) + - hardware architecture (uname -m, ...) + - type and firmware version of your device (base and wireless module, + if any) + - output of "lsusb -v" (if using an USB device) + - error messages + - relevant system log messages (it would help if you activate debug + output as described in 3.2.) + + For help with general configuration problems not specific to our driver, + such as isdn4linux and network configuration issues, please refer to the + appropriate forums and newsgroups. + +3.4. Reporting problem solutions + --------------------------- + If you solved a problem with our drivers, wrote startup scripts for your + distribution, ... feel free to contact us (using one of the places + mentioned in 3.3.). We'd like to add scripts, hints, documentation + to the driver and/or the project web page. + + +4. Links, other software + --------------------- + - Sourceforge project developing this driver and associated tools + http://sourceforge.net/projects/gigaset307x + - Yahoo! Group on the Siemens Gigaset family of devices + http://de.groups.yahoo.com/group/Siemens-Gigaset + - Siemens Gigaset/T-Sinus compatibility table + http://www.erbze.info/sinus_gigaset.htm + + +5. Credits + ------- + Thanks to + + Karsten Keil + for his help with isdn4linux + Deti Fliegl + for his base driver code + Dennis Dietrich + for his kernel 2.6 patches + Andreas Rummel + for his work and logs to get unimodem mode working + Andreas Degert + for his logs and patches to get cx 100 working + Dietrich Feist + for his generous donation of one M105 and two M101 cordless adapters + Christoph Schweers + for his generous donation of a M34 device + + and all the other people who sent logs and other information. + diff --git a/Documentation/kbuild/modules.txt b/Documentation/kbuild/modules.txt index fcccf2432f98..61fc079eb966 100644 --- a/Documentation/kbuild/modules.txt +++ b/Documentation/kbuild/modules.txt @@ -44,7 +44,7 @@ What is covered within this file is mainly information to authors of modules. The author of an external modules should supply a makefile that hides most of the complexity so one only has to type 'make' to build the module. A complete example will be present in -chapter ¤. Creating a kbuild file for an external module". +chapter 4, "Creating a kbuild file for an external module". === 2. How to build external modules diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index f8cb55c30b0f..b3a6187e5305 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1,4 +1,4 @@ -February 2003 Kernel Parameters v2.5.59 + Kernel Parameters ~~~~~~~~~~~~~~~~~ The following is a consolidated list of the kernel parameters as implemented @@ -17,9 +17,17 @@ are specified on the kernel command line with the module name plus usbcore.blinkenlights=1 -The text in square brackets at the beginning of the description states the -restrictions on the kernel for the said kernel parameter to be valid. The -restrictions referred to are that the relevant option is valid if: +This document may not be entirely up to date and comprehensive. The command +"modinfo -p ${modulename}" shows a current list of all parameters of a loadable +module. Loadable modules, after being loaded into the running kernel, also +reveal their parameters in /sys/module/${modulename}/parameters/. Some of these +parameters may be changed at runtime by the command +"echo -n ${value} > /sys/module/${modulename}/parameters/${parm}". + +The parameters listed below are only valid if certain kernel build options were +enabled and if respective hardware is present. The text in square brackets at +the beginning of each description states the restrictions within which a +parameter is applicable: ACPI ACPI support is enabled. ALSA ALSA sound support is enabled. @@ -1046,10 +1054,10 @@ running once the system is up. noltlbs [PPC] Do not use large page/tlb entries for kernel lowmem mapping on PPC40x. - nomce [IA-32] Machine Check Exception - nomca [IA-64] Disable machine check abort handling + nomce [IA-32] Machine Check Exception + noresidual [PPC] Don't use residual data on PReP machines. noresume [SWSUSP] Disables resume and restores original swap @@ -1682,20 +1690,6 @@ running once the system is up. ______________________________________________________________________ -Changelog: - -2000-06-?? Mr. Unknown - The last known update (for 2.4.0) - the changelog was not kept before. - -2002-11-24 Petr Baudis - Randy Dunlap - Update for 2.5.49, description for most of the options introduced, - references to other documentation (C files, READMEs, ..), added S390, - PPC, SPARC, MTD, ALSA and OSS category. Minor corrections and - reformatting. - -2005-10-19 Randy Dunlap - Lots of typos, whitespace, some reformatting. TODO: diff --git a/Documentation/laptop-mode.txt b/Documentation/laptop-mode.txt index b18e21675906..5696e879449b 100644 --- a/Documentation/laptop-mode.txt +++ b/Documentation/laptop-mode.txt @@ -919,11 +919,11 @@ int main(int argc, char **argv) int settle_time = 60; /* Parse the simple command-line */ - if (ac == 2) - disk = av[1]; - else if (ac == 4) { - settle_time = atoi(av[2]); - disk = av[3]; + if (argc == 2) + disk = argv[1]; + else if (argc == 4) { + settle_time = atoi(argv[2]); + disk = argv[3]; } else usage(); diff --git a/Documentation/leds-class.txt b/Documentation/leds-class.txt new file mode 100644 index 000000000000..8c35c0426110 --- /dev/null +++ b/Documentation/leds-class.txt @@ -0,0 +1,71 @@ +LED handling under Linux +======================== + +If you're reading this and thinking about keyboard leds, these are +handled by the input subsystem and the led class is *not* needed. + +In its simplest form, the LED class just allows control of LEDs from +userspace. LEDs appear in /sys/class/leds/. The brightness file will +set the brightness of the LED (taking a value 0-255). Most LEDs don't +have hardware brightness support so will just be turned on for non-zero +brightness settings. + +The class also introduces the optional concept of an LED trigger. A trigger +is a kernel based source of led events. Triggers can either be simple or +complex. A simple trigger isn't configurable and is designed to slot into +existing subsystems with minimal additional code. Examples are the ide-disk, +nand-disk and sharpsl-charge triggers. With led triggers disabled, the code +optimises away. + +Complex triggers whilst available to all LEDs have LED specific +parameters and work on a per LED basis. The timer trigger is an example. + +You can change triggers in a similar manner to the way an IO scheduler +is chosen (via /sys/class/leds//trigger). Trigger specific +parameters can appear in /sys/class/leds/ once a given trigger is +selected. + + +Design Philosophy +================= + +The underlying design philosophy is simplicity. LEDs are simple devices +and the aim is to keep a small amount of code giving as much functionality +as possible. Please keep this in mind when suggesting enhancements. + + +LED Device Naming +================= + +Is currently of the form: + +"devicename:colour" + +There have been calls for LED properties such as colour to be exported as +individual led class attributes. As a solution which doesn't incur as much +overhead, I suggest these become part of the device name. The naming scheme +above leaves scope for further attributes should they be needed. + + +Known Issues +============ + +The LED Trigger core cannot be a module as the simple trigger functions +would cause nightmare dependency issues. I see this as a minor issue +compared to the benefits the simple trigger functionality brings. The +rest of the LED subsystem can be modular. + +Some leds can be programmed to flash in hardware. As this isn't a generic +LED device property, this should be exported as a device specific sysfs +attribute rather than part of the class if this functionality is required. + + +Future Development +================== + +At the moment, a trigger can't be created specifically for a single LED. +There are a number of cases where a trigger might only be mappable to a +particular LED (ACPI?). The addition of triggers provided by the LED driver +should cover this option and be possible to add without breaking the +current interface. + diff --git a/Documentation/memory-barriers.txt b/Documentation/memory-barriers.txt new file mode 100644 index 000000000000..92f0056d928c --- /dev/null +++ b/Documentation/memory-barriers.txt @@ -0,0 +1,1941 @@ + ============================ + LINUX KERNEL MEMORY BARRIERS + ============================ + +By: David Howells + +Contents: + + (*) Abstract memory access model. + + - Device operations. + - Guarantees. + + (*) What are memory barriers? + + - Varieties of memory barrier. + - What may not be assumed about memory barriers? + - Data dependency barriers. + - Control dependencies. + - SMP barrier pairing. + - Examples of memory barrier sequences. + + (*) Explicit kernel barriers. + + - Compiler barrier. + - The CPU memory barriers. + - MMIO write barrier. + + (*) Implicit kernel memory barriers. + + - Locking functions. + - Interrupt disabling functions. + - Miscellaneous functions. + + (*) Inter-CPU locking barrier effects. + + - Locks vs memory accesses. + - Locks vs I/O accesses. + + (*) Where are memory barriers needed? + + - Interprocessor interaction. + - Atomic operations. + - Accessing devices. + - Interrupts. + + (*) Kernel I/O barrier effects. + + (*) Assumed minimum execution ordering model. + + (*) The effects of the cpu cache. + + - Cache coherency. + - Cache coherency vs DMA. + - Cache coherency vs MMIO. + + (*) The things CPUs get up to. + + - And then there's the Alpha. + + (*) References. + + +============================ +ABSTRACT MEMORY ACCESS MODEL +============================ + +Consider the following abstract model of the system: + + : : + : : + : : + +-------+ : +--------+ : +-------+ + | | : | | : | | + | | : | | : | | + | CPU 1 |<----->| Memory |<----->| CPU 2 | + | | : | | : | | + | | : | | : | | + +-------+ : +--------+ : +-------+ + ^ : ^ : ^ + | : | : | + | : | : | + | : v : | + | : +--------+ : | + | : | | : | + | : | | : | + +---------->| Device |<----------+ + : | | : + : | | : + : +--------+ : + : : + +Each CPU executes a program that generates memory access operations. In the +abstract CPU, memory operation ordering is very relaxed, and a CPU may actually +perform the memory operations in any order it likes, provided program causality +appears to be maintained. Similarly, the compiler may also arrange the +instructions it emits in any order it likes, provided it doesn't affect the +apparent operation of the program. + +So in the above diagram, the effects of the memory operations performed by a +CPU are perceived by the rest of the system as the operations cross the +interface between the CPU and rest of the system (the dotted lines). + + +For example, consider the following sequence of events: + + CPU 1 CPU 2 + =============== =============== + { A == 1; B == 2 } + A = 3; x = A; + B = 4; y = B; + +The set of accesses as seen by the memory system in the middle can be arranged +in 24 different combinations: + + STORE A=3, STORE B=4, x=LOAD A->3, y=LOAD B->4 + STORE A=3, STORE B=4, y=LOAD B->4, x=LOAD A->3 + STORE A=3, x=LOAD A->3, STORE B=4, y=LOAD B->4 + STORE A=3, x=LOAD A->3, y=LOAD B->2, STORE B=4 + STORE A=3, y=LOAD B->2, STORE B=4, x=LOAD A->3 + STORE A=3, y=LOAD B->2, x=LOAD A->3, STORE B=4 + STORE B=4, STORE A=3, x=LOAD A->3, y=LOAD B->4 + STORE B=4, ... + ... + +and can thus result in four different combinations of values: + + x == 1, y == 2 + x == 1, y == 4 + x == 3, y == 2 + x == 3, y == 4 + + +Furthermore, the stores committed by a CPU to the memory system may not be +perceived by the loads made by another CPU in the same order as the stores were +committed. + + +As a further example, consider this sequence of events: + + CPU 1 CPU 2 + =============== =============== + { A == 1, B == 2, C = 3, P == &A, Q == &C } + B = 4; Q = P; + P = &B D = *Q; + +There is an obvious data dependency here, as the value loaded into D depends on +the address retrieved from P by CPU 2. At the end of the sequence, any of the +following results are possible: + + (Q == &A) and (D == 1) + (Q == &B) and (D == 2) + (Q == &B) and (D == 4) + +Note that CPU 2 will never try and load C into D because the CPU will load P +into Q before issuing the load of *Q. + + +DEVICE OPERATIONS +----------------- + +Some devices present their control interfaces as collections of memory +locations, but the order in which the control registers are accessed is very +important. For instance, imagine an ethernet card with a set of internal +registers that are accessed through an address port register (A) and a data +port register (D). To read internal register 5, the following code might then +be used: + + *A = 5; + x = *D; + +but this might show up as either of the following two sequences: + + STORE *A = 5, x = LOAD *D + x = LOAD *D, STORE *A = 5 + +the second of which will almost certainly result in a malfunction, since it set +the address _after_ attempting to read the register. + + +GUARANTEES +---------- + +There are some minimal guarantees that may be expected of a CPU: + + (*) On any given CPU, dependent memory accesses will be issued in order, with + respect to itself. This means that for: + + Q = P; D = *Q; + + the CPU will issue the following memory operations: + + Q = LOAD P, D = LOAD *Q + + and always in that order. + + (*) Overlapping loads and stores within a particular CPU will appear to be + ordered within that CPU. This means that for: + + a = *X; *X = b; + + the CPU will only issue the following sequence of memory operations: + + a = LOAD *X, STORE *X = b + + And for: + + *X = c; d = *X; + + the CPU will only issue: + + STORE *X = c, d = LOAD *X + + (Loads and stores overlap if they are targetted at overlapping pieces of + memory). + +And there are a number of things that _must_ or _must_not_ be assumed: + + (*) It _must_not_ be assumed that independent loads and stores will be issued + in the order given. This means that for: + + X = *A; Y = *B; *D = Z; + + we may get any of the following sequences: + + X = LOAD *A, Y = LOAD *B, STORE *D = Z + X = LOAD *A, STORE *D = Z, Y = LOAD *B + Y = LOAD *B, X = LOAD *A, STORE *D = Z + Y = LOAD *B, STORE *D = Z, X = LOAD *A + STORE *D = Z, X = LOAD *A, Y = LOAD *B + STORE *D = Z, Y = LOAD *B, X = LOAD *A + + (*) It _must_ be assumed that overlapping memory accesses may be merged or + discarded. This means that for: + + X = *A; Y = *(A + 4); + + we may get any one of the following sequences: + + X = LOAD *A; Y = LOAD *(A + 4); + Y = LOAD *(A + 4); X = LOAD *A; + {X, Y} = LOAD {*A, *(A + 4) }; + + And for: + + *A = X; Y = *A; + + we may get either of: + + STORE *A = X; Y = LOAD *A; + STORE *A = Y; + + +========================= +WHAT ARE MEMORY BARRIERS? +========================= + +As can be seen above, independent memory operations are effectively performed +in random order, but this can be a problem for CPU-CPU interaction and for I/O. +What is required is some way of intervening to instruct the compiler and the +CPU to restrict the order. + +Memory barriers are such interventions. They impose a perceived partial +ordering between the memory operations specified on either side of the barrier. +They request that the sequence of memory events generated appears to other +parts of the system as if the barrier is effective on that CPU. + + +VARIETIES OF MEMORY BARRIER +--------------------------- + +Memory barriers come in four basic varieties: + + (1) Write (or store) memory barriers. + + A write memory barrier gives a guarantee that all the STORE operations + specified before the barrier will appear to happen before all the STORE + operations specified after the barrier with respect to the other + components of the system. + + A write barrier is a partial ordering on stores only; it is not required + to have any effect on loads. + + A CPU can be viewed as as commiting a sequence of store operations to the + memory system as time progresses. All stores before a write barrier will + occur in the sequence _before_ all the stores after the write barrier. + + [!] Note that write barriers should normally be paired with read or data + dependency barriers; see the "SMP barrier pairing" subsection. + + + (2) Data dependency barriers. + + A data dependency barrier is a weaker form of read barrier. In the case + where two loads are performed such that the second depends on the result + of the first (eg: the first load retrieves the address to which the second + load will be directed), a data dependency barrier would be required to + make sure that the target of the second load is updated before the address + obtained by the first load is accessed. + + A data dependency barrier is a partial ordering on interdependent loads + only; it is not required to have any effect on stores, independent loads + or overlapping loads. + + As mentioned in (1), the other CPUs in the system can be viewed as + committing sequences of stores to the memory system that the CPU being + considered can then perceive. A data dependency barrier issued by the CPU + under consideration guarantees that for any load preceding it, if that + load touches one of a sequence of stores from another CPU, then by the + time the barrier completes, the effects of all the stores prior to that + touched by the load will be perceptible to any loads issued after the data + dependency barrier. + + See the "Examples of memory barrier sequences" subsection for diagrams + showing the ordering constraints. + + [!] Note that the first load really has to have a _data_ dependency and + not a control dependency. If the address for the second load is dependent + on the first load, but the dependency is through a conditional rather than + actually loading the address itself, then it's a _control_ dependency and + a full read barrier or better is required. See the "Control dependencies" + subsection for more information. + + [!] Note that data dependency barriers should normally be paired with + write barriers; see the "SMP barrier pairing" subsection. + + + (3) Read (or load) memory barriers. + + A read barrier is a data dependency barrier plus a guarantee that all the + LOAD operations specified before the barrier will appear to happen before + all the LOAD operations specified after the barrier with respect to the + other components of the system. + + A read barrier is a partial ordering on loads only; it is not required to + have any effect on stores. + + Read memory barriers imply data dependency barriers, and so can substitute + for them. + + [!] Note that read barriers should normally be paired with write barriers; + see the "SMP barrier pairing" subsection. + + + (4) General memory barriers. + + A general memory barrier is a combination of both a read memory barrier + and a write memory barrier. It is a partial ordering over both loads and + stores. + + General memory barriers imply both read and write memory barriers, and so + can substitute for either. + + +And a couple of implicit varieties: + + (5) LOCK operations. + + This acts as a one-way permeable barrier. It guarantees that all memory + operations after the LOCK operation will appear to happen after the LOCK + operation with respect to the other components of the system. + + Memory operations that occur before a LOCK operation may appear to happen + after it completes. + + A LOCK operation should almost always be paired with an UNLOCK operation. + + + (6) UNLOCK operations. + + This also acts as a one-way permeable barrier. It guarantees that all + memory operations before the UNLOCK operation will appear to happen before + the UNLOCK operation with respect to the other components of the system. + + Memory operations that occur after an UNLOCK operation may appear to + happen before it completes. + + LOCK and UNLOCK operations are guaranteed to appear with respect to each + other strictly in the order specified. + + The use of LOCK and UNLOCK operations generally precludes the need for + other sorts of memory barrier (but note the exceptions mentioned in the + subsection "MMIO write barrier"). + + +Memory barriers are only required where there's a possibility of interaction +between two CPUs or between a CPU and a device. If it can be guaranteed that +there won't be any such interaction in any particular piece of code, then +memory barriers are unnecessary in that piece of code. + + +Note that these are the _minimum_ guarantees. Different architectures may give +more substantial guarantees, but they may _not_ be relied upon outside of arch +specific code. + + +WHAT MAY NOT BE ASSUMED ABOUT MEMORY BARRIERS? +---------------------------------------------- + +There are certain things that the Linux kernel memory barriers do not guarantee: + + (*) There is no guarantee that any of the memory accesses specified before a + memory barrier will be _complete_ by the completion of a memory barrier + instruction; the barrier can be considered to draw a line in that CPU's + access queue that accesses of the appropriate type may not cross. + + (*) There is no guarantee that issuing a memory barrier on one CPU will have + any direct effect on another CPU or any other hardware in the system. The + indirect effect will be the order in which the second CPU sees the effects + of the first CPU's accesses occur, but see the next point: + + (*) There is no guarantee that the a CPU will see the correct order of effects + from a second CPU's accesses, even _if_ the second CPU uses a memory + barrier, unless the first CPU _also_ uses a matching memory barrier (see + the subsection on "SMP Barrier Pairing"). + + (*) There is no guarantee that some intervening piece of off-the-CPU + hardware[*] will not reorder the memory accesses. CPU cache coherency + mechanisms should propagate the indirect effects of a memory barrier + between CPUs, but might not do so in order. + + [*] For information on bus mastering DMA and coherency please read: + + Documentation/pci.txt + Documentation/DMA-mapping.txt + Documentation/DMA-API.txt + + +DATA DEPENDENCY BARRIERS +------------------------ + +The usage requirements of data dependency barriers are a little subtle, and +it's not always obvious that they're needed. To illustrate, consider the +following sequence of events: + + CPU 1 CPU 2 + =============== =============== + { A == 1, B == 2, C = 3, P == &A, Q == &C } + B = 4; + + P = &B + Q = P; + D = *Q; + +There's a clear data dependency here, and it would seem that by the end of the +sequence, Q must be either &A or &B, and that: + + (Q == &A) implies (D == 1) + (Q == &B) implies (D == 4) + +But! CPU 2's perception of P may be updated _before_ its perception of B, thus +leading to the following situation: + + (Q == &B) and (D == 2) ???? + +Whilst this may seem like a failure of coherency or causality maintenance, it +isn't, and this behaviour can be observed on certain real CPUs (such as the DEC +Alpha). + +To deal with this, a data dependency barrier must be inserted between the +address load and the data load: + + CPU 1 CPU 2 + =============== =============== + { A == 1, B == 2, C = 3, P == &A, Q == &C } + B = 4; + + P = &B + Q = P; + + D = *Q; + +This enforces the occurrence of one of the two implications, and prevents the +third possibility from arising. + +[!] Note that this extremely counterintuitive situation arises most easily on +machines with split caches, so that, for example, one cache bank processes +even-numbered cache lines and the other bank processes odd-numbered cache +lines. The pointer P might be stored in an odd-numbered cache line, and the +variable B might be stored in an even-numbered cache line. Then, if the +even-numbered bank of the reading CPU's cache is extremely busy while the +odd-numbered bank is idle, one can see the new value of the pointer P (&B), +but the old value of the variable B (1). + + +Another example of where data dependency barriers might by required is where a +number is read from memory and then used to calculate the index for an array +access: + + CPU 1 CPU 2 + =============== =============== + { M[0] == 1, M[1] == 2, M[3] = 3, P == 0, Q == 3 } + M[1] = 4; + + P = 1 + Q = P; + + D = M[Q]; + + +The data dependency barrier is very important to the RCU system, for example. +See rcu_dereference() in include/linux/rcupdate.h. This permits the current +target of an RCU'd pointer to be replaced with a new modified target, without +the replacement target appearing to be incompletely initialised. + +See also the subsection on "Cache Coherency" for a more thorough example. + + +CONTROL DEPENDENCIES +-------------------- + +A control dependency requires a full read memory barrier, not simply a data +dependency barrier to make it work correctly. Consider the following bit of +code: + + q = &a; + if (p) + q = &b; + + x = *q; + +This will not have the desired effect because there is no actual data +dependency, but rather a control dependency that the CPU may short-circuit by +attempting to predict the outcome in advance. In such a case what's actually +required is: + + q = &a; + if (p) + q = &b; + + x = *q; + + +SMP BARRIER PAIRING +------------------- + +When dealing with CPU-CPU interactions, certain types of memory barrier should +always be paired. A lack of appropriate pairing is almost certainly an error. + +A write barrier should always be paired with a data dependency barrier or read +barrier, though a general barrier would also be viable. Similarly a read +barrier or a data dependency barrier should always be paired with at least an +write barrier, though, again, a general barrier is viable: + + CPU 1 CPU 2 + =============== =============== + a = 1; + + b = 2; x = a; + + y = b; + +Or: + + CPU 1 CPU 2 + =============== =============================== + a = 1; + + b = &a; x = b; + + y = *x; + +Basically, the read barrier always has to be there, even though it can be of +the "weaker" type. + + +EXAMPLES OF MEMORY BARRIER SEQUENCES +------------------------------------ + +Firstly, write barriers act as a partial orderings on store operations. +Consider the following sequence of events: + + CPU 1 + ======================= + STORE A = 1 + STORE B = 2 + STORE C = 3 + + STORE D = 4 + STORE E = 5 + +This sequence of events is committed to the memory coherence system in an order +that the rest of the system might perceive as the unordered set of { STORE A, +STORE B, STORE C } all occuring before the unordered set of { STORE D, STORE E +}: + + +-------+ : : + | | +------+ + | |------>| C=3 | } /\ + | | : +------+ }----- \ -----> Events perceptible + | | : | A=1 | } \/ to rest of system + | | : +------+ } + | CPU 1 | : | B=2 | } + | | +------+ } + | | wwwwwwwwwwwwwwww } <--- At this point the write barrier + | | +------+ } requires all stores prior to the + | | : | E=5 | } barrier to be committed before + | | : +------+ } further stores may be take place. + | |------>| D=4 | } + | | +------+ + +-------+ : : + | + | Sequence in which stores committed to memory system + | by CPU 1 + V + + +Secondly, data dependency barriers act as a partial orderings on data-dependent +loads. Consider the following sequence of events: + + CPU 1 CPU 2 + ======================= ======================= + { B = 7; X = 9; Y = 8; C = &Y } + STORE A = 1 + STORE B = 2 + + STORE C = &B LOAD X + STORE D = 4 LOAD C (gets &B) + LOAD *C (reads B) + +Without intervention, CPU 2 may perceive the events on CPU 1 in some +effectively random order, despite the write barrier issued by CPU 1: + + +-------+ : : : : + | | +------+ +-------+ | Sequence of update + | |------>| B=2 |----- --->| Y->8 | | of perception on + | | : +------+ \ +-------+ | CPU 2 + | CPU 1 | : | A=1 | \ --->| C->&Y | V + | | +------+ | +-------+ + | | wwwwwwwwwwwwwwww | : : + | | +------+ | : : + | | : | C=&B |--- | : : +-------+ + | | : +------+ \ | +-------+ | | + | |------>| D=4 | ----------->| C->&B |------>| | + | | +------+ | +-------+ | | + +-------+ : : | : : | | + | : : | | + | : : | CPU 2 | + | +-------+ | | + Apparently incorrect ---> | | B->7 |------>| | + perception of B (!) | +-------+ | | + | : : | | + | +-------+ | | + The load of X holds ---> \ | X->9 |------>| | + up the maintenance \ +-------+ | | + of coherence of B ----->| B->2 | +-------+ + +-------+ + : : + + +In the above example, CPU 2 perceives that B is 7, despite the load of *C +(which would be B) coming after the the LOAD of C. + +If, however, a data dependency barrier were to be placed between the load of C +and the load of *C (ie: B) on CPU 2: + + CPU 1 CPU 2 + ======================= ======================= + { B = 7; X = 9; Y = 8; C = &Y } + STORE A = 1 + STORE B = 2 + + STORE C = &B LOAD X + STORE D = 4 LOAD C (gets &B) + + LOAD *C (reads B) + +then the following will occur: + + +-------+ : : : : + | | +------+ +-------+ + | |------>| B=2 |----- --->| Y->8 | + | | : +------+ \ +-------+ + | CPU 1 | : | A=1 | \ --->| C->&Y | + | | +------+ | +-------+ + | | wwwwwwwwwwwwwwww | : : + | | +------+ | : : + | | : | C=&B |--- | : : +-------+ + | | : +------+ \ | +-------+ | | + | |------>| D=4 | ----------->| C->&B |------>| | + | | +------+ | +-------+ | | + +-------+ : : | : : | | + | : : | | + | : : | CPU 2 | + | +-------+ | | + \ | X->9 |------>| | + \ +-------+ | | + ----->| B->2 | | | + +-------+ | | + Makes sure all effects ---> ddddddddddddddddd | | + prior to the store of C +-------+ | | + are perceptible to | B->2 |------>| | + successive loads +-------+ | | + : : +-------+ + + +And thirdly, a read barrier acts as a partial order on loads. Consider the +following sequence of events: + + CPU 1 CPU 2 + ======================= ======================= + STORE A=1 + STORE B=2 + STORE C=3 + + STORE D=4 + STORE E=5 + LOAD A + LOAD B + LOAD C + LOAD D + LOAD E + +Without intervention, CPU 2 may then choose to perceive the events on CPU 1 in +some effectively random order, despite the write barrier issued by CPU 1: + + +-------+ : : + | | +------+ + | |------>| C=3 | } + | | : +------+ } + | | : | A=1 | } + | | : +------+ } + | CPU 1 | : | B=2 | }--- + | | +------+ } \ + | | wwwwwwwwwwwww} \ + | | +------+ } \ : : +-------+ + | | : | E=5 | } \ +-------+ | | + | | : +------+ } \ { | C->3 |------>| | + | |------>| D=4 | } \ { +-------+ : | | + | | +------+ \ { | E->5 | : | | + +-------+ : : \ { +-------+ : | | + Transfer -->{ | A->1 | : | CPU 2 | + from CPU 1 { +-------+ : | | + to CPU 2 { | D->4 | : | | + { +-------+ : | | + { | B->2 |------>| | + +-------+ | | + : : +-------+ + + +If, however, a read barrier were to be placed between the load of C and the +load of D on CPU 2, then the partial ordering imposed by CPU 1 will be +perceived correctly by CPU 2. + + +-------+ : : + | | +------+ + | |------>| C=3 | } + | | : +------+ } + | | : | A=1 | }--- + | | : +------+ } \ + | CPU 1 | : | B=2 | } \ + | | +------+ \ + | | wwwwwwwwwwwwwwww \ + | | +------+ \ : : +-------+ + | | : | E=5 | } \ +-------+ | | + | | : +------+ }--- \ { | C->3 |------>| | + | |------>| D=4 | } \ \ { +-------+ : | | + | | +------+ \ -->{ | B->2 | : | | + +-------+ : : \ { +-------+ : | | + \ { | A->1 | : | CPU 2 | + \ +-------+ | | + At this point the read ----> \ rrrrrrrrrrrrrrrrr | | + barrier causes all effects \ +-------+ | | + prior to the storage of C \ { | E->5 | : | | + to be perceptible to CPU 2 -->{ +-------+ : | | + { | D->4 |------>| | + +-------+ | | + : : +-------+ + + +======================== +EXPLICIT KERNEL BARRIERS +======================== + +The Linux kernel has a variety of different barriers that act at different +levels: + + (*) Compiler barrier. + + (*) CPU memory barriers. + + (*) MMIO write barrier. + + +COMPILER BARRIER +---------------- + +The Linux kernel has an explicit compiler barrier function that prevents the +compiler from moving the memory accesses either side of it to the other side: + + barrier(); + +This a general barrier - lesser varieties of compiler barrier do not exist. + +The compiler barrier has no direct effect on the CPU, which may then reorder +things however it wishes. + + +CPU MEMORY BARRIERS +------------------- + +The Linux kernel has eight basic CPU memory barriers: + + TYPE MANDATORY SMP CONDITIONAL + =============== ======================= =========================== + GENERAL mb() smp_mb() + WRITE wmb() smp_wmb() + READ rmb() smp_rmb() + DATA DEPENDENCY read_barrier_depends() smp_read_barrier_depends() + + +All CPU memory barriers unconditionally imply compiler barriers. + +SMP memory barriers are reduced to compiler barriers on uniprocessor compiled +systems because it is assumed that a CPU will be appear to be self-consistent, +and will order overlapping accesses correctly with respect to itself. + +[!] Note that SMP memory barriers _must_ be used to control the ordering of +references to shared memory on SMP systems, though the use of locking instead +is sufficient. + +Mandatory barriers should not be used to control SMP effects, since mandatory +barriers unnecessarily impose overhead on UP systems. They may, however, be +used to control MMIO effects on accesses through relaxed memory I/O windows. +These are required even on non-SMP systems as they affect the order in which +memory operations appear to a device by prohibiting both the compiler and the +CPU from reordering them. + + +There are some more advanced barrier functions: + + (*) set_mb(var, value) + (*) set_wmb(var, value) + + These assign the value to the variable and then insert at least a write + barrier after it, depending on the function. They aren't guaranteed to + insert anything more than a compiler barrier in a UP compilation. + + + (*) smp_mb__before_atomic_dec(); + (*) smp_mb__after_atomic_dec(); + (*) smp_mb__before_atomic_inc(); + (*) smp_mb__after_atomic_inc(); + + These are for use with atomic add, subtract, increment and decrement + functions that don't return a value, especially when used for reference + counting. These functions do not imply memory barriers. + + As an example, consider a piece of code that marks an object as being dead + and then decrements the object's reference count: + + obj->dead = 1; + smp_mb__before_atomic_dec(); + atomic_dec(&obj->ref_count); + + This makes sure that the death mark on the object is perceived to be set + *before* the reference counter is decremented. + + See Documentation/atomic_ops.txt for more information. See the "Atomic + operations" subsection for information on where to use these. + + + (*) smp_mb__before_clear_bit(void); + (*) smp_mb__after_clear_bit(void); + + These are for use similar to the atomic inc/dec barriers. These are + typically used for bitwise unlocking operations, so care must be taken as + there are no implicit memory barriers here either. + + Consider implementing an unlock operation of some nature by clearing a + locking bit. The clear_bit() would then need to be barriered like this: + + smp_mb__before_clear_bit(); + clear_bit( ... ); + + This prevents memory operations before the clear leaking to after it. See + the subsection on "Locking Functions" with reference to UNLOCK operation + implications. + + See Documentation/atomic_ops.txt for more information. See the "Atomic + operations" subsection for information on where to use these. + + +MMIO WRITE BARRIER +------------------ + +The Linux kernel also has a special barrier for use with memory-mapped I/O +writes: + + mmiowb(); + +This is a variation on the mandatory write barrier that causes writes to weakly +ordered I/O regions to be partially ordered. Its effects may go beyond the +CPU->Hardware interface and actually affect the hardware at some level. + +See the subsection "Locks vs I/O accesses" for more information. + + +=============================== +IMPLICIT KERNEL MEMORY BARRIERS +=============================== + +Some of the other functions in the linux kernel imply memory barriers, amongst +which are locking, scheduling and memory allocation functions. + +This specification is a _minimum_ guarantee; any particular architecture may +provide more substantial guarantees, but these may not be relied upon outside +of arch specific code. + + +LOCKING FUNCTIONS +----------------- + +The Linux kernel has a number of locking constructs: + + (*) spin locks + (*) R/W spin locks + (*) mutexes + (*) semaphores + (*) R/W semaphores + (*) RCU + +In all cases there are variants on "LOCK" operations and "UNLOCK" operations +for each construct. These operations all imply certain barriers: + + (1) LOCK operation implication: + + Memory operations issued after the LOCK will be completed after the LOCK + operation has completed. + + Memory operations issued before the LOCK may be completed after the LOCK + operation has completed. + + (2) UNLOCK operation implication: + + Memory operations issued before the UNLOCK will be completed before the + UNLOCK operation has completed. + + Memory operations issued after the UNLOCK may be completed before the + UNLOCK operation has completed. + + (3) LOCK vs LOCK implication: + + All LOCK operations issued before another LOCK operation will be completed + before that LOCK operation. + + (4) LOCK vs UNLOCK implication: + + All LOCK operations issued before an UNLOCK operation will be completed + before the UNLOCK operation. + + All UNLOCK operations issued before a LOCK operation will be completed + before the LOCK operation. + + (5) Failed conditional LOCK implication: + + Certain variants of the LOCK operation may fail, either due to being + unable to get the lock immediately, or due to receiving an unblocked + signal whilst asleep waiting for the lock to become available. Failed + locks do not imply any sort of barrier. + +Therefore, from (1), (2) and (4) an UNLOCK followed by an unconditional LOCK is +equivalent to a full barrier, but a LOCK followed by an UNLOCK is not. + +[!] Note: one of the consequence of LOCKs and UNLOCKs being only one-way + barriers is that the effects instructions outside of a critical section may + seep into the inside of the critical section. + +Locks and semaphores may not provide any guarantee of ordering on UP compiled +systems, and so cannot be counted on in such a situation to actually achieve +anything at all - especially with respect to I/O accesses - unless combined +with interrupt disabling operations. + +See also the section on "Inter-CPU locking barrier effects". + + +As an example, consider the following: + + *A = a; + *B = b; + LOCK + *C = c; + *D = d; + UNLOCK + *E = e; + *F = f; + +The following sequence of events is acceptable: + + LOCK, {*F,*A}, *E, {*C,*D}, *B, UNLOCK + + [+] Note that {*F,*A} indicates a combined access. + +But none of the following are: + + {*F,*A}, *B, LOCK, *C, *D, UNLOCK, *E + *A, *B, *C, LOCK, *D, UNLOCK, *E, *F + *A, *B, LOCK, *C, UNLOCK, *D, *E, *F + *B, LOCK, *C, *D, UNLOCK, {*F,*A}, *E + + + +INTERRUPT DISABLING FUNCTIONS +----------------------------- + +Functions that disable interrupts (LOCK equivalent) and enable interrupts +(UNLOCK equivalent) will act as compiler barriers only. So if memory or I/O +barriers are required in such a situation, they must be provided from some +other means. + + +MISCELLANEOUS FUNCTIONS +----------------------- + +Other functions that imply barriers: + + (*) schedule() and similar imply full memory barriers. + + (*) Memory allocation and release functions imply full memory barriers. + + +================================= +INTER-CPU LOCKING BARRIER EFFECTS +================================= + +On SMP systems locking primitives give a more substantial form of barrier: one +that does affect memory access ordering on other CPUs, within the context of +conflict on any particular lock. + + +LOCKS VS MEMORY ACCESSES +------------------------ + +Consider the following: the system has a pair of spinlocks (N) and (Q), and +three CPUs; then should the following sequence of events occur: + + CPU 1 CPU 2 + =============================== =============================== + *A = a; *E = e; + LOCK M LOCK Q + *B = b; *F = f; + *C = c; *G = g; + UNLOCK M UNLOCK Q + *D = d; *H = h; + +Then there is no guarantee as to what order CPU #3 will see the accesses to *A +through *H occur in, other than the constraints imposed by the separate locks +on the separate CPUs. It might, for example, see: + + *E, LOCK M, LOCK Q, *G, *C, *F, *A, *B, UNLOCK Q, *D, *H, UNLOCK M + +But it won't see any of: + + *B, *C or *D preceding LOCK M + *A, *B or *C following UNLOCK M + *F, *G or *H preceding LOCK Q + *E, *F or *G following UNLOCK Q + + +However, if the following occurs: + + CPU 1 CPU 2 + =============================== =============================== + *A = a; + LOCK M [1] + *B = b; + *C = c; + UNLOCK M [1] + *D = d; *E = e; + LOCK M [2] + *F = f; + *G = g; + UNLOCK M [2] + *H = h; + +CPU #3 might see: + + *E, LOCK M [1], *C, *B, *A, UNLOCK M [1], + LOCK M [2], *H, *F, *G, UNLOCK M [2], *D + +But assuming CPU #1 gets the lock first, it won't see any of: + + *B, *C, *D, *F, *G or *H preceding LOCK M [1] + *A, *B or *C following UNLOCK M [1] + *F, *G or *H preceding LOCK M [2] + *A, *B, *C, *E, *F or *G following UNLOCK M [2] + + +LOCKS VS I/O ACCESSES +--------------------- + +Under certain circumstances (especially involving NUMA), I/O accesses within +two spinlocked sections on two different CPUs may be seen as interleaved by the +PCI bridge, because the PCI bridge does not necessarily participate in the +cache-coherence protocol, and is therefore incapable of issuing the required +read memory barriers. + +For example: + + CPU 1 CPU 2 + =============================== =============================== + spin_lock(Q) + writel(0, ADDR) + writel(1, DATA); + spin_unlock(Q); + spin_lock(Q); + writel(4, ADDR); + writel(5, DATA); + spin_unlock(Q); + +may be seen by the PCI bridge as follows: + + STORE *ADDR = 0, STORE *ADDR = 4, STORE *DATA = 1, STORE *DATA = 5 + +which would probably cause the hardware to malfunction. + + +What is necessary here is to intervene with an mmiowb() before dropping the +spinlock, for example: + + CPU 1 CPU 2 + =============================== =============================== + spin_lock(Q) + writel(0, ADDR) + writel(1, DATA); + mmiowb(); + spin_unlock(Q); + spin_lock(Q); + writel(4, ADDR); + writel(5, DATA); + mmiowb(); + spin_unlock(Q); + +this will ensure that the two stores issued on CPU #1 appear at the PCI bridge +before either of the stores issued on CPU #2. + + +Furthermore, following a store by a load to the same device obviates the need +for an mmiowb(), because the load forces the store to complete before the load +is performed: + + CPU 1 CPU 2 + =============================== =============================== + spin_lock(Q) + writel(0, ADDR) + a = readl(DATA); + spin_unlock(Q); + spin_lock(Q); + writel(4, ADDR); + b = readl(DATA); + spin_unlock(Q); + + +See Documentation/DocBook/deviceiobook.tmpl for more information. + + +================================= +WHERE ARE MEMORY BARRIERS NEEDED? +================================= + +Under normal operation, memory operation reordering is generally not going to +be a problem as a single-threaded linear piece of code will still appear to +work correctly, even if it's in an SMP kernel. There are, however, three +circumstances in which reordering definitely _could_ be a problem: + + (*) Interprocessor interaction. + + (*) Atomic operations. + + (*) Accessing devices (I/O). + + (*) Interrupts. + + +INTERPROCESSOR INTERACTION +-------------------------- + +When there's a system with more than one processor, more than one CPU in the +system may be working on the same data set at the same time. This can cause +synchronisation problems, and the usual way of dealing with them is to use +locks. Locks, however, are quite expensive, and so it may be preferable to +operate without the use of a lock if at all possible. In such a case +operations that affect both CPUs may have to be carefully ordered to prevent +a malfunction. + +Consider, for example, the R/W semaphore slow path. Here a waiting process is +queued on the semaphore, by virtue of it having a piece of its stack linked to +the semaphore's list of waiting processes: + + struct rw_semaphore { + ... + spinlock_t lock; + struct list_head waiters; + }; + + struct rwsem_waiter { + struct list_head list; + struct task_struct *task; + }; + +To wake up a particular waiter, the up_read() or up_write() functions have to: + + (1) read the next pointer from this waiter's record to know as to where the + next waiter record is; + + (4) read the pointer to the waiter's task structure; + + (3) clear the task pointer to tell the waiter it has been given the semaphore; + + (4) call wake_up_process() on the task; and + + (5) release the reference held on the waiter's task struct. + +In otherwords, it has to perform this sequence of events: + + LOAD waiter->list.next; + LOAD waiter->task; + STORE waiter->task; + CALL wakeup + RELEASE task + +and if any of these steps occur out of order, then the whole thing may +malfunction. + +Once it has queued itself and dropped the semaphore lock, the waiter does not +get the lock again; it instead just waits for its task pointer to be cleared +before proceeding. Since the record is on the waiter's stack, this means that +if the task pointer is cleared _before_ the next pointer in the list is read, +another CPU might start processing the waiter and might clobber the waiter's +stack before the up*() function has a chance to read the next pointer. + +Consider then what might happen to the above sequence of events: + + CPU 1 CPU 2 + =============================== =============================== + down_xxx() + Queue waiter + Sleep + up_yyy() + LOAD waiter->task; + STORE waiter->task; + Woken up by other event + + Resume processing + down_xxx() returns + call foo() + foo() clobbers *waiter + + LOAD waiter->list.next; + --- OOPS --- + +This could be dealt with using the semaphore lock, but then the down_xxx() +function has to needlessly get the spinlock again after being woken up. + +The way to deal with this is to insert a general SMP memory barrier: + + LOAD waiter->list.next; + LOAD waiter->task; + smp_mb(); + STORE waiter->task; + CALL wakeup + RELEASE task + +In this case, the barrier makes a guarantee that all memory accesses before the +barrier will appear to happen before all the memory accesses after the barrier +with respect to the other CPUs on the system. It does _not_ guarantee that all +the memory accesses before the barrier will be complete by the time the barrier +instruction itself is complete. + +On a UP system - where this wouldn't be a problem - the smp_mb() is just a +compiler barrier, thus making sure the compiler emits the instructions in the +right order without actually intervening in the CPU. Since there there's only +one CPU, that CPU's dependency ordering logic will take care of everything +else. + + +ATOMIC OPERATIONS +----------------- + +Whilst they are technically interprocessor interaction considerations, atomic +operations are noted specially as some of them imply full memory barriers and +some don't, but they're very heavily relied on as a group throughout the +kernel. + +Any atomic operation that modifies some state in memory and returns information +about the state (old or new) implies an SMP-conditional general memory barrier +(smp_mb()) on each side of the actual operation. These include: + + xchg(); + cmpxchg(); + atomic_cmpxchg(); + atomic_inc_return(); + atomic_dec_return(); + atomic_add_return(); + atomic_sub_return(); + atomic_inc_and_test(); + atomic_dec_and_test(); + atomic_sub_and_test(); + atomic_add_negative(); + atomic_add_unless(); + test_and_set_bit(); + test_and_clear_bit(); + test_and_change_bit(); + +These are used for such things as implementing LOCK-class and UNLOCK-class +operations and adjusting reference counters towards object destruction, and as +such the implicit memory barrier effects are necessary. + + +The following operation are potential problems as they do _not_ imply memory +barriers, but might be used for implementing such things as UNLOCK-class +operations: + + atomic_set(); + set_bit(); + clear_bit(); + change_bit(); + +With these the appropriate explicit memory barrier should be used if necessary +(smp_mb__before_clear_bit() for instance). + + +The following also do _not_ imply memory barriers, and so may require explicit +memory barriers under some circumstances (smp_mb__before_atomic_dec() for +instance)): + + atomic_add(); + atomic_sub(); + atomic_inc(); + atomic_dec(); + +If they're used for statistics generation, then they probably don't need memory +barriers, unless there's a coupling between statistical data. + +If they're used for reference counting on an object to control its lifetime, +they probably don't need memory barriers because either the reference count +will be adjusted inside a locked section, or the caller will already hold +sufficient references to make the lock, and thus a memory barrier unnecessary. + +If they're used for constructing a lock of some description, then they probably +do need memory barriers as a lock primitive generally has to do things in a +specific order. + + +Basically, each usage case has to be carefully considered as to whether memory +barriers are needed or not. + +[!] Note that special memory barrier primitives are available for these +situations because on some CPUs the atomic instructions used imply full memory +barriers, and so barrier instructions are superfluous in conjunction with them, +and in such cases the special barrier primitives will be no-ops. + +See Documentation/atomic_ops.txt for more information. + + +ACCESSING DEVICES +----------------- + +Many devices can be memory mapped, and so appear to the CPU as if they're just +a set of memory locations. To control such a device, the driver usually has to +make the right memory accesses in exactly the right order. + +However, having a clever CPU or a clever compiler creates a potential problem +in that the carefully sequenced accesses in the driver code won't reach the +device in the requisite order if the CPU or the compiler thinks it is more +efficient to reorder, combine or merge accesses - something that would cause +the device to malfunction. + +Inside of the Linux kernel, I/O should be done through the appropriate accessor +routines - such as inb() or writel() - which know how to make such accesses +appropriately sequential. Whilst this, for the most part, renders the explicit +use of memory barriers unnecessary, there are a couple of situations where they +might be needed: + + (1) On some systems, I/O stores are not strongly ordered across all CPUs, and + so for _all_ general drivers locks should be used and mmiowb() must be + issued prior to unlocking the critical section. + + (2) If the accessor functions are used to refer to an I/O memory window with + relaxed memory access properties, then _mandatory_ memory barriers are + required to enforce ordering. + +See Documentation/DocBook/deviceiobook.tmpl for more information. + + +INTERRUPTS +---------- + +A driver may be interrupted by its own interrupt service routine, and thus the +two parts of the driver may interfere with each other's attempts to control or +access the device. + +This may be alleviated - at least in part - by disabling local interrupts (a +form of locking), such that the critical operations are all contained within +the interrupt-disabled section in the driver. Whilst the driver's interrupt +routine is executing, the driver's core may not run on the same CPU, and its +interrupt is not permitted to happen again until the current interrupt has been +handled, thus the interrupt handler does not need to lock against that. + +However, consider a driver that was talking to an ethernet card that sports an +address register and a data register. If that driver's core talks to the card +under interrupt-disablement and then the driver's interrupt handler is invoked: + + LOCAL IRQ DISABLE + writew(ADDR, 3); + writew(DATA, y); + LOCAL IRQ ENABLE + + writew(ADDR, 4); + q = readw(DATA); + + +The store to the data register might happen after the second store to the +address register if ordering rules are sufficiently relaxed: + + STORE *ADDR = 3, STORE *ADDR = 4, STORE *DATA = y, q = LOAD *DATA + + +If ordering rules are relaxed, it must be assumed that accesses done inside an +interrupt disabled section may leak outside of it and may interleave with +accesses performed in an interrupt - and vice versa - unless implicit or +explicit barriers are used. + +Normally this won't be a problem because the I/O accesses done inside such +sections will include synchronous load operations on strictly ordered I/O +registers that form implicit I/O barriers. If this isn't sufficient then an +mmiowb() may need to be used explicitly. + + +A similar situation may occur between an interrupt routine and two routines +running on separate CPUs that communicate with each other. If such a case is +likely, then interrupt-disabling locks should be used to guarantee ordering. + + +========================== +KERNEL I/O BARRIER EFFECTS +========================== + +When accessing I/O memory, drivers should use the appropriate accessor +functions: + + (*) inX(), outX(): + + These are intended to talk to I/O space rather than memory space, but + that's primarily a CPU-specific concept. The i386 and x86_64 processors do + indeed have special I/O space access cycles and instructions, but many + CPUs don't have such a concept. + + The PCI bus, amongst others, defines an I/O space concept - which on such + CPUs as i386 and x86_64 cpus readily maps to the CPU's concept of I/O + space. However, it may also mapped as a virtual I/O space in the CPU's + memory map, particularly on those CPUs that don't support alternate + I/O spaces. + + Accesses to this space may be fully synchronous (as on i386), but + intermediary bridges (such as the PCI host bridge) may not fully honour + that. + + They are guaranteed to be fully ordered with respect to each other. + + They are not guaranteed to be fully ordered with respect to other types of + memory and I/O operation. + + (*) readX(), writeX(): + + Whether these are guaranteed to be fully ordered and uncombined with + respect to each other on the issuing CPU depends on the characteristics + defined for the memory window through which they're accessing. On later + i386 architecture machines, for example, this is controlled by way of the + MTRR registers. + + Ordinarily, these will be guaranteed to be fully ordered and uncombined,, + provided they're not accessing a prefetchable device. + + However, intermediary hardware (such as a PCI bridge) may indulge in + deferral if it so wishes; to flush a store, a load from the same location + is preferred[*], but a load from the same device or from configuration + space should suffice for PCI. + + [*] NOTE! attempting to load from the same location as was written to may + cause a malfunction - consider the 16550 Rx/Tx serial registers for + example. + + Used with prefetchable I/O memory, an mmiowb() barrier may be required to + force stores to be ordered. + + Please refer to the PCI specification for more information on interactions + between PCI transactions. + + (*) readX_relaxed() + + These are similar to readX(), but are not guaranteed to be ordered in any + way. Be aware that there is no I/O read barrier available. + + (*) ioreadX(), iowriteX() + + These will perform as appropriate for the type of access they're actually + doing, be it inX()/outX() or readX()/writeX(). + + +======================================== +ASSUMED MINIMUM EXECUTION ORDERING MODEL +======================================== + +It has to be assumed that the conceptual CPU is weakly-ordered but that it will +maintain the appearance of program causality with respect to itself. Some CPUs +(such as i386 or x86_64) are more constrained than others (such as powerpc or +frv), and so the most relaxed case (namely DEC Alpha) must be assumed outside +of arch-specific code. + +This means that it must be considered that the CPU will execute its instruction +stream in any order it feels like - or even in parallel - provided that if an +instruction in the stream depends on the an earlier instruction, then that +earlier instruction must be sufficiently complete[*] before the later +instruction may proceed; in other words: provided that the appearance of +causality is maintained. + + [*] Some instructions have more than one effect - such as changing the + condition codes, changing registers or changing memory - and different + instructions may depend on different effects. + +A CPU may also discard any instruction sequence that winds up having no +ultimate effect. For example, if two adjacent instructions both load an +immediate value into the same register, the first may be discarded. + + +Similarly, it has to be assumed that compiler might reorder the instruction +stream in any way it sees fit, again provided the appearance of causality is +maintained. + + +============================ +THE EFFECTS OF THE CPU CACHE +============================ + +The way cached memory operations are perceived across the system is affected to +a certain extent by the caches that lie between CPUs and memory, and by the +memory coherence system that maintains the consistency of state in the system. + +As far as the way a CPU interacts with another part of the system through the +caches goes, the memory system has to include the CPU's caches, and memory +barriers for the most part act at the interface between the CPU and its cache +(memory barriers logically act on the dotted line in the following diagram): + + <--- CPU ---> : <----------- Memory -----------> + : + +--------+ +--------+ : +--------+ +-----------+ + | | | | : | | | | +--------+ + | CPU | | Memory | : | CPU | | | | | + | Core |--->| Access |----->| Cache |<-->| | | | + | | | Queue | : | | | |--->| Memory | + | | | | : | | | | | | + +--------+ +--------+ : +--------+ | | | | + : | Cache | +--------+ + : | Coherency | + : | Mechanism | +--------+ + +--------+ +--------+ : +--------+ | | | | + | | | | : | | | | | | + | CPU | | Memory | : | CPU | | |--->| Device | + | Core |--->| Access |----->| Cache |<-->| | | | + | | | Queue | : | | | | | | + | | | | : | | | | +--------+ + +--------+ +--------+ : +--------+ +-----------+ + : + : + +Although any particular load or store may not actually appear outside of the +CPU that issued it since it may have been satisfied within the CPU's own cache, +it will still appear as if the full memory access had taken place as far as the +other CPUs are concerned since the cache coherency mechanisms will migrate the +cacheline over to the accessing CPU and propagate the effects upon conflict. + +The CPU core may execute instructions in any order it deems fit, provided the +expected program causality appears to be maintained. Some of the instructions +generate load and store operations which then go into the queue of memory +accesses to be performed. The core may place these in the queue in any order +it wishes, and continue execution until it is forced to wait for an instruction +to complete. + +What memory barriers are concerned with is controlling the order in which +accesses cross from the CPU side of things to the memory side of things, and +the order in which the effects are perceived to happen by the other observers +in the system. + +[!] Memory barriers are _not_ needed within a given CPU, as CPUs always see +their own loads and stores as if they had happened in program order. + +[!] MMIO or other device accesses may bypass the cache system. This depends on +the properties of the memory window through which devices are accessed and/or +the use of any special device communication instructions the CPU may have. + + +CACHE COHERENCY +--------------- + +Life isn't quite as simple as it may appear above, however: for while the +caches are expected to be coherent, there's no guarantee that that coherency +will be ordered. This means that whilst changes made on one CPU will +eventually become visible on all CPUs, there's no guarantee that they will +become apparent in the same order on those other CPUs. + + +Consider dealing with a system that has pair of CPUs (1 & 2), each of which has +a pair of parallel data caches (CPU 1 has A/B, and CPU 2 has C/D): + + : + : +--------+ + : +---------+ | | + +--------+ : +--->| Cache A |<------->| | + | | : | +---------+ | | + | CPU 1 |<---+ | | + | | : | +---------+ | | + +--------+ : +--->| Cache B |<------->| | + : +---------+ | | + : | Memory | + : +---------+ | System | + +--------+ : +--->| Cache C |<------->| | + | | : | +---------+ | | + | CPU 2 |<---+ | | + | | : | +---------+ | | + +--------+ : +--->| Cache D |<------->| | + : +---------+ | | + : +--------+ + : + +Imagine the system has the following properties: + + (*) an odd-numbered cache line may be in cache A, cache C or it may still be + resident in memory; + + (*) an even-numbered cache line may be in cache B, cache D or it may still be + resident in memory; + + (*) whilst the CPU core is interrogating one cache, the other cache may be + making use of the bus to access the rest of the system - perhaps to + displace a dirty cacheline or to do a speculative load; + + (*) each cache has a queue of operations that need to be applied to that cache + to maintain coherency with the rest of the system; + + (*) the coherency queue is not flushed by normal loads to lines already + present in the cache, even though the contents of the queue may + potentially effect those loads. + +Imagine, then, that two writes are made on the first CPU, with a write barrier +between them to guarantee that they will appear to reach that CPU's caches in +the requisite order: + + CPU 1 CPU 2 COMMENT + =============== =============== ======================================= + u == 0, v == 1 and p == &u, q == &u + v = 2; + smp_wmb(); Make sure change to v visible before + change to p + v is now in cache A exclusively + p = &v; + p is now in cache B exclusively + +The write memory barrier forces the other CPUs in the system to perceive that +the local CPU's caches have apparently been updated in the correct order. But +now imagine that the second CPU that wants to read those values: + + CPU 1 CPU 2 COMMENT + =============== =============== ======================================= + ... + q = p; + x = *q; + +The above pair of reads may then fail to happen in expected order, as the +cacheline holding p may get updated in one of the second CPU's caches whilst +the update to the cacheline holding v is delayed in the other of the second +CPU's caches by some other cache event: + + CPU 1 CPU 2 COMMENT + =============== =============== ======================================= + u == 0, v == 1 and p == &u, q == &u + v = 2; + smp_wmb(); + + + p = &b; q = p; + + + + x = *q; + Reads from v before v updated in cache + + + +Basically, whilst both cachelines will be updated on CPU 2 eventually, there's +no guarantee that, without intervention, the order of update will be the same +as that committed on CPU 1. + + +To intervene, we need to interpolate a data dependency barrier or a read +barrier between the loads. This will force the cache to commit its coherency +queue before processing any further requests: + + CPU 1 CPU 2 COMMENT + =============== =============== ======================================= + u == 0, v == 1 and p == &u, q == &u + v = 2; + smp_wmb(); + + + p = &b; q = p; + + + + smp_read_barrier_depends() + + + x = *q; + Reads from v after v updated in cache + + +This sort of problem can be encountered on DEC Alpha processors as they have a +split cache that improves performance by making better use of the data bus. +Whilst most CPUs do imply a data dependency barrier on the read when a memory +access depends on a read, not all do, so it may not be relied on. + +Other CPUs may also have split caches, but must coordinate between the various +cachelets for normal memory accesss. The semantics of the Alpha removes the +need for coordination in absence of memory barriers. + + +CACHE COHERENCY VS DMA +---------------------- + +Not all systems maintain cache coherency with respect to devices doing DMA. In +such cases, a device attempting DMA may obtain stale data from RAM because +dirty cache lines may be resident in the caches of various CPUs, and may not +have been written back to RAM yet. To deal with this, the appropriate part of +the kernel must flush the overlapping bits of cache on each CPU (and maybe +invalidate them as well). + +In addition, the data DMA'd to RAM by a device may be overwritten by dirty +cache lines being written back to RAM from a CPU's cache after the device has +installed its own data, or cache lines simply present in a CPUs cache may +simply obscure the fact that RAM has been updated, until at such time as the +cacheline is discarded from the CPU's cache and reloaded. To deal with this, +the appropriate part of the kernel must invalidate the overlapping bits of the +cache on each CPU. + +See Documentation/cachetlb.txt for more information on cache management. + + +CACHE COHERENCY VS MMIO +----------------------- + +Memory mapped I/O usually takes place through memory locations that are part of +a window in the CPU's memory space that have different properties assigned than +the usual RAM directed window. + +Amongst these properties is usually the fact that such accesses bypass the +caching entirely and go directly to the device buses. This means MMIO accesses +may, in effect, overtake accesses to cached memory that were emitted earlier. +A memory barrier isn't sufficient in such a case, but rather the cache must be +flushed between the cached memory write and the MMIO access if the two are in +any way dependent. + + +========================= +THE THINGS CPUS GET UP TO +========================= + +A programmer might take it for granted that the CPU will perform memory +operations in exactly the order specified, so that if a CPU is, for example, +given the following piece of code to execute: + + a = *A; + *B = b; + c = *C; + d = *D; + *E = e; + +They would then expect that the CPU will complete the memory operation for each +instruction before moving on to the next one, leading to a definite sequence of +operations as seen by external observers in the system: + + LOAD *A, STORE *B, LOAD *C, LOAD *D, STORE *E. + + +Reality is, of course, much messier. With many CPUs and compilers, the above +assumption doesn't hold because: + + (*) loads are more likely to need to be completed immediately to permit + execution progress, whereas stores can often be deferred without a + problem; + + (*) loads may be done speculatively, and the result discarded should it prove + to have been unnecessary; + + (*) loads may be done speculatively, leading to the result having being + fetched at the wrong time in the expected sequence of events; + + (*) the order of the memory accesses may be rearranged to promote better use + of the CPU buses and caches; + + (*) loads and stores may be combined to improve performance when talking to + memory or I/O hardware that can do batched accesses of adjacent locations, + thus cutting down on transaction setup costs (memory and PCI devices may + both be able to do this); and + + (*) the CPU's data cache may affect the ordering, and whilst cache-coherency + mechanisms may alleviate this - once the store has actually hit the cache + - there's no guarantee that the coherency management will be propagated in + order to other CPUs. + +So what another CPU, say, might actually observe from the above piece of code +is: + + LOAD *A, ..., LOAD {*C,*D}, STORE *E, STORE *B + + (Where "LOAD {*C,*D}" is a combined load) + + +However, it is guaranteed that a CPU will be self-consistent: it will see its +_own_ accesses appear to be correctly ordered, without the need for a memory +barrier. For instance with the following code: + + U = *A; + *A = V; + *A = W; + X = *A; + *A = Y; + Z = *A; + +and assuming no intervention by an external influence, it can be assumed that +the final result will appear to be: + + U == the original value of *A + X == W + Z == Y + *A == Y + +The code above may cause the CPU to generate the full sequence of memory +accesses: + + U=LOAD *A, STORE *A=V, STORE *A=W, X=LOAD *A, STORE *A=Y, Z=LOAD *A + +in that order, but, without intervention, the sequence may have almost any +combination of elements combined or discarded, provided the program's view of +the world remains consistent. + +The compiler may also combine, discard or defer elements of the sequence before +the CPU even sees them. + +For instance: + + *A = V; + *A = W; + +may be reduced to: + + *A = W; + +since, without a write barrier, it can be assumed that the effect of the +storage of V to *A is lost. Similarly: + + *A = Y; + Z = *A; + +may, without a memory barrier, be reduced to: + + *A = Y; + Z = Y; + +and the LOAD operation never appear outside of the CPU. + + +AND THEN THERE'S THE ALPHA +-------------------------- + +The DEC Alpha CPU is one of the most relaxed CPUs there is. Not only that, +some versions of the Alpha CPU have a split data cache, permitting them to have +two semantically related cache lines updating at separate times. This is where +the data dependency barrier really becomes necessary as this synchronises both +caches with the memory coherence system, thus making it seem like pointer +changes vs new data occur in the right order. + +The Alpha defines the Linux's kernel's memory barrier model. + +See the subsection on "Cache Coherency" above. + + +========== +REFERENCES +========== + +Alpha AXP Architecture Reference Manual, Second Edition (Sites & Witek, +Digital Press) + Chapter 5.2: Physical Address Space Characteristics + Chapter 5.4: Caches and Write Buffers + Chapter 5.5: Data Sharing + Chapter 5.6: Read/Write Ordering + +AMD64 Architecture Programmer's Manual Volume 2: System Programming + Chapter 7.1: Memory-Access Ordering + Chapter 7.4: Buffering and Combining Memory Writes + +IA-32 Intel Architecture Software Developer's Manual, Volume 3: +System Programming Guide + Chapter 7.1: Locked Atomic Operations + Chapter 7.2: Memory Ordering + Chapter 7.4: Serializing Instructions + +The SPARC Architecture Manual, Version 9 + Chapter 8: Memory Models + Appendix D: Formal Specification of the Memory Models + Appendix J: Programming with the Memory Models + +UltraSPARC Programmer Reference Manual + Chapter 5: Memory Accesses and Cacheability + Chapter 15: Sparc-V9 Memory Models + +UltraSPARC III Cu User's Manual + Chapter 9: Memory Models + +UltraSPARC IIIi Processor User's Manual + Chapter 8: Memory Models + +UltraSPARC Architecture 2005 + Chapter 9: Memory + Appendix D: Formal Specifications of the Memory Models + +UltraSPARC T1 Supplement to the UltraSPARC Architecture 2005 + Chapter 8: Memory Models + Appendix F: Caches and Cache Coherency + +Solaris Internals, Core Kernel Architecture, p63-68: + Chapter 3.3: Hardware Considerations for Locks and + Synchronization + +Unix Systems for Modern Architectures, Symmetric Multiprocessing and Caching +for Kernel Programmers: + Chapter 13: Other Memory Models + +Intel Itanium Architecture Software Developer's Manual: Volume 1: + Section 2.6: Speculation + Section 4.4: Memory Access diff --git a/Documentation/mtrr.txt b/Documentation/mtrr.txt index b78af1c32996..c39ac395970e 100644 --- a/Documentation/mtrr.txt +++ b/Documentation/mtrr.txt @@ -138,19 +138,29 @@ Reading MTRRs from a C program using ioctl()'s: */ #include +#include #include #include #include #include #include #include -#define MTRR_NEED_STRINGS #include #define TRUE 1 #define FALSE 0 #define ERRSTRING strerror (errno) +static char *mtrr_strings[MTRR_NUM_TYPES] = +{ + "uncachable", /* 0 */ + "write-combining", /* 1 */ + "?", /* 2 */ + "?", /* 3 */ + "write-through", /* 4 */ + "write-protect", /* 5 */ + "write-back", /* 6 */ +}; int main () { @@ -232,13 +242,22 @@ Creating MTRRs from a C programme using ioctl()'s: #include #include #include -#define MTRR_NEED_STRINGS #include #define TRUE 1 #define FALSE 0 #define ERRSTRING strerror (errno) +static char *mtrr_strings[MTRR_NUM_TYPES] = +{ + "uncachable", /* 0 */ + "write-combining", /* 1 */ + "?", /* 2 */ + "?", /* 3 */ + "write-through", /* 4 */ + "write-protect", /* 5 */ + "write-back", /* 6 */ +}; int main (int argc, char **argv) { diff --git a/Documentation/networking/bcm43xx.txt b/Documentation/networking/bcm43xx.txt new file mode 100644 index 000000000000..28541d2bee1e --- /dev/null +++ b/Documentation/networking/bcm43xx.txt @@ -0,0 +1,36 @@ + + BCM43xx Linux Driver Project + ============================ + +About this software +------------------- + +The goal of this project is to develop a linux driver for Broadcom +BCM43xx chips, based on the specification at +http://bcm-specs.sipsolutions.net/ + +The project page is http://bcm43xx.berlios.de/ + + +Requirements +------------ + +1) Linux Kernel 2.6.16 or later + http://www.kernel.org/ + + You may want to configure your kernel with: + + CONFIG_DEBUG_FS (optional): + -> Kernel hacking + -> Debug Filesystem + +2) SoftMAC IEEE 802.11 Networking Stack extension and patched ieee80211 + modules: + http://softmac.sipsolutions.net/ + +3) Firmware Files + + Please try fwcutter. Fwcutter can extract the firmware from various + binary driver files. It supports driver files from Windows, MacOS and + Linux. You can get fwcutter from http://bcm43xx.berlios.de/. + Also, fwcutter comes with a README file for further instructions. diff --git a/Documentation/networking/packet_mmap.txt b/Documentation/networking/packet_mmap.txt index 4fc8e9874320..aaf99d5f0dad 100644 --- a/Documentation/networking/packet_mmap.txt +++ b/Documentation/networking/packet_mmap.txt @@ -254,7 +254,7 @@ and, the number of frames be * / -Suposse the following parameters, which apply for 2.6 kernel and an +Suppose the following parameters, which apply for 2.6 kernel and an i386 architecture: = 131072 bytes diff --git a/Documentation/networking/tuntap.txt b/Documentation/networking/tuntap.txt index ec3d109d787a..76750fb9151a 100644 --- a/Documentation/networking/tuntap.txt +++ b/Documentation/networking/tuntap.txt @@ -138,7 +138,7 @@ This means that you have to read/write IP packets when you are using tun and ethernet frames when using tap. 5. What is the difference between BPF and TUN/TAP driver? -BFP is an advanced packet filter. It can be attached to existing +BPF is an advanced packet filter. It can be attached to existing network interface. It does not provide a virtual network interface. A TUN/TAP driver does provide a virtual network interface and it is possible to attach BPF to this interface. diff --git a/Documentation/pcmcia/driver-changes.txt b/Documentation/pcmcia/driver-changes.txt index 97420f08c786..4739c5c3face 100644 --- a/Documentation/pcmcia/driver-changes.txt +++ b/Documentation/pcmcia/driver-changes.txt @@ -1,5 +1,11 @@ This file details changes in 2.6 which affect PCMCIA card driver authors: +* New release helper (as of 2.6.17) + Instead of calling pcmcia_release_{configuration,io,irq,win}, all that's + necessary now is calling pcmcia_disable_device. As there is no valid + reason left to call pcmcia_release_io and pcmcia_release_irq, the + exports for them were removed. + * Unify detach and REMOVAL event code, as well as attach and INSERTION code (as of 2.6.16) void (*remove) (struct pcmcia_device *dev); diff --git a/Documentation/scsi/scsi_eh.txt b/Documentation/scsi/scsi_eh.txt index 331afd791cbb..ce767b90bb0d 100644 --- a/Documentation/scsi/scsi_eh.txt +++ b/Documentation/scsi/scsi_eh.txt @@ -19,9 +19,9 @@ TABLE OF CONTENTS [2-1-1] Overview [2-1-2] Flow of scmds through EH [2-1-3] Flow of control - [2-2] EH through hostt->eh_strategy_handler() - [2-2-1] Pre hostt->eh_strategy_handler() SCSI midlayer conditions - [2-2-2] Post hostt->eh_strategy_handler() SCSI midlayer conditions + [2-2] EH through transportt->eh_strategy_handler() + [2-2-1] Pre transportt->eh_strategy_handler() SCSI midlayer conditions + [2-2-2] Post transportt->eh_strategy_handler() SCSI midlayer conditions [2-2-3] Things to consider @@ -413,9 +413,9 @@ scmd->allowed. layer of failure of the scmds. -[2-2] EH through hostt->eh_strategy_handler() +[2-2] EH through transportt->eh_strategy_handler() - hostt->eh_strategy_handler() is invoked in the place of + transportt->eh_strategy_handler() is invoked in the place of scsi_unjam_host() and it is responsible for whole recovery process. On completion, the handler should have made lower layers forget about all failed scmds and either ready for new commands or offline. Also, @@ -424,7 +424,7 @@ SCSI midlayer. IOW, of the steps described in [2-1-2], all steps except for #1 must be implemented by eh_strategy_handler(). -[2-2-1] Pre hostt->eh_strategy_handler() SCSI midlayer conditions +[2-2-1] Pre transportt->eh_strategy_handler() SCSI midlayer conditions The following conditions are true on entry to the handler. @@ -437,7 +437,7 @@ except for #1 must be implemented by eh_strategy_handler(). - shost->host_failed == shost->host_busy -[2-2-2] Post hostt->eh_strategy_handler() SCSI midlayer conditions +[2-2-2] Post transportt->eh_strategy_handler() SCSI midlayer conditions The following conditions must be true on exit from the handler. diff --git a/Documentation/scsi/scsi_mid_low_api.txt b/Documentation/scsi/scsi_mid_low_api.txt index 8bbae3e1abdf..75a535a975c3 100644 --- a/Documentation/scsi/scsi_mid_low_api.txt +++ b/Documentation/scsi/scsi_mid_low_api.txt @@ -804,7 +804,6 @@ Summary: eh_bus_reset_handler - issue SCSI bus reset eh_device_reset_handler - issue SCSI device reset eh_host_reset_handler - reset host (host bus adapter) - eh_strategy_handler - driver supplied alternate to scsi_unjam_host() info - supply information about given host ioctl - driver can respond to ioctls proc_info - supports /proc/scsi/{driver_name}/{host_no} @@ -969,24 +968,6 @@ Details: int eh_host_reset_handler(struct scsi_cmnd * scp) -/** - * eh_strategy_handler - driver supplied alternate to scsi_unjam_host() - * @shp: host on which error has occurred - * - * Returns TRUE if host unjammed, else FALSE. - * - * Locks: none - * - * Calling context: kernel thread - * - * Notes: Invoked from scsi_eh thread. LLD supplied alternate to - * scsi_unjam_host() found in scsi_error.c - * - * Optionally defined in: LLD - **/ - int eh_strategy_handler(struct Scsi_Host * shp) - - /** * info - supply information about given host: driver name plus data * to distinguish given host diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index 1def6049784c..0ee2c7dfc482 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -120,6 +120,34 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. enable - enable card - Default: enabled, for PCI and ISA PnP cards + Module snd-adlib + ---------------- + + Module for AdLib FM cards. + + port - port # for OPL chip + + This module supports multiple cards. It does not support autoprobe, so + the port must be specified. For actual AdLib FM cards it will be 0x388. + Note that this card does not have PCM support and no mixer; only FM + synthesis. + + Make sure you have "sbiload" from the alsa-tools package available and, + after loading the module, find out the assigned ALSA sequencer port + number through "sbiload -l". Example output: + + Port Client name Port name + 64:0 OPL2 FM synth OPL2 FM Port + + Load the std.sb and drums.sb patches also supplied by sbiload: + + sbiload -p 64:0 std.sb drums.sb + + If you use this driver to drive an OPL3, you can use std.o3 and drums.o3 + instead. To have the card produce sound, use aplaymidi from alsa-utils: + + aplaymidi -p 64:0 foo.mid + Module snd-ad1816a ------------------ @@ -190,6 +218,15 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. The power-management is supported. + Module snd-als300 + ----------------- + + Module for Avance Logic ALS300 and ALS300+ + + This module supports multiple cards. + + The power-management is supported. + Module snd-als4000 ------------------ @@ -701,6 +738,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. uniwill 3-jack F1734 2-jack lg LG laptop (m1 express dual) + lg-lw LG LW20 laptop test for testing/debugging purpose, almost all controls can be adjusted. Appearing only when compiled with $CONFIG_SND_DEBUG=y @@ -1013,6 +1051,23 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. The power-management is supported. + Module snd-miro + --------------- + + Module for Miro soundcards: miroSOUND PCM 1 pro, + miroSOUND PCM 12, + miroSOUND PCM 20 Radio. + + port - Port # (0x530,0x604,0xe80,0xf40) + irq - IRQ # (5,7,9,10,11) + dma1 - 1st dma # (0,1,3) + dma2 - 2nd dma # (0,1) + mpu_port - MPU-401 port # (0x300,0x310,0x320,0x330) + mpu_irq - MPU-401 irq # (5,7,9,10) + fm_port - FM Port # (0x388) + wss - enable WSS mode + ide - enable onboard ide support + Module snd-mixart ----------------- @@ -1202,6 +1257,20 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. The power-management is supported. + Module snd-riptide + ------------------ + + Module for Conexant Riptide chip + + joystick_port - Joystick port # (default: 0x200) + mpu_port - MPU401 port # (default: 0x330) + opl3_port - OPL3 port # (default: 0x388) + + This module supports multiple cards. + The driver requires the firmware loader support on kernel. + You need to install the firmware file "riptide.hex" to the standard + firmware path (e.g. /lib/firmware). + Module snd-rme32 ---------------- diff --git a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl index 6feef9e82b63..68eeebc17ff4 100644 --- a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl +++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl @@ -1123,8 +1123,8 @@ if ((err = pci_enable_device(pci)) < 0) return err; /* check PCI availability (28bit DMA) */ - if (pci_set_dma_mask(pci, 0x0fffffff) < 0 || - pci_set_consistent_dma_mask(pci, 0x0fffffff) < 0) { + if (pci_set_dma_mask(pci, DMA_28BIT_MASK) < 0 || + pci_set_consistent_dma_mask(pci, DMA_28BIT_MASK) < 0) { printk(KERN_ERR "error to set 28bit mask DMA\n"); pci_disable_device(pci); return -ENXIO; @@ -1216,7 +1216,7 @@ The allocation of PCI resources is done in the probe() function, and usually an extra xxx_create() function is written for this - purpose. + purpose. @@ -1225,7 +1225,7 @@ allocating resources. Also, you need to set the proper PCI DMA mask to limit the accessed i/o range. In some cases, you might need to call pci_set_master() function, - too. + too. @@ -1236,8 +1236,8 @@ Now assume that this PCI device has an I/O port with 8 bytes and an interrupt. Then struct mychip will have the - following fields: + following fields: diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index 8c7195455963..bca50903233f 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -52,7 +52,7 @@ 51 -> ProVideo PV952 [1540:9524] 52 -> AverMedia AverTV/305 [1461:2108] 53 -> ASUS TV-FM 7135 [1043:4845] - 54 -> LifeView FlyTV Platinum FM [5168:0214,1489:0214] + 54 -> LifeView FlyTV Platinum FM / Gold [5168:0214,1489:0214,5168:0304] 55 -> LifeView FlyDVB-T DUO [5168:0306] 56 -> Avermedia AVerTV 307 [1461:a70a] 57 -> Avermedia AVerTV GO 007 FM [1461:f31f] @@ -84,7 +84,7 @@ 83 -> Terratec Cinergy 250 PCI TV [153b:1160] 84 -> LifeView FlyDVB Trio [5168:0319] 85 -> AverTV DVB-T 777 [1461:2c05] - 86 -> LifeView FlyDVB-T [5168:0301] + 86 -> LifeView FlyDVB-T / Genius VideoWonder DVB-T [5168:0301,1489:0301] 87 -> ADS Instant TV Duo Cardbus PTV331 [0331:1421] 88 -> Tevion/KWorld DVB-T 220RF [17de:7201] 89 -> ELSA EX-VISION 700TV [1048:226c] @@ -92,3 +92,4 @@ 91 -> AVerMedia A169 B [1461:7360] 92 -> AVerMedia A169 B1 [1461:6360] 93 -> Medion 7134 Bridge #2 [16be:0005] + 94 -> LifeView FlyDVB-T Hybrid Cardbus [5168:3306,5168:3502] diff --git a/Documentation/usb/et61x251.txt b/Documentation/video4linux/et61x251.txt similarity index 100% rename from Documentation/usb/et61x251.txt rename to Documentation/video4linux/et61x251.txt diff --git a/Documentation/usb/ibmcam.txt b/Documentation/video4linux/ibmcam.txt similarity index 99% rename from Documentation/usb/ibmcam.txt rename to Documentation/video4linux/ibmcam.txt index c25003644131..4a40a2e99451 100644 --- a/Documentation/usb/ibmcam.txt +++ b/Documentation/video4linux/ibmcam.txt @@ -122,7 +122,7 @@ WHAT YOU NEED: - A Linux box with USB support (2.3/2.4; 2.2 w/backport may work) - A Video4Linux compatible frame grabber program such as xawtv. - + HOW TO COMPILE THE DRIVER: You need to compile the driver only if you are a developer diff --git a/Documentation/usb/ov511.txt b/Documentation/video4linux/ov511.txt similarity index 99% rename from Documentation/usb/ov511.txt rename to Documentation/video4linux/ov511.txt index a7fc0432bff1..142741e3c578 100644 --- a/Documentation/usb/ov511.txt +++ b/Documentation/video4linux/ov511.txt @@ -9,7 +9,7 @@ INTRODUCTION: This is a driver for the OV511, a USB-only chip used in many "webcam" devices. Any camera using the OV511/OV511+ and the OV6620/OV7610/20/20AE should work. -Video capture devices that use the Philips SAA7111A decoder also work. It +Video capture devices that use the Philips SAA7111A decoder also work. It supports streaming and capture of color or monochrome video via the Video4Linux API. Most V4L apps are compatible with it. Most resolutions with a width and height that are a multiple of 8 are supported. @@ -52,15 +52,15 @@ from it: chmod 666 /dev/video chmod 666 /dev/video0 (if necessary) - + Now you are ready to run a video app! Both vidcat and xawtv work well for me at 640x480. - + [Using vidcat:] vidcat -s 640x480 -p c > test.jpg xview test.jpg - + [Using xawtv:] From the main xawtv directory: @@ -70,7 +70,7 @@ From the main xawtv directory: make make install -Now you should be able to run xawtv. Right click for the options dialog. +Now you should be able to run xawtv. Right click for the options dialog. MODULE PARAMETERS: @@ -286,4 +286,3 @@ Randy Dunlap, and others. Big thanks to them for their pioneering work on that and the USB stack. Thanks to Bret Wallach for getting camera reg IO, ISOC, and image capture working. Thanks to Orion Sky Lawlor, Kevin Moore, and Claudio Matsuoka for their work as well. - diff --git a/Documentation/usb/se401.txt b/Documentation/video4linux/se401.txt similarity index 100% rename from Documentation/usb/se401.txt rename to Documentation/video4linux/se401.txt diff --git a/Documentation/usb/sn9c102.txt b/Documentation/video4linux/sn9c102.txt similarity index 98% rename from Documentation/usb/sn9c102.txt rename to Documentation/video4linux/sn9c102.txt index b957beae5607..142920bc011f 100644 --- a/Documentation/usb/sn9c102.txt +++ b/Documentation/video4linux/sn9c102.txt @@ -174,7 +174,7 @@ Module parameters are listed below: ------------------------------------------------------------------------------- Name: video_nr Type: short array (min = 0, max = 64) -Syntax: <-1|n[,...]> +Syntax: <-1|n[,...]> Description: Specify V4L2 minor mode number: -1 = use next available n = use minor number n @@ -187,7 +187,7 @@ Default: -1 ------------------------------------------------------------------------------- Name: force_munmap Type: bool array (min = 0, max = 64) -Syntax: <0|1[,...]> +Syntax: <0|1[,...]> Description: Force the application to unmap previously mapped buffer memory before calling any VIDIOC_S_CROP or VIDIOC_S_FMT ioctl's. Not all the applications support this feature. This parameter is @@ -206,7 +206,7 @@ Default: 2 ------------------------------------------------------------------------------- Name: debug Type: ushort -Syntax: +Syntax: Description: Debugging information level, from 0 to 3: 0 = none (use carefully) 1 = critical errors @@ -267,7 +267,7 @@ The sysfs interface also provides the "frame_header" entry, which exports the frame header of the most recent requested and captured video frame. The header is always 18-bytes long and is appended to every video frame by the SN9C10x controllers. As an example, this additional information can be used by the user -application for implementing auto-exposure features via software. +application for implementing auto-exposure features via software. The following table describes the frame header: @@ -441,7 +441,7 @@ blue pixels in one video frame. Each pixel is associated with a 8-bit long value and is disposed in memory according to the pattern shown below: B[0] G[1] B[2] G[3] ... B[m-2] G[m-1] -G[m] R[m+1] G[m+2] R[m+2] ... G[2m-2] R[2m-1] +G[m] R[m+1] G[m+2] R[m+2] ... G[2m-2] R[2m-1] ... ... B[(n-1)(m-2)] G[(n-1)(m-1)] ... G[n(m-2)] R[n(m-1)] @@ -472,12 +472,12 @@ The pixel reference value is calculated as follows: The algorithm purely describes the conversion from compressed Bayer code used in the SN9C10x chips to uncompressed Bayer. Additional steps are required to convert this to a color image (i.e. a color interpolation algorithm). - + The following Huffman codes have been found: -0: +0 (relative to reference pixel value) +0: +0 (relative to reference pixel value) 100: +4 101: -4? -1110xxxx: set absolute value to xxxx.0000 +1110xxxx: set absolute value to xxxx.0000 1101: +11 1111: -11 11001: +20 diff --git a/Documentation/usb/stv680.txt b/Documentation/video4linux/stv680.txt similarity index 82% rename from Documentation/usb/stv680.txt rename to Documentation/video4linux/stv680.txt index 6448041e7a37..4f8946f32f51 100644 --- a/Documentation/usb/stv680.txt +++ b/Documentation/video4linux/stv680.txt @@ -5,15 +5,15 @@ Copyright, 2001, Kevin Sisson INTRODUCTION: -STMicroelectronics produces the STV0680B chip, which comes in two -types, -001 and -003. The -003 version allows the recording and downloading -of sound clips from the camera, and allows a flash attachment. Otherwise, -it uses the same commands as the -001 version. Both versions support a -variety of SDRAM sizes and sensors, allowing for a maximum of 26 VGA or 20 -CIF pictures. The STV0680 supports either a serial or a usb interface, and +STMicroelectronics produces the STV0680B chip, which comes in two +types, -001 and -003. The -003 version allows the recording and downloading +of sound clips from the camera, and allows a flash attachment. Otherwise, +it uses the same commands as the -001 version. Both versions support a +variety of SDRAM sizes and sensors, allowing for a maximum of 26 VGA or 20 +CIF pictures. The STV0680 supports either a serial or a usb interface, and video is possible through the usb interface. -The following cameras are known to work with this driver, although any +The following cameras are known to work with this driver, although any camera with Vendor/Product codes of 0553/0202 should work: Aiptek Pencam (various models) @@ -34,15 +34,15 @@ http://www.linux-usb.org MODULE OPTIONS: When the driver is compiled as a module, you can set a "swapRGB=1" -option, if necessary, for those applications that require it -(such as xawtv). However, the driver should detect and set this +option, if necessary, for those applications that require it +(such as xawtv). However, the driver should detect and set this automatically, so this option should not normally be used. KNOWN PROBLEMS: -The driver seems to work better with the usb-ohci than the usb-uhci host -controller driver. +The driver seems to work better with the usb-ohci than the usb-uhci host +controller driver. HELP: @@ -50,6 +50,4 @@ The latest info on this driver can be found at: http://personal.clt.bellsouth.net/~kjsisson or at http://stv0680-usb.sourceforge.net -Any questions to me can be send to: kjsisson@bellsouth.net - - +Any questions to me can be send to: kjsisson@bellsouth.net \ No newline at end of file diff --git a/Documentation/usb/w9968cf.txt b/Documentation/video4linux/w9968cf.txt similarity index 97% rename from Documentation/usb/w9968cf.txt rename to Documentation/video4linux/w9968cf.txt index 9d46cd0b19e3..3b704f2aae6d 100644 --- a/Documentation/usb/w9968cf.txt +++ b/Documentation/video4linux/w9968cf.txt @@ -1,5 +1,5 @@ - W996[87]CF JPEG USB Dual Mode Camera Chip + W996[87]CF JPEG USB Dual Mode Camera Chip Driver for Linux 2.6 (basic version) ========================================= @@ -115,7 +115,7 @@ additional testing and full support, would be much appreciated. ====================== For it to work properly, the driver needs kernel support for Video4Linux, USB and I2C, and the "ovcamchip" module for the image sensor. Make sure you are not -actually using any external "ovcamchip" module, given that the W996[87]CF +actually using any external "ovcamchip" module, given that the W996[87]CF driver depends on the version of the module present in the official kernels. The following options of the kernel configuration file must be enabled and @@ -197,16 +197,16 @@ Note: The kernel must be compiled with the CONFIG_KMOD option enabled for the 'ovcamchip' module to be loaded and for this parameter to be present. ------------------------------------------------------------------------------- -Name: simcams -Type: int -Syntax: +Name: simcams +Type: int +Syntax: Description: Number of cameras allowed to stream simultaneously. n may vary from 0 to 32. Default: 32 ------------------------------------------------------------------------------- Name: video_nr Type: int array (min = 0, max = 32) -Syntax: <-1|n[,...]> +Syntax: <-1|n[,...]> Description: Specify V4L minor mode number. -1 = use next available n = use minor number n @@ -219,7 +219,7 @@ Default: -1 ------------------------------------------------------------------------------- Name: packet_size Type: int array (min = 0, max = 32) -Syntax: +Syntax: Description: Specify the maximum data payload size in bytes for alternate settings, for each device. n is scaled between 63 and 1023. Default: 1023 @@ -234,7 +234,7 @@ Default: 2 ------------------------------------------------------------------------------- Name: double_buffer Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> +Syntax: <0|1[,...]> Description: Hardware double buffering: 0 disabled, 1 enabled. It should be enabled if you want smooth video output: if you obtain out of sync. video, disable it, or try to @@ -243,13 +243,13 @@ Default: 1 for every device. ------------------------------------------------------------------------------- Name: clamping Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> +Syntax: <0|1[,...]> Description: Video data clamping: 0 disabled, 1 enabled. Default: 0 for every device. ------------------------------------------------------------------------------- Name: filter_type Type: int array (min = 0, max = 32) -Syntax: <0|1|2[,...]> +Syntax: <0|1|2[,...]> Description: Video filter type. 0 none, 1 (1-2-1) 3-tap filter, 2 (2-3-6-3-2) 5-tap filter. The filter is used to reduce noise and aliasing artifacts @@ -258,13 +258,13 @@ Default: 0 for every device. ------------------------------------------------------------------------------- Name: largeview Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> +Syntax: <0|1[,...]> Description: Large view: 0 disabled, 1 enabled. Default: 1 for every device. ------------------------------------------------------------------------------- Name: upscaling Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> +Syntax: <0|1[,...]> Description: Software scaling (for non-compressed video only): 0 disabled, 1 enabled. Disable it if you have a slow CPU or you don't have enough @@ -341,8 +341,8 @@ Default: 50 for every device. ------------------------------------------------------------------------------- Name: bandingfilter Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> -Description: Banding filter to reduce effects of fluorescent +Syntax: <0|1[,...]> +Description: Banding filter to reduce effects of fluorescent lighting: 0 disabled, 1 enabled. This filter tries to reduce the pattern of horizontal @@ -374,7 +374,7 @@ Default: 0 for every device. ------------------------------------------------------------------------------- Name: monochrome Type: bool array (min = 0, max = 32) -Syntax: <0|1[,...]> +Syntax: <0|1[,...]> Description: The image sensor is monochrome: 0 = no, 1 = yes Default: 0 for every device. @@ -400,19 +400,19 @@ Default: 32768 for every device. ------------------------------------------------------------------------------- Name: contrast Type: long array (min = 0, max = 32) -Syntax: +Syntax: Description: Set picture contrast (0-65535). Default: 50000 for every device. ------------------------------------------------------------------------------- Name: whiteness Type: long array (min = 0, max = 32) -Syntax: +Syntax: Description: Set picture whiteness (0-65535). Default: 32768 for every device. ------------------------------------------------------------------------------- Name: debug Type: int -Syntax: +Syntax: Description: Debugging information level, from 0 to 6: 0 = none (use carefully) 1 = critical errors diff --git a/Documentation/usb/zc0301.txt b/Documentation/video4linux/zc0301.txt similarity index 100% rename from Documentation/usb/zc0301.txt rename to Documentation/video4linux/zc0301.txt diff --git a/Documentation/vm/hugetlbpage.txt b/Documentation/vm/hugetlbpage.txt index 1ad9af1ca4d0..2803f63c1a27 100644 --- a/Documentation/vm/hugetlbpage.txt +++ b/Documentation/vm/hugetlbpage.txt @@ -27,7 +27,7 @@ number of free hugetlb pages at any time. It also displays information about the configured hugepage size - this is needed for generating the proper alignment and size of the arguments to the above system calls. -The output of "cat /proc/meminfo" will have output like: +The output of "cat /proc/meminfo" will have lines like: ..... HugePages_Total: xxx @@ -42,11 +42,11 @@ pages in the kernel. Super user can dynamically request more (or free some pre-configured) hugepages. The allocation (or deallocation) of hugetlb pages is possible only if there are enough physically contiguous free pages in system (freeing of hugepages is -possible only if there are enough hugetlb pages free that can be transfered +possible only if there are enough hugetlb pages free that can be transferred back to regular memory pool). -Pages that are used as hugetlb pages are reserved inside the kernel and can -not be used for other purposes. +Pages that are used as hugetlb pages are reserved inside the kernel and cannot +be used for other purposes. Once the kernel with Hugetlb page support is built and running, a user can use either the mmap system call or shared memory system calls to start using @@ -60,7 +60,7 @@ Use the following command to dynamically allocate/deallocate hugepages: This command will try to configure 20 hugepages in the system. The success or failure of allocation depends on the amount of physically contiguous memory that is preset in system at this time. System administrators may want -to put this command in one of the local rc init file. This will enable the +to put this command in one of the local rc init files. This will enable the kernel to request huge pages early in the boot process (when the possibility of getting physical contiguous pages is still very high). @@ -78,8 +78,8 @@ the uid and gid of the current process are taken. The mode option sets the mode of root of file system to value & 0777. This value is given in octal. By default the value 0755 is picked. The size option sets the maximum value of memory (huge pages) allowed for that filesystem (/mnt/huge). The size is -rounded down to HPAGE_SIZE. The option nr_inode sets the maximum number of -inodes that /mnt/huge can use. If the size or nr_inode options are not +rounded down to HPAGE_SIZE. The option nr_inodes sets the maximum number of +inodes that /mnt/huge can use. If the size or nr_inodes options are not provided on command line then no limits are set. For size and nr_inodes options, you can use [G|g]/[M|m]/[K|k] to represent giga/mega/kilo. For example, size=2K has the same meaning as size=2048. An example is given at @@ -88,7 +88,7 @@ the end of this document. read and write system calls are not supported on files that reside on hugetlb file systems. -A regular chown, chgrp and chmod commands (with right permissions) could be +Regular chown, chgrp, and chmod commands (with right permissions) could be used to change the file attributes on hugetlbfs. Also, it is important to note that no such mount command is required if the @@ -96,8 +96,8 @@ applications are going to use only shmat/shmget system calls. Users who wish to use hugetlb page via shared memory segment should be a member of a supplementary group and system admin needs to configure that gid into /proc/sys/vm/hugetlb_shm_group. It is possible for same or different -applications to use any combination of mmaps and shm* calls. Though the -mount of filesystem will be required for using mmaps. +applications to use any combination of mmaps and shm* calls, though the +mount of filesystem will be required for using mmap calls. ******************************************************************* diff --git a/Documentation/x86_64/boot-options.txt b/Documentation/x86_64/boot-options.txt index 1921353259ae..f2cd6ef53ff3 100644 --- a/Documentation/x86_64/boot-options.txt +++ b/Documentation/x86_64/boot-options.txt @@ -151,6 +151,11 @@ NUMA numa=fake=X Fake X nodes and ignore NUMA setup of the actual machine. + numa=hotadd=percent + Only allow hotadd memory to preallocate page structures upto + percent of already available memory. + numa=hotadd=0 will disable hotadd memory. + ACPI acpi=off Don't enable ACPI diff --git a/Kbuild b/Kbuild index 95d6a00bace0..2d4f95e4b89f 100644 --- a/Kbuild +++ b/Kbuild @@ -18,7 +18,7 @@ define sed-y "/^->/{s:^->\([^ ]*\) [\$$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:; s:->::; p;}" endef # Override default regexp for specific architectures -sed-$(CONFIG_MIPS) := "/^@@@/s///p" +sed-$(CONFIG_MIPS) := "/^@@@/{s/^@@@//; s/ \#.*\$$//; p;}" quiet_cmd_offsets = GEN $@ define cmd_offsets diff --git a/MAINTAINERS b/MAINTAINERS index c9465811addc..d00dea52123f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1451,6 +1451,12 @@ P: Juanjo Ciarlante M: jjciarla@raiz.uncu.edu.ar S: Maintained +IPATH DRIVER: +P: Bryan O'Sullivan +M: support@pathscale.com +L: openib-general@openib.org +S: Supported + IPX NETWORK LAYER P: Arnaldo Carvalho de Melo M: acme@conectiva.com.br @@ -1550,9 +1556,7 @@ S: Maintained KEXEC P: Eric Biederman -P: Randy Dunlap M: ebiederm@xmission.com -M: rdunlap@xenotime.net W: http://www.xmission.com/~ebiederm/files/kexec/ L: linux-kernel@vger.kernel.org L: fastboot@osdl.org diff --git a/Makefile b/Makefile index af6210d48836..fc8e08c419f0 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 -SUBLEVEL = 16 -EXTRAVERSION = +SUBLEVEL = 17 +EXTRAVERSION =-rc1 NAME=Sliding Snow Leopard # *DOCUMENTATION* @@ -1112,7 +1112,6 @@ modules_install: _emodinst_ _emodinst_post install-dir := $(if $(INSTALL_MOD_DIR),$(INSTALL_MOD_DIR),extra) PHONY += _emodinst_ _emodinst_: - $(Q)rm -rf $(MODLIB)/$(install-dir) $(Q)mkdir -p $(MODLIB)/$(install-dir) $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modinst @@ -1275,40 +1274,43 @@ kernelversion: # Single targets # --------------------------------------------------------------------------- -# The directory part is taken from first prerequisite, so this -# works even with external modules -%.s: %.c prepare scripts FORCE - $(Q)$(MAKE) $(build)=$(dir $<) $(dir $<)$(notdir $@) -%.i: %.c prepare scripts FORCE - $(Q)$(MAKE) $(build)=$(dir $<) $(dir $<)$(notdir $@) -%.o: %.c prepare scripts FORCE - $(Q)$(MAKE) $(build)=$(dir $<) $(dir $<)$(notdir $@) -%.lst: %.c prepare scripts FORCE - $(Q)$(MAKE) $(build)=$(dir $<) $(dir $<)$(notdir $@) -%.s: %.S prepare scripts FORCE - $(Q)$(MAKE) $(build)=$(dir $<) $(dir $<)$(notdir $@) -%.o: %.S prepare scripts FORCE - $(Q)$(MAKE) $(build)=$(dir $<) $(dir $<)$(notdir $@) +# Single targets are compatible with: +# - build whith mixed source and output +# - build with separate output dir 'make O=...' +# - external modules +# +# target-dir => where to store outputfile +# build-dir => directory in kernel source tree to use -# For external modules we shall include any directory of the target, -# but usual case there is no directory part. -# make M=`pwd` module.o => $(dir $@)=./ -# make M=`pwd` foo/module.o => $(dir $@)=foo/ -# make M=`pwd` / => $(dir $@)=/ - ifeq ($(KBUILD_EXTMOD),) - target-dir = $(@D) + build-dir = $(patsubst %/,%,$(dir $@)) + target-dir = $(dir $@) else zap-slash=$(filter-out .,$(patsubst %/,%,$(dir $@))) - target-dir = $(KBUILD_EXTMOD)$(if $(zap-slash),/$(zap-slash)) + build-dir = $(KBUILD_EXTMOD)$(if $(zap-slash),/$(zap-slash)) + target-dir = $(if $(KBUILD_EXTMOD),$(dir $<),$(dir $@)) endif -/ %/: scripts prepare FORCE +%.s: %.c prepare scripts FORCE + $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) +%.i: %.c prepare scripts FORCE + $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) +%.o: %.c prepare scripts FORCE + $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) +%.lst: %.c prepare scripts FORCE + $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) +%.s: %.S prepare scripts FORCE + $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) +%.o: %.S prepare scripts FORCE + $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@) + +# Modules +/ %/: prepare scripts FORCE $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \ - $(build)=$(target-dir) -%.ko: scripts FORCE + $(build)=$(build-dir) +%.ko: prepare scripts FORCE $(Q)$(MAKE) KBUILD_MODULES=$(if $(CONFIG_MODULES),1) \ - $(build)=$(target-dir) $(@:.ko=.o) + $(build)=$(build-dir) $(@:.ko=.o) $(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modpost # FIXME Should go into a make.lib or something diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index 9bef61b30367..8290b69da202 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -549,6 +549,11 @@ config NUMA Access). This option is for configuring high-end multiprocessor server machines. If in doubt, say N. +config NODES_SHIFT + int + default "7" + depends on NEED_MULTIPLE_NODES + # LARGE_VMALLOC is racy, if you *really* need it then fix it first config ALPHA_LARGE_VMALLOC bool diff --git a/arch/alpha/kernel/alpha_ksyms.c b/arch/alpha/kernel/alpha_ksyms.c index 1898ea79d0e2..c645c5e14786 100644 --- a/arch/alpha/kernel/alpha_ksyms.c +++ b/arch/alpha/kernel/alpha_ksyms.c @@ -76,7 +76,6 @@ EXPORT_SYMBOL(strncpy); EXPORT_SYMBOL(strnlen); EXPORT_SYMBOL(strncat); EXPORT_SYMBOL(strstr); -EXPORT_SYMBOL(strpbrk); EXPORT_SYMBOL(strchr); EXPORT_SYMBOL(strrchr); EXPORT_SYMBOL(memcmp); @@ -216,8 +215,6 @@ EXPORT_SYMBOL(memcpy); EXPORT_SYMBOL(memset); EXPORT_SYMBOL(memchr); -EXPORT_SYMBOL(get_wchan); - #ifdef CONFIG_ALPHA_IRONGATE EXPORT_SYMBOL(irongate_ioremap); EXPORT_SYMBOL(irongate_iounmap); diff --git a/arch/alpha/kernel/core_marvel.c b/arch/alpha/kernel/core_marvel.c index 44866cb26a80..7f6a98455e74 100644 --- a/arch/alpha/kernel/core_marvel.c +++ b/arch/alpha/kernel/core_marvel.c @@ -435,7 +435,7 @@ marvel_specify_io7(char *str) str = pchar; } while(*str); - return 0; + return 1; } __setup("io7=", marvel_specify_io7); diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c index dd8769670596..558b83368559 100644 --- a/arch/alpha/kernel/setup.c +++ b/arch/alpha/kernel/setup.c @@ -24,10 +24,12 @@ #include /* CONFIG_ALPHA_LCA etc */ #include #include +#include #include #include #include #include +#include #include #include #include @@ -470,6 +472,22 @@ page_is_ram(unsigned long pfn) return 0; } +static int __init +register_cpus(void) +{ + int i; + + for_each_possible_cpu(i) { + struct cpu *p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; + register_cpu(p, i, NULL); + } + return 0; +} + +arch_initcall(register_cpus); + void __init setup_arch(char **cmdline_p) { @@ -1478,3 +1496,20 @@ alpha_panic_event(struct notifier_block *this, unsigned long event, void *ptr) #endif return NOTIFY_DONE; } + +static __init int add_pcspkr(void) +{ + struct platform_device *pd; + int ret; + + pd = platform_device_alloc("pcspkr", -1); + if (!pd) + return -ENOMEM; + + ret = platform_device_add(pd); + if (ret) + platform_device_put(pd); + + return ret; +} +device_initcall(add_pcspkr); diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c index 02c2db08114a..185255416e85 100644 --- a/arch/alpha/kernel/smp.c +++ b/arch/alpha/kernel/smp.c @@ -439,7 +439,7 @@ setup_smp(void) if ((cpu->flags & 0x1cc) == 0x1cc) { smp_num_probed++; /* Assume here that "whami" == index */ - cpu_set(i, cpu_possible_map); + cpu_set(i, cpu_present_mask); cpu->pal_revision = boot_cpu_palrev; } @@ -450,9 +450,8 @@ setup_smp(void) } } else { smp_num_probed = 1; - cpu_set(boot_cpuid, cpu_possible_map); + cpu_set(boot_cpuid, cpu_present_mask); } - cpu_present_mask = cpumask_of_cpu(boot_cpuid); printk(KERN_INFO "SMP: %d CPUs probed -- cpu_present_mask = %lx\n", smp_num_probed, cpu_possible_map.bits[0]); @@ -488,9 +487,8 @@ void __devinit smp_prepare_boot_cpu(void) { /* - * Mark the boot cpu (current cpu) as both present and online + * Mark the boot cpu (current cpu) as online */ - cpu_set(smp_processor_id(), cpu_present_mask); cpu_set(smp_processor_id(), cpu_online_map); } diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index ba46d779ede7..1dbf6ddb300d 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -77,6 +77,14 @@ config FIQ config ARCH_MTD_XIP bool +config VECTORS_BASE + hex + default 0xffff0000 if MMU + default DRAM_BASE if REMAP_VECTORS_TO_RAM + default 0x00000000 + help + The base address of exception vectors. + source "init/Kconfig" menu "System Type" @@ -504,6 +512,12 @@ config ARCH_DISCONTIGMEM_ENABLE or have huge holes in the physical address space for other reasons. See for more. +config NODES_SHIFT + int + default "4" if ARCH_LH7A40X + default "2" + depends on NEED_MULTIPLE_NODES + source "mm/Kconfig" config LEDS @@ -839,6 +853,8 @@ source "drivers/misc/Kconfig" source "drivers/mfd/Kconfig" +source "drivers/leds/Kconfig" + source "drivers/media/Kconfig" source "drivers/video/Kconfig" diff --git a/arch/arm/Kconfig-nommu b/arch/arm/Kconfig-nommu new file mode 100644 index 000000000000..e1574be2ded6 --- /dev/null +++ b/arch/arm/Kconfig-nommu @@ -0,0 +1,44 @@ +# +# Kconfig for uClinux(non-paged MM) depend configurations +# Hyok S. Choi +# + +config SET_MEM_PARAM + bool "Set flash/sdram size and base addr" + help + Say Y to manually set the base addresses and sizes. + otherwise, the default values are assigned. + +config DRAM_BASE + hex '(S)DRAM Base Address' if SET_MEM_PARAM + default 0x00800000 + +config DRAM_SIZE + hex '(S)DRAM SIZE' if SET_MEM_PARAM + default 0x00800000 + +config FLASH_MEM_BASE + hex 'FLASH Base Address' if SET_MEM_PARAM + default 0x00400000 + +config FLASH_SIZE + hex 'FLASH Size' if SET_MEM_PARAM + default 0x00400000 + +config REMAP_VECTORS_TO_RAM + bool 'Install vectors to the begining of RAM' if DRAM_BASE + depends on DRAM_BASE + help + The kernel needs to change the hardware exception vectors. + In nommu mode, the hardware exception vectors are normally + placed at address 0x00000000. However, this region may be + occupied by read-only memory depending on H/W design. + + If the region contains read-write memory, say 'n' here. + + If your CPU provides a remap facility which allows the exception + vectors to be mapped to writable memory, say 'n' here. + + Otherwise, say 'y' here. In this case, the kernel will require + external support to redirect the hardware exception vectors to + the writable versions located at DRAM_BASE. diff --git a/arch/arm/Makefile b/arch/arm/Makefile index ce3e804ea0f3..95a96275f88a 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -20,6 +20,11 @@ GZFLAGS :=-9 # Select a platform tht is kept up-to-date KBUILD_DEFCONFIG := versatile_defconfig +# defines filename extension depending memory manement type. +ifeq ($(CONFIG_MMU),) +MMUEXT := -nommu +endif + ifeq ($(CONFIG_FRAME_POINTER),y) CFLAGS +=-fno-omit-frame-pointer -mapcs -mno-sched-prolog endif @@ -73,7 +78,7 @@ AFLAGS +=$(CFLAGS_ABI) $(arch-y) $(tune-y) -msoft-float CHECKFLAGS += -D__arm__ #Default value -head-y := arch/arm/kernel/head.o arch/arm/kernel/init_task.o +head-y := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o textofs-y := 0x00008000 machine-$(CONFIG_ARCH_RPC) := rpc @@ -133,7 +138,7 @@ else MACHINE := endif -export TEXT_OFFSET GZFLAGS +export TEXT_OFFSET GZFLAGS MMUEXT # Do we have FASTFPE? FASTFPE :=arch/arm/fastfpe diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S index 491c7e4c9ac6..b56f5e691d65 100644 --- a/arch/arm/boot/compressed/head.S +++ b/arch/arm/boot/compressed/head.S @@ -2,6 +2,7 @@ * linux/arch/arm/boot/compressed/head.S * * Copyright (C) 1996-2002 Russell King + * Copyright (C) 2004 Hyok S. Choi (MPU support) * * 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 @@ -320,6 +321,62 @@ params: ldr r0, =params_phys cache_on: mov r3, #8 @ cache_on function b call_cache_fn +/* + * Initialize the highest priority protection region, PR7 + * to cover all 32bit address and cacheable and bufferable. + */ +__armv4_mpu_cache_on: + mov r0, #0x3f @ 4G, the whole + mcr p15, 0, r0, c6, c7, 0 @ PR7 Area Setting + mcr p15, 0, r0, c6, c7, 1 + + mov r0, #0x80 @ PR7 + mcr p15, 0, r0, c2, c0, 0 @ D-cache on + mcr p15, 0, r0, c2, c0, 1 @ I-cache on + mcr p15, 0, r0, c3, c0, 0 @ write-buffer on + + mov r0, #0xc000 + mcr p15, 0, r0, c5, c0, 1 @ I-access permission + mcr p15, 0, r0, c5, c0, 0 @ D-access permission + + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 @ drain write buffer + mcr p15, 0, r0, c7, c5, 0 @ flush(inval) I-Cache + mcr p15, 0, r0, c7, c6, 0 @ flush(inval) D-Cache + mrc p15, 0, r0, c1, c0, 0 @ read control reg + @ ...I .... ..D. WC.M + orr r0, r0, #0x002d @ .... .... ..1. 11.1 + orr r0, r0, #0x1000 @ ...1 .... .... .... + + mcr p15, 0, r0, c1, c0, 0 @ write control reg + + mov r0, #0 + mcr p15, 0, r0, c7, c5, 0 @ flush(inval) I-Cache + mcr p15, 0, r0, c7, c6, 0 @ flush(inval) D-Cache + mov pc, lr + +__armv3_mpu_cache_on: + mov r0, #0x3f @ 4G, the whole + mcr p15, 0, r0, c6, c7, 0 @ PR7 Area Setting + + mov r0, #0x80 @ PR7 + mcr p15, 0, r0, c2, c0, 0 @ cache on + mcr p15, 0, r0, c3, c0, 0 @ write-buffer on + + mov r0, #0xc000 + mcr p15, 0, r0, c5, c0, 0 @ access permission + + mov r0, #0 + mcr p15, 0, r0, c7, c0, 0 @ invalidate whole cache v3 + mrc p15, 0, r0, c1, c0, 0 @ read control reg + @ .... .... .... WC.M + orr r0, r0, #0x000d @ .... .... .... 11.1 + mov r0, #0 + mcr p15, 0, r0, c1, c0, 0 @ write control reg + + mcr p15, 0, r0, c7, c0, 0 @ invalidate whole cache v3 + mov pc, lr + __setup_mmu: sub r3, r4, #16384 @ Page directory size bic r3, r3, #0xff @ Align the pointer bic r3, r3, #0x3f00 @@ -496,6 +553,18 @@ proc_types: b __armv4_mmu_cache_off mov pc, lr + .word 0x41007400 @ ARM74x + .word 0xff00ff00 + b __armv3_mpu_cache_on + b __armv3_mpu_cache_off + b __armv3_mpu_cache_flush + + .word 0x41009400 @ ARM94x + .word 0xff00ff00 + b __armv4_mpu_cache_on + b __armv4_mpu_cache_off + b __armv4_mpu_cache_flush + .word 0x00007000 @ ARM7 IDs .word 0x0000f000 mov pc, lr @@ -562,6 +631,24 @@ proc_types: cache_off: mov r3, #12 @ cache_off function b call_cache_fn +__armv4_mpu_cache_off: + mrc p15, 0, r0, c1, c0 + bic r0, r0, #0x000d + mcr p15, 0, r0, c1, c0 @ turn MPU and cache off + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 @ drain write buffer + mcr p15, 0, r0, c7, c6, 0 @ flush D-Cache + mcr p15, 0, r0, c7, c5, 0 @ flush I-Cache + mov pc, lr + +__armv3_mpu_cache_off: + mrc p15, 0, r0, c1, c0 + bic r0, r0, #0x000d + mcr p15, 0, r0, c1, c0, 0 @ turn MPU and cache off + mov r0, #0 + mcr p15, 0, r0, c7, c0, 0 @ invalidate whole cache v3 + mov pc, lr + __armv4_mmu_cache_off: mrc p15, 0, r0, c1, c0 bic r0, r0, #0x000d @@ -601,6 +688,24 @@ cache_clean_flush: mov r3, #16 b call_cache_fn +__armv4_mpu_cache_flush: + mov r2, #1 + mov r3, #0 + mcr p15, 0, ip, c7, c6, 0 @ invalidate D cache + mov r1, #7 << 5 @ 8 segments +1: orr r3, r1, #63 << 26 @ 64 entries +2: mcr p15, 0, r3, c7, c14, 2 @ clean & invalidate D index + subs r3, r3, #1 << 26 + bcs 2b @ entries 63 to 0 + subs r1, r1, #1 << 5 + bcs 1b @ segments 7 to 0 + + teq r2, #0 + mcrne p15, 0, ip, c7, c5, 0 @ invalidate I cache + mcr p15, 0, ip, c7, c10, 4 @ drain WB + mov pc, lr + + __armv6_mmu_cache_flush: mov r1, #0 mcr p15, 0, r1, c7, c14, 0 @ clean+invalidate D @@ -638,6 +743,7 @@ no_cache_id: mov pc, lr __armv3_mmu_cache_flush: +__armv3_mpu_cache_flush: mov r1, #0 mcr p15, 0, r0, c7, c0, 0 @ invalidate whole cache v3 mov pc, lr diff --git a/arch/arm/boot/compressed/vmlinux.lds.in b/arch/arm/boot/compressed/vmlinux.lds.in index eed616113e47..153a07e7222b 100644 --- a/arch/arm/boot/compressed/vmlinux.lds.in +++ b/arch/arm/boot/compressed/vmlinux.lds.in @@ -18,6 +18,7 @@ SECTIONS _start = .; *(.start) *(.text) + *(.text.*) *(.fixup) *(.gnu.warning) *(.rodata) diff --git a/arch/arm/common/sharpsl_pm.c b/arch/arm/common/sharpsl_pm.c index 978d32e82d39..3cd8c9ee4510 100644 --- a/arch/arm/common/sharpsl_pm.c +++ b/arch/arm/common/sharpsl_pm.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -75,6 +76,7 @@ static void sharpsl_battery_thread(void *private_); struct sharpsl_pm_status sharpsl_pm; DECLARE_WORK(toggle_charger, sharpsl_charge_toggle, NULL); DECLARE_WORK(sharpsl_bat, sharpsl_battery_thread, NULL); +DEFINE_LED_TRIGGER(sharpsl_charge_led_trigger); static int get_percentage(int voltage) @@ -190,10 +192,10 @@ void sharpsl_pm_led(int val) dev_err(sharpsl_pm.dev, "Charging Error!\n"); } else if (val == SHARPSL_LED_ON) { dev_dbg(sharpsl_pm.dev, "Charge LED On\n"); - + led_trigger_event(sharpsl_charge_led_trigger, LED_FULL); } else { dev_dbg(sharpsl_pm.dev, "Charge LED Off\n"); - + led_trigger_event(sharpsl_charge_led_trigger, LED_OFF); } } @@ -786,6 +788,8 @@ static int __init sharpsl_pm_probe(struct platform_device *pdev) init_timer(&sharpsl_pm.chrg_full_timer); sharpsl_pm.chrg_full_timer.function = sharpsl_chrg_full_timer; + led_trigger_register_simple("sharpsl-charge", &sharpsl_charge_led_trigger); + sharpsl_pm.machinfo->init(); device_create_file(&pdev->dev, &dev_attr_battery_percentage); @@ -807,6 +811,8 @@ static int sharpsl_pm_remove(struct platform_device *pdev) device_remove_file(&pdev->dev, &dev_attr_battery_percentage); device_remove_file(&pdev->dev, &dev_attr_battery_voltage); + led_trigger_unregister_simple(sharpsl_charge_led_trigger); + sharpsl_pm.machinfo->exit(); del_timer_sync(&sharpsl_pm.chrg_full_timer); diff --git a/arch/arm/configs/at91rm9200dk_defconfig b/arch/arm/configs/at91rm9200dk_defconfig index 1fe73d198888..9e1c1cceb735 100644 --- a/arch/arm/configs/at91rm9200dk_defconfig +++ b/arch/arm/configs/at91rm9200dk_defconfig @@ -379,7 +379,7 @@ CONFIG_MTD_PHYSMAP_BANKWIDTH=2 # CONFIG_MTD_DOC2001 is not set # CONFIG_MTD_DOC2001PLUS is not set CONFIG_MTD_AT91_DATAFLASH=y -CONFIG_MTD_AT91_DATAFLASH_CARD=y +# CONFIG_MTD_AT91_DATAFLASH_CARD is not set # # NAND Flash Device Drivers diff --git a/arch/arm/configs/at91rm9200ek_defconfig b/arch/arm/configs/at91rm9200ek_defconfig index b7d934cdb1b7..6e0805a971d7 100644 --- a/arch/arm/configs/at91rm9200ek_defconfig +++ b/arch/arm/configs/at91rm9200ek_defconfig @@ -370,7 +370,7 @@ CONFIG_MTD_PHYSMAP_BANKWIDTH=2 # CONFIG_MTD_DOC2001 is not set # CONFIG_MTD_DOC2001PLUS is not set CONFIG_MTD_AT91_DATAFLASH=y -CONFIG_MTD_AT91_DATAFLASH_CARD=y +# CONFIG_MTD_AT91_DATAFLASH_CARD is not set # # NAND Flash Device Drivers diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c index 1574941ebfe1..c49b5d4d7fca 100644 --- a/arch/arm/kernel/armksyms.c +++ b/arch/arm/kernel/armksyms.c @@ -100,23 +100,11 @@ EXPORT_SYMBOL(__raw_writesl); #endif /* string / mem functions */ -EXPORT_SYMBOL(strcpy); -EXPORT_SYMBOL(strncpy); -EXPORT_SYMBOL(strcat); -EXPORT_SYMBOL(strncat); -EXPORT_SYMBOL(strcmp); -EXPORT_SYMBOL(strncmp); EXPORT_SYMBOL(strchr); -EXPORT_SYMBOL(strlen); -EXPORT_SYMBOL(strnlen); -EXPORT_SYMBOL(strpbrk); EXPORT_SYMBOL(strrchr); -EXPORT_SYMBOL(strstr); EXPORT_SYMBOL(memset); EXPORT_SYMBOL(memcpy); EXPORT_SYMBOL(memmove); -EXPORT_SYMBOL(memcmp); -EXPORT_SYMBOL(memscan); EXPORT_SYMBOL(memchr); EXPORT_SYMBOL(__memzero); @@ -190,8 +178,6 @@ EXPORT_SYMBOL(_find_next_bit_be); /* syscalls */ EXPORT_SYMBOL(sys_write); -EXPORT_SYMBOL(sys_read); EXPORT_SYMBOL(sys_lseek); -EXPORT_SYMBOL(sys_open); EXPORT_SYMBOL(sys_exit); EXPORT_SYMBOL(sys_wait4); diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index 355914ffb192..ab8e600c18c8 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S @@ -666,7 +666,7 @@ __kuser_helper_start: * * #define __kernel_dmb() \ * asm volatile ( "mov r0, #0xffff0fff; mov lr, pc; sub pc, r0, #95" \ - * : : : "lr","cc" ) + * : : : "r0", "lr","cc" ) */ __kuser_memory_barrier: @ 0xffff0fa0 diff --git a/arch/arm/kernel/head-common.S b/arch/arm/kernel/head-common.S new file mode 100644 index 000000000000..a52da0ddb43d --- /dev/null +++ b/arch/arm/kernel/head-common.S @@ -0,0 +1,217 @@ +/* + * linux/arch/arm/kernel/head-common.S + * + * Copyright (C) 1994-2002 Russell King + * Copyright (c) 2003 ARM Limited + * 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 version 2 as + * published by the Free Software Foundation. + * + */ + + .type __switch_data, %object +__switch_data: + .long __mmap_switched + .long __data_loc @ r4 + .long __data_start @ r5 + .long __bss_start @ r6 + .long _end @ r7 + .long processor_id @ r4 + .long __machine_arch_type @ r5 + .long cr_alignment @ r6 + .long init_thread_union + THREAD_START_SP @ sp + +/* + * The following fragment of code is executed with the MMU on in MMU mode, + * and uses absolute addresses; this is not position independent. + * + * r0 = cp#15 control register + * r1 = machine ID + * r9 = processor ID + */ + .type __mmap_switched, %function +__mmap_switched: + adr r3, __switch_data + 4 + + ldmia r3!, {r4, r5, r6, r7} + cmp r4, r5 @ Copy data segment if needed +1: cmpne r5, r6 + ldrne fp, [r4], #4 + strne fp, [r5], #4 + bne 1b + + mov fp, #0 @ Clear BSS (and zero fp) +1: cmp r6, r7 + strcc fp, [r6],#4 + bcc 1b + + ldmia r3, {r4, r5, r6, sp} + str r9, [r4] @ Save processor ID + str r1, [r5] @ Save machine type + bic r4, r0, #CR_A @ Clear 'A' bit + stmia r6, {r0, r4} @ Save control register values + b start_kernel + +/* + * Exception handling. Something went wrong and we can't proceed. We + * ought to tell the user, but since we don't have any guarantee that + * we're even running on the right architecture, we do virtually nothing. + * + * If CONFIG_DEBUG_LL is set we try to print out something about the error + * and hope for the best (useful if bootloader fails to pass a proper + * machine ID for example). + */ + + .type __error_p, %function +__error_p: +#ifdef CONFIG_DEBUG_LL + adr r0, str_p1 + bl printascii + b __error +str_p1: .asciz "\nError: unrecognized/unsupported processor variant.\n" + .align +#endif + + .type __error_a, %function +__error_a: +#ifdef CONFIG_DEBUG_LL + mov r4, r1 @ preserve machine ID + adr r0, str_a1 + bl printascii + mov r0, r4 + bl printhex8 + adr r0, str_a2 + bl printascii + adr r3, 3f + ldmia r3, {r4, r5, r6} @ get machine desc list + sub r4, r3, r4 @ get offset between virt&phys + add r5, r5, r4 @ convert virt addresses to + add r6, r6, r4 @ physical address space +1: ldr r0, [r5, #MACHINFO_TYPE] @ get machine type + bl printhex8 + mov r0, #'\t' + bl printch + ldr r0, [r5, #MACHINFO_NAME] @ get machine name + add r0, r0, r4 + bl printascii + mov r0, #'\n' + bl printch + add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc + cmp r5, r6 + blo 1b + adr r0, str_a3 + bl printascii + b __error +str_a1: .asciz "\nError: unrecognized/unsupported machine ID (r1 = 0x" +str_a2: .asciz ").\n\nAvailable machine support:\n\nID (hex)\tNAME\n" +str_a3: .asciz "\nPlease check your kernel config and/or bootloader.\n" + .align +#endif + + .type __error, %function +__error: +#ifdef CONFIG_ARCH_RPC +/* + * Turn the screen red on a error - RiscPC only. + */ + mov r0, #0x02000000 + mov r3, #0x11 + orr r3, r3, r3, lsl #8 + orr r3, r3, r3, lsl #16 + str r3, [r0], #4 + str r3, [r0], #4 + str r3, [r0], #4 + str r3, [r0], #4 +#endif +1: mov r0, r0 + b 1b + + +/* + * Read processor ID register (CP#15, CR0), and look up in the linker-built + * supported processor list. Note that we can't use the absolute addresses + * for the __proc_info lists since we aren't running with the MMU on + * (and therefore, we are not in the correct address space). We have to + * calculate the offset. + * + * r9 = cpuid + * Returns: + * r3, r4, r6 corrupted + * r5 = proc_info pointer in physical address space + * r9 = cpuid (preserved) + */ + .type __lookup_processor_type, %function +__lookup_processor_type: + adr r3, 3f + ldmda r3, {r5 - r7} + sub r3, r3, r7 @ get offset between virt&phys + add r5, r5, r3 @ convert virt addresses to + add r6, r6, r3 @ physical address space +1: ldmia r5, {r3, r4} @ value, mask + and r4, r4, r9 @ mask wanted bits + teq r3, r4 + beq 2f + add r5, r5, #PROC_INFO_SZ @ sizeof(proc_info_list) + cmp r5, r6 + blo 1b + mov r5, #0 @ unknown processor +2: mov pc, lr + +/* + * This provides a C-API version of the above function. + */ +ENTRY(lookup_processor_type) + stmfd sp!, {r4 - r7, r9, lr} + mov r9, r0 + bl __lookup_processor_type + mov r0, r5 + ldmfd sp!, {r4 - r7, r9, pc} + +/* + * Look in include/asm-arm/procinfo.h and arch/arm/kernel/arch.[ch] for + * more information about the __proc_info and __arch_info structures. + */ + .long __proc_info_begin + .long __proc_info_end +3: .long . + .long __arch_info_begin + .long __arch_info_end + +/* + * Lookup machine architecture in the linker-build list of architectures. + * Note that we can't use the absolute addresses for the __arch_info + * lists since we aren't running with the MMU on (and therefore, we are + * not in the correct address space). We have to calculate the offset. + * + * r1 = machine architecture number + * Returns: + * r3, r4, r6 corrupted + * r5 = mach_info pointer in physical address space + */ + .type __lookup_machine_type, %function +__lookup_machine_type: + adr r3, 3b + ldmia r3, {r4, r5, r6} + sub r3, r3, r4 @ get offset between virt&phys + add r5, r5, r3 @ convert virt addresses to + add r6, r6, r3 @ physical address space +1: ldr r3, [r5, #MACHINFO_TYPE] @ get machine type + teq r3, r1 @ matches loader number? + beq 2f @ found + add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc + cmp r5, r6 + blo 1b + mov r5, #0 @ unknown machine +2: mov pc, lr + +/* + * This provides a C-API version of the above function. + */ +ENTRY(lookup_machine_type) + stmfd sp!, {r4 - r6, lr} + mov r1, r0 + bl __lookup_machine_type + mov r0, r5 + ldmfd sp!, {r4 - r6, pc} diff --git a/arch/arm/kernel/head-nommu.S b/arch/arm/kernel/head-nommu.S new file mode 100644 index 000000000000..b093ab8738b5 --- /dev/null +++ b/arch/arm/kernel/head-nommu.S @@ -0,0 +1,83 @@ +/* + * linux/arch/arm/kernel/head-nommu.S + * + * Copyright (C) 1994-2002 Russell King + * Copyright (C) 2003-2006 Hyok S. Choi + * + * 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. + * + * Common kernel startup code (non-paged MM) + * for 32-bit CPUs which has a process ID register(CP15). + * + */ +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define PROCINFO_INITFUNC 12 + +/* + * Kernel startup entry point. + * --------------------------- + * + * This is normally called from the decompressor code. The requirements + * are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0, + * r1 = machine nr. + * + * See linux/arch/arm/tools/mach-types for the complete list of machine + * numbers for r1. + * + */ + __INIT + .type stext, %function +ENTRY(stext) + msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | MODE_SVC @ ensure svc mode + @ and irqs disabled + mrc p15, 0, r9, c0, c0 @ get processor id + bl __lookup_processor_type @ r5=procinfo r9=cpuid + movs r10, r5 @ invalid processor (r5=0)? + beq __error_p @ yes, error 'p' + bl __lookup_machine_type @ r5=machinfo + movs r8, r5 @ invalid machine (r5=0)? + beq __error_a @ yes, error 'a' + + ldr r13, __switch_data @ address to jump to after + @ the initialization is done + adr lr, __after_proc_init @ return (PIC) address + add pc, r10, #PROCINFO_INITFUNC + +/* + * Set the Control Register and Read the process ID. + */ + .type __after_proc_init, %function +__after_proc_init: + mrc p15, 0, r0, c1, c0, 0 @ read control reg +#ifdef CONFIG_ALIGNMENT_TRAP + orr r0, r0, #CR_A +#else + bic r0, r0, #CR_A +#endif +#ifdef CONFIG_CPU_DCACHE_DISABLE + bic r0, r0, #CR_C +#endif +#ifdef CONFIG_CPU_BPREDICT_DISABLE + bic r0, r0, #CR_Z +#endif +#ifdef CONFIG_CPU_ICACHE_DISABLE + bic r0, r0, #CR_I +#endif + mcr p15, 0, r0, c1, c0, 0 @ write control reg + + mov pc, r13 @ clear the BSS and jump + @ to start_kernel + +#include "head-common.S" diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S index 53b6901f70a6..04b66a9328ef 100644 --- a/arch/arm/kernel/head.S +++ b/arch/arm/kernel/head.S @@ -102,49 +102,6 @@ ENTRY(stext) adr lr, __enable_mmu @ return (PIC) address add pc, r10, #PROCINFO_INITFUNC - .type __switch_data, %object -__switch_data: - .long __mmap_switched - .long __data_loc @ r4 - .long __data_start @ r5 - .long __bss_start @ r6 - .long _end @ r7 - .long processor_id @ r4 - .long __machine_arch_type @ r5 - .long cr_alignment @ r6 - .long init_thread_union + THREAD_START_SP @ sp - -/* - * The following fragment of code is executed with the MMU on, and uses - * absolute addresses; this is not position independent. - * - * r0 = cp#15 control register - * r1 = machine ID - * r9 = processor ID - */ - .type __mmap_switched, %function -__mmap_switched: - adr r3, __switch_data + 4 - - ldmia r3!, {r4, r5, r6, r7} - cmp r4, r5 @ Copy data segment if needed -1: cmpne r5, r6 - ldrne fp, [r4], #4 - strne fp, [r5], #4 - bne 1b - - mov fp, #0 @ Clear BSS (and zero fp) -1: cmp r6, r7 - strcc fp, [r6],#4 - bcc 1b - - ldmia r3, {r4, r5, r6, sp} - str r9, [r4] @ Save processor ID - str r1, [r5] @ Save machine type - bic r4, r0, #CR_A @ Clear 'A' bit - stmia r6, {r0, r4} @ Save control register values - b start_kernel - #if defined(CONFIG_SMP) .type secondary_startup, #function ENTRY(secondary_startup) @@ -367,166 +324,4 @@ __create_page_tables: mov pc, lr .ltorg - - -/* - * Exception handling. Something went wrong and we can't proceed. We - * ought to tell the user, but since we don't have any guarantee that - * we're even running on the right architecture, we do virtually nothing. - * - * If CONFIG_DEBUG_LL is set we try to print out something about the error - * and hope for the best (useful if bootloader fails to pass a proper - * machine ID for example). - */ - - .type __error_p, %function -__error_p: -#ifdef CONFIG_DEBUG_LL - adr r0, str_p1 - bl printascii - b __error -str_p1: .asciz "\nError: unrecognized/unsupported processor variant.\n" - .align -#endif - - .type __error_a, %function -__error_a: -#ifdef CONFIG_DEBUG_LL - mov r4, r1 @ preserve machine ID - adr r0, str_a1 - bl printascii - mov r0, r4 - bl printhex8 - adr r0, str_a2 - bl printascii - adr r3, 3f - ldmia r3, {r4, r5, r6} @ get machine desc list - sub r4, r3, r4 @ get offset between virt&phys - add r5, r5, r4 @ convert virt addresses to - add r6, r6, r4 @ physical address space -1: ldr r0, [r5, #MACHINFO_TYPE] @ get machine type - bl printhex8 - mov r0, #'\t' - bl printch - ldr r0, [r5, #MACHINFO_NAME] @ get machine name - add r0, r0, r4 - bl printascii - mov r0, #'\n' - bl printch - add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc - cmp r5, r6 - blo 1b - adr r0, str_a3 - bl printascii - b __error -str_a1: .asciz "\nError: unrecognized/unsupported machine ID (r1 = 0x" -str_a2: .asciz ").\n\nAvailable machine support:\n\nID (hex)\tNAME\n" -str_a3: .asciz "\nPlease check your kernel config and/or bootloader.\n" - .align -#endif - - .type __error, %function -__error: -#ifdef CONFIG_ARCH_RPC -/* - * Turn the screen red on a error - RiscPC only. - */ - mov r0, #0x02000000 - mov r3, #0x11 - orr r3, r3, r3, lsl #8 - orr r3, r3, r3, lsl #16 - str r3, [r0], #4 - str r3, [r0], #4 - str r3, [r0], #4 - str r3, [r0], #4 -#endif -1: mov r0, r0 - b 1b - - -/* - * Read processor ID register (CP#15, CR0), and look up in the linker-built - * supported processor list. Note that we can't use the absolute addresses - * for the __proc_info lists since we aren't running with the MMU on - * (and therefore, we are not in the correct address space). We have to - * calculate the offset. - * - * r9 = cpuid - * Returns: - * r3, r4, r6 corrupted - * r5 = proc_info pointer in physical address space - * r9 = cpuid (preserved) - */ - .type __lookup_processor_type, %function -__lookup_processor_type: - adr r3, 3f - ldmda r3, {r5 - r7} - sub r3, r3, r7 @ get offset between virt&phys - add r5, r5, r3 @ convert virt addresses to - add r6, r6, r3 @ physical address space -1: ldmia r5, {r3, r4} @ value, mask - and r4, r4, r9 @ mask wanted bits - teq r3, r4 - beq 2f - add r5, r5, #PROC_INFO_SZ @ sizeof(proc_info_list) - cmp r5, r6 - blo 1b - mov r5, #0 @ unknown processor -2: mov pc, lr - -/* - * This provides a C-API version of the above function. - */ -ENTRY(lookup_processor_type) - stmfd sp!, {r4 - r7, r9, lr} - mov r9, r0 - bl __lookup_processor_type - mov r0, r5 - ldmfd sp!, {r4 - r7, r9, pc} - -/* - * Look in include/asm-arm/procinfo.h and arch/arm/kernel/arch.[ch] for - * more information about the __proc_info and __arch_info structures. - */ - .long __proc_info_begin - .long __proc_info_end -3: .long . - .long __arch_info_begin - .long __arch_info_end - -/* - * Lookup machine architecture in the linker-build list of architectures. - * Note that we can't use the absolute addresses for the __arch_info - * lists since we aren't running with the MMU on (and therefore, we are - * not in the correct address space). We have to calculate the offset. - * - * r1 = machine architecture number - * Returns: - * r3, r4, r6 corrupted - * r5 = mach_info pointer in physical address space - */ - .type __lookup_machine_type, %function -__lookup_machine_type: - adr r3, 3b - ldmia r3, {r4, r5, r6} - sub r3, r3, r4 @ get offset between virt&phys - add r5, r5, r3 @ convert virt addresses to - add r6, r6, r3 @ physical address space -1: ldr r3, [r5, #MACHINFO_TYPE] @ get machine type - teq r3, r1 @ matches loader number? - beq 2f @ found - add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc - cmp r5, r6 - blo 1b - mov r5, #0 @ unknown machine -2: mov pc, lr - -/* - * This provides a C-API version of the above function. - */ -ENTRY(lookup_machine_type) - stmfd sp!, {r4 - r6, lr} - mov r1, r0 - bl __lookup_machine_type - mov r0, r5 - ldmfd sp!, {r4 - r6, pc} +#include "head-common.S" diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 489c069e5c3e..1ff75cee4b0d 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -474,4 +474,3 @@ unsigned long get_wchan(struct task_struct *p) } while (count ++ < 16); return 0; } -EXPORT_SYMBOL(get_wchan); diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index b7cd280bfd63..8cff73e668b3 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -252,6 +252,9 @@ static void __init dump_cpu_info(int cpu) dump_cache("cache", cpu, CACHE_ISIZE(info)); } } + + if (arch_is_coherent()) + printk("Cache coherency enabled\n"); } int cpu_architecture(void) @@ -319,6 +322,12 @@ static void __init setup_processor(void) sprintf(system_utsname.machine, "%s%c", list->arch_name, ENDIANNESS); sprintf(elf_platform, "%s%c", list->elf_name, ENDIANNESS); elf_hwcap = list->elf_hwcap; +#ifndef CONFIG_ARM_THUMB + elf_hwcap &= ~HWCAP_THUMB; +#endif +#ifndef CONFIG_VFP + elf_hwcap &= ~HWCAP_VFP; +#endif cpu_proc_init(); } diff --git a/arch/arm/kernel/signal.h b/arch/arm/kernel/signal.h index 9991049c522d..27beece15502 100644 --- a/arch/arm/kernel/signal.h +++ b/arch/arm/kernel/signal.h @@ -7,6 +7,6 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#define KERN_SIGRETURN_CODE 0xffff0500 +#define KERN_SIGRETURN_CODE (CONFIG_VECTORS_BASE + 0x00000500) extern const unsigned long sigreturn_codes[7]; diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index d566d5f4574d..35230a060108 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -688,6 +688,7 @@ EXPORT_SYMBOL(abort); void __init trap_init(void) { + unsigned long vectors = CONFIG_VECTORS_BASE; extern char __stubs_start[], __stubs_end[]; extern char __vectors_start[], __vectors_end[]; extern char __kuser_helper_start[], __kuser_helper_end[]; @@ -698,9 +699,9 @@ void __init trap_init(void) * into the vector page, mapped at 0xffff0000, and ensure these * are visible to the instruction stream. */ - memcpy((void *)0xffff0000, __vectors_start, __vectors_end - __vectors_start); - memcpy((void *)0xffff0200, __stubs_start, __stubs_end - __stubs_start); - memcpy((void *)0xffff1000 - kuser_sz, __kuser_helper_start, kuser_sz); + memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start); + memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start); + memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz); /* * Copy signal return handlers into the vector page, and @@ -709,6 +710,6 @@ void __init trap_init(void) memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes, sizeof(sigreturn_codes)); - flush_icache_range(0xffff0000, 0xffff0000 + PAGE_SIZE); + flush_icache_range(vectors, vectors + PAGE_SIZE); modify_domain(DOMAIN_USER, DOMAIN_CLIENT); } diff --git a/arch/arm/mach-at91rm9200/Makefile b/arch/arm/mach-at91rm9200/Makefile index 75e6ee318ded..ef88c4128edc 100644 --- a/arch/arm/mach-at91rm9200/Makefile +++ b/arch/arm/mach-at91rm9200/Makefile @@ -16,11 +16,12 @@ obj-$(CONFIG_MACH_CSB637) += board-csb637.o #obj-$(CONFIG_MACH_KB9200) += board-kb9202.o # LEDs support -#led-$(CONFIG_ARCH_AT91RM9200DK) += leds.o -#led-$(CONFIG_MACH_AT91RM9200EK) += leds.o -#led-$(CONFIG_MACH_CSB337) += leds.o -#led-$(CONFIG_MACH_CSB637) += leds.o +led-$(CONFIG_ARCH_AT91RM9200DK) += leds.o +led-$(CONFIG_MACH_AT91RM9200EK) += leds.o +led-$(CONFIG_MACH_CSB337) += leds.o +led-$(CONFIG_MACH_CSB637) += leds.o #led-$(CONFIG_MACH_KB9200) += leds.o +#led-$(CONFIG_MACH_KAFA) += leds.o obj-$(CONFIG_LEDS) += $(led-y) # VGA support diff --git a/arch/arm/mach-at91rm9200/board-csb337.c b/arch/arm/mach-at91rm9200/board-csb337.c index 54022e58d50d..f45104ceea8f 100644 --- a/arch/arm/mach-at91rm9200/board-csb337.c +++ b/arch/arm/mach-at91rm9200/board-csb337.c @@ -67,6 +67,9 @@ static void __init csb337_map_io(void) /* Initialize clocks: 3.6864 MHz crystal */ at91_clock_init(3686400); + /* Setup the LEDs */ + at91_init_leds(AT91_PIN_PB2, AT91_PIN_PB2); + #ifdef CONFIG_SERIAL_AT91 at91_console_port = CSB337_SERIAL_CONSOLE; memcpy(at91_serial_map, serial, sizeof(serial)); diff --git a/arch/arm/mach-at91rm9200/board-csb637.c b/arch/arm/mach-at91rm9200/board-csb637.c index 8195f9d919ea..f2c2d6e79bc6 100644 --- a/arch/arm/mach-at91rm9200/board-csb637.c +++ b/arch/arm/mach-at91rm9200/board-csb637.c @@ -67,6 +67,9 @@ static void __init csb637_map_io(void) /* Initialize clocks: 3.6864 MHz crystal */ at91_clock_init(3686400); + /* Setup the LEDs */ + at91_init_leds(AT91_PIN_PB2, AT91_PIN_PB2); + #ifdef CONFIG_SERIAL_AT91 at91_console_port = CSB637_SERIAL_CONSOLE; memcpy(at91_serial_map, serial, sizeof(serial)); diff --git a/arch/arm/mach-at91rm9200/board-dk.c b/arch/arm/mach-at91rm9200/board-dk.c index 8a783368366e..2d7200ed66ed 100644 --- a/arch/arm/mach-at91rm9200/board-dk.c +++ b/arch/arm/mach-at91rm9200/board-dk.c @@ -70,6 +70,9 @@ static void __init dk_map_io(void) /* Initialize clocks: 18.432 MHz crystal */ at91_clock_init(18432000); + /* Setup the LEDs */ + at91_init_leds(AT91_PIN_PB2, AT91_PIN_PB2); + #ifdef CONFIG_SERIAL_AT91 at91_console_port = DK_SERIAL_CONSOLE; memcpy(at91_serial_map, serial, sizeof(serial)); @@ -118,9 +121,14 @@ static void __init dk_board_init(void) at91_add_device_udc(&dk_udc_data); /* Compact Flash */ at91_add_device_cf(&dk_cf_data); +#ifdef CONFIG_MTD_AT91_DATAFLASH_CARD + /* DataFlash card */ + at91_set_gpio_output(AT91_PIN_PB7, 0); +#else /* MMC */ - at91_set_gpio_output(AT91_PIN_PB7, 1); /* this MMC card slot can optionally use SPI signaling (CS3). default: MMC */ + at91_set_gpio_output(AT91_PIN_PB7, 1); /* this MMC card slot can optionally use SPI signaling (CS3). */ at91_add_device_mmc(&dk_mmc_data); +#endif /* VGA */ // dk_add_device_video(); } diff --git a/arch/arm/mach-at91rm9200/board-ek.c b/arch/arm/mach-at91rm9200/board-ek.c index fd0752eba897..80d90f5135a1 100644 --- a/arch/arm/mach-at91rm9200/board-ek.c +++ b/arch/arm/mach-at91rm9200/board-ek.c @@ -70,6 +70,9 @@ static void __init ek_map_io(void) /* Initialize clocks: 18.432 MHz crystal */ at91_clock_init(18432000); + /* Setup the LEDs */ + at91_init_leds(AT91_PIN_PB1, AT91_PIN_PB2); + #ifdef CONFIG_SERIAL_AT91 at91_console_port = EK_SERIAL_CONSOLE; memcpy(at91_serial_map, serial, sizeof(serial)); @@ -111,9 +114,14 @@ static void __init ek_board_init(void) at91_add_device_usbh(&ek_usbh_data); /* USB Device */ at91_add_device_udc(&ek_udc_data); +#ifdef CONFIG_MTD_AT91_DATAFLASH_CARD + /* DataFlash card */ + at91_set_gpio_output(AT91_PIN_PB22, 0); +#else /* MMC */ - at91_set_gpio_output(AT91_PIN_PB22, 1); /* this MMC card slot can optionally use SPI signaling (CS3). default: MMC */ + at91_set_gpio_output(AT91_PIN_PB22, 1); /* this MMC card slot can optionally use SPI signaling (CS3). */ at91_add_device_mmc(&ek_mmc_data); +#endif /* VGA */ // ek_add_device_video(); } diff --git a/arch/arm/mach-at91rm9200/devices.c b/arch/arm/mach-at91rm9200/devices.c index 57eedd5beaf6..1781b8f342c4 100644 --- a/arch/arm/mach-at91rm9200/devices.c +++ b/arch/arm/mach-at91rm9200/devices.c @@ -28,10 +28,10 @@ static u64 ohci_dmamask = 0xffffffffUL; static struct at91_usbh_data usbh_data; -static struct resource at91rm9200_usbh_resource[] = { +static struct resource at91_usbh_resource[] = { [0] = { .start = AT91_UHP_BASE, - .end = AT91_UHP_BASE + SZ_1M -1, + .end = AT91_UHP_BASE + SZ_1M - 1, .flags = IORESOURCE_MEM, }, [1] = { @@ -49,8 +49,8 @@ static struct platform_device at91rm9200_usbh_device = { .coherent_dma_mask = 0xffffffff, .platform_data = &usbh_data, }, - .resource = at91rm9200_usbh_resource, - .num_resources = ARRAY_SIZE(at91rm9200_usbh_resource), + .resource = at91_usbh_resource, + .num_resources = ARRAY_SIZE(at91_usbh_resource), }; void __init at91_add_device_usbh(struct at91_usbh_data *data) @@ -121,6 +121,19 @@ void __init at91_add_device_udc(struct at91_udc_data *data) {} static u64 eth_dmamask = 0xffffffffUL; static struct at91_eth_data eth_data; +static struct resource at91_eth_resources[] = { + [0] = { + .start = AT91_BASE_EMAC, + .end = AT91_BASE_EMAC + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91_ID_EMAC, + .end = AT91_ID_EMAC, + .flags = IORESOURCE_IRQ, + }, +}; + static struct platform_device at91rm9200_eth_device = { .name = "at91_ether", .id = -1, @@ -129,7 +142,8 @@ static struct platform_device at91rm9200_eth_device = { .coherent_dma_mask = 0xffffffff, .platform_data = ð_data, }, - .num_resources = 0, + .resource = at91_eth_resources, + .num_resources = ARRAY_SIZE(at91_eth_resources), }; void __init at91_add_device_eth(struct at91_eth_data *data) @@ -224,15 +238,20 @@ static u64 mmc_dmamask = 0xffffffffUL; static struct at91_mmc_data mmc_data; static struct resource at91_mmc_resources[] = { - { + [0] = { .start = AT91_BASE_MCI, .end = AT91_BASE_MCI + SZ_16K - 1, .flags = IORESOURCE_MEM, - } + }, + [1] = { + .start = AT91_ID_MCI, + .end = AT91_ID_MCI, + .flags = IORESOURCE_IRQ, + }, }; static struct platform_device at91rm9200_mmc_device = { - .name = "at91rm9200_mci", + .name = "at91_mci", .id = -1, .dev = { .dma_mask = &mmc_dmamask, @@ -290,4 +309,123 @@ void __init at91_add_device_mmc(struct at91_mmc_data *data) void __init at91_add_device_mmc(struct at91_mmc_data *data) {} #endif +/* -------------------------------------------------------------------- + * NAND / SmartMedia + * -------------------------------------------------------------------- */ + +#if defined(CONFIG_MTD_NAND_AT91) || defined(CONFIG_MTD_NAND_AT91_MODULE) +static struct at91_nand_data nand_data; + +static struct resource at91_nand_resources[] = { + { + .start = AT91_SMARTMEDIA_BASE, + .end = AT91_SMARTMEDIA_BASE + SZ_8M - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device at91_nand_device = { + .name = "at91_nand", + .id = -1, + .dev = { + .platform_data = &nand_data, + }, + .resource = at91_nand_resources, + .num_resources = ARRAY_SIZE(at91_nand_resources), +}; + +void __init at91_add_device_nand(struct at91_nand_data *data) +{ + if (!data) + return; + + /* enable pin */ + if (data->enable_pin) + at91_set_gpio_output(data->enable_pin, 1); + + /* ready/busy pin */ + if (data->rdy_pin) + at91_set_gpio_input(data->rdy_pin, 1); + + /* card detect pin */ + if (data->det_pin) + at91_set_gpio_input(data->det_pin, 1); + + at91_set_A_periph(AT91_PIN_PC1, 0); /* SMOE */ + at91_set_A_periph(AT91_PIN_PC3, 0); /* SMWE */ + + nand_data = *data; + platform_device_register(&at91_nand_device); +} +#else +void __init at91_add_device_nand(struct at91_nand_data *data) {} +#endif + + +/* -------------------------------------------------------------------- + * TWI (i2c) + * -------------------------------------------------------------------- */ + +#if defined(CONFIG_I2C_AT91) || defined(CONFIG_I2C_AT91_MODULE) +static struct platform_device at91rm9200_twi_device = { + .name = "at91_i2c", + .id = -1, + .num_resources = 0, +}; + +void __init at91_add_device_i2c(void) +{ + /* pins used for TWI interface */ + at91_set_A_periph(AT91_PIN_PA25, 0); /* TWD */ + at91_set_multi_drive(AT91_PIN_PA25, 1); + + at91_set_A_periph(AT91_PIN_PA26, 0); /* TWCK */ + at91_set_multi_drive(AT91_PIN_PA26, 1); + + platform_device_register(&at91rm9200_twi_device); +} +#else +void __init at91_add_device_i2c(void) {} +#endif + + +/* -------------------------------------------------------------------- + * RTC + * -------------------------------------------------------------------- */ + +#if defined(CONFIG_AT91_RTC) || defined(CONFIG_AT91_RTC_MODULE) +static struct platform_device at91rm9200_rtc_device = { + .name = "at91_rtc", + .id = -1, + .num_resources = 0, +}; + +void __init at91_add_device_rtc(void) +{ + platform_device_register(&at91rm9200_rtc_device); +} +#else +void __init at91_add_device_rtc(void) {} +#endif + + +/* -------------------------------------------------------------------- + * LEDs + * -------------------------------------------------------------------- */ + +#if defined(CONFIG_LEDS) +u8 at91_leds_cpu; +u8 at91_leds_timer; + +void __init at91_init_leds(u8 cpu_led, u8 timer_led) +{ + at91_leds_cpu = cpu_led; + at91_leds_timer = timer_led; +} + +#else +void __init at91_init_leds(u8 cpu_led, u8 timer_led) {} +#endif + + /* -------------------------------------------------------------------- */ diff --git a/arch/arm/mach-at91rm9200/leds.c b/arch/arm/mach-at91rm9200/leds.c new file mode 100644 index 000000000000..28150e8905ba --- /dev/null +++ b/arch/arm/mach-at91rm9200/leds.c @@ -0,0 +1,100 @@ +/* + * LED driver for Atmel AT91-based boards. + * + * Copyright (C) SAN People (Pty) Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include + + +static inline void at91_led_on(unsigned int led) +{ + at91_set_gpio_value(led, 0); +} + +static inline void at91_led_off(unsigned int led) +{ + at91_set_gpio_value(led, 1); +} + +static inline void at91_led_toggle(unsigned int led) +{ + unsigned long is_off = at91_get_gpio_value(led); + if (is_off) + at91_led_on(led); + else + at91_led_off(led); +} + + +/* + * Handle LED events. + */ +static void at91_leds_event(led_event_t evt) +{ + unsigned long flags; + + local_irq_save(flags); + + switch(evt) { + case led_start: /* System startup */ + at91_led_on(at91_leds_cpu); + break; + + case led_stop: /* System stop / suspend */ + at91_led_off(at91_leds_cpu); + break; + +#ifdef CONFIG_LEDS_TIMER + case led_timer: /* Every 50 timer ticks */ + at91_led_toggle(at91_leds_timer); + break; +#endif + +#ifdef CONFIG_LEDS_CPU + case led_idle_start: /* Entering idle state */ + at91_led_off(at91_leds_cpu); + break; + + case led_idle_end: /* Exit idle state */ + at91_led_on(at91_leds_cpu); + break; +#endif + + default: + break; + } + + local_irq_restore(flags); +} + + +static int __init leds_init(void) +{ + if (!at91_leds_timer || !at91_leds_cpu) + return -ENODEV; + + /* Enable PIO to access the LEDs */ + at91_set_gpio_output(at91_leds_timer, 1); + at91_set_gpio_output(at91_leds_cpu, 1); + + leds_event = at91_leds_event; + + leds_event(led_start); + return 0; +} + +__initcall(leds_init); diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c index 2d892e4daa07..dcd417625389 100644 --- a/arch/arm/mach-ep93xx/core.c +++ b/arch/arm/mach-ep93xx/core.c @@ -424,6 +424,14 @@ static struct amba_device uart3_device = { .periphid = 0x00041010, }; + +static struct platform_device ep93xx_rtc_device = { + .name = "ep93xx-rtc", + .id = -1, + .num_resources = 0, +}; + + void __init ep93xx_init_devices(void) { unsigned int v; @@ -439,4 +447,6 @@ void __init ep93xx_init_devices(void) amba_device_register(&uart1_device, &iomem_resource); amba_device_register(&uart2_device, &iomem_resource); amba_device_register(&uart3_device, &iomem_resource); + + platform_device_register(&ep93xx_rtc_device); } diff --git a/arch/arm/mach-ep93xx/ts72xx.c b/arch/arm/mach-ep93xx/ts72xx.c index 777e75daa8a5..9be01b0c3f48 100644 --- a/arch/arm/mach-ep93xx/ts72xx.c +++ b/arch/arm/mach-ep93xx/ts72xx.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include #include @@ -39,6 +41,16 @@ static struct map_desc ts72xx_io_desc[] __initdata = { .pfn = __phys_to_pfn(TS72XX_OPTIONS2_PHYS_BASE), .length = TS72XX_OPTIONS2_SIZE, .type = MT_DEVICE, + }, { + .virtual = TS72XX_RTC_INDEX_VIRT_BASE, + .pfn = __phys_to_pfn(TS72XX_RTC_INDEX_PHYS_BASE), + .length = TS72XX_RTC_INDEX_SIZE, + .type = MT_DEVICE, + }, { + .virtual = TS72XX_RTC_DATA_VIRT_BASE, + .pfn = __phys_to_pfn(TS72XX_RTC_DATA_PHYS_BASE), + .length = TS72XX_RTC_DATA_SIZE, + .type = MT_DEVICE, } }; @@ -99,11 +111,38 @@ static void __init ts72xx_map_io(void) } } +static unsigned char ts72xx_rtc_readb(unsigned long addr) +{ + __raw_writeb(addr, TS72XX_RTC_INDEX_VIRT_BASE); + return __raw_readb(TS72XX_RTC_DATA_VIRT_BASE); +} + +static void ts72xx_rtc_writeb(unsigned char value, unsigned long addr) +{ + __raw_writeb(addr, TS72XX_RTC_INDEX_VIRT_BASE); + __raw_writeb(value, TS72XX_RTC_DATA_VIRT_BASE); +} + +static struct m48t86_ops ts72xx_rtc_ops = { + .readb = ts72xx_rtc_readb, + .writeb = ts72xx_rtc_writeb, +}; + +static struct platform_device ts72xx_rtc_device = { + .name = "rtc-m48t86", + .id = -1, + .dev = { + .platform_data = &ts72xx_rtc_ops, + }, + .num_resources = 0, +}; + static void __init ts72xx_init_machine(void) { ep93xx_init_devices(); if (board_is_ts7200()) physmap_configure(TS72XX_NOR_PHYS_BASE, 0x01000000, 1, NULL); + platform_device_register(&ts72xx_rtc_device); } MACHINE_START(TS72XX, "Technologic Systems TS-72xx SBC") diff --git a/arch/arm/mach-imx/dma.c b/arch/arm/mach-imx/dma.c index 71a59e196166..4ca51dcf13ac 100644 --- a/arch/arm/mach-imx/dma.c +++ b/arch/arm/mach-imx/dma.c @@ -7,11 +7,18 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * 03/03/2004 Sascha Hauer + * 2004-03-03 Sascha Hauer * initial version heavily inspired by * linux/arch/arm/mach-pxa/dma.c + * + * 2005-04-17 Pavel Pisa + * Changed to support scatter gather DMA + * by taking Russell's code from RiscPC + * */ +#undef DEBUG + #include #include #include @@ -22,69 +29,368 @@ #include #include #include +#include -static struct dma_channel { - char *name; - void (*irq_handler) (int, void *, struct pt_regs *); - void (*err_handler) (int, void *, struct pt_regs *); - void *data; -} dma_channels[11]; +struct imx_dma_channel imx_dma_channels[IMX_DMA_CHANNELS]; -/* set err_handler to NULL to have the standard info-only error handler */ -int -imx_request_dma(char *name, imx_dma_prio prio, - void (*irq_handler) (int, void *, struct pt_regs *), - void (*err_handler) (int, void *, struct pt_regs *), void *data) +/* + * imx_dma_sg_next - prepare next chunk for scatter-gather DMA emulation + * @dma_ch: i.MX DMA channel number + * @lastcount: number of bytes transferred during last transfer + * + * Functions prepares DMA controller for next sg data chunk transfer. + * The @lastcount argument informs function about number of bytes transferred + * during last block. Zero value can be used for @lastcount to setup DMA + * for the first chunk. + */ +static inline int imx_dma_sg_next(imx_dmach_t dma_ch, unsigned int lastcount) { - unsigned long flags; - int i, found = 0; + struct imx_dma_channel *imxdma = &imx_dma_channels[dma_ch]; + unsigned int nextcount; + unsigned int nextaddr; - /* basic sanity checks */ - if (!name || !irq_handler) - return -EINVAL; + if (!imxdma->name) { + printk(KERN_CRIT "%s: called for not allocated channel %d\n", + __FUNCTION__, dma_ch); + return 0; + } - local_irq_save(flags); + imxdma->resbytes -= lastcount; - /* try grabbing a DMA channel with the requested priority */ - for (i = prio; i < prio + (prio == DMA_PRIO_LOW) ? 8 : 4; i++) { - if (!dma_channels[i].name) { - found = 1; - break; + if (!imxdma->sg) { + pr_debug("imxdma%d: no sg data\n", dma_ch); + return 0; + } + + imxdma->sgbc += lastcount; + if ((imxdma->sgbc >= imxdma->sg->length) || !imxdma->resbytes) { + if ((imxdma->sgcount <= 1) || !imxdma->resbytes) { + pr_debug("imxdma%d: sg transfer limit reached\n", + dma_ch); + imxdma->sgcount=0; + imxdma->sg = NULL; + return 0; + } else { + imxdma->sgcount--; + imxdma->sg++; + imxdma->sgbc = 0; } } + nextcount = imxdma->sg->length - imxdma->sgbc; + nextaddr = imxdma->sg->dma_address + imxdma->sgbc; - if (!found) { - /* requested prio group is full, try hier priorities */ - for (i = prio - 1; i >= 0; i--) { - if (!dma_channels[i].name) { - found = 1; - break; - } - } - } + if(imxdma->resbytes < nextcount) + nextcount = imxdma->resbytes; - if (found) { - DIMR &= ~(1 << i); - dma_channels[i].name = name; - dma_channels[i].irq_handler = irq_handler; - dma_channels[i].err_handler = err_handler; - dma_channels[i].data = data; - } else { - printk(KERN_WARNING "No more available DMA channels for %s\n", - name); - i = -ENODEV; - } + if ((imxdma->dma_mode & DMA_MODE_MASK) == DMA_MODE_READ) + DAR(dma_ch) = nextaddr; + else + SAR(dma_ch) = nextaddr; - local_irq_restore(flags); - return i; + CNTR(dma_ch) = nextcount; + pr_debug("imxdma%d: next sg chunk dst 0x%08x, src 0x%08x, size 0x%08x\n", + dma_ch, DAR(dma_ch), SAR(dma_ch), CNTR(dma_ch)); + + return nextcount; } -void -imx_free_dma(int dma_ch) +/* + * imx_dma_setup_sg_base - scatter-gather DMA emulation + * @dma_ch: i.MX DMA channel number + * @sg: pointer to the scatter-gather list/vector + * @sgcount: scatter-gather list hungs count + * + * Functions sets up i.MX DMA state for emulated scatter-gather transfer + * and sets up channel registers to be ready for the first chunk + */ +static int +imx_dma_setup_sg_base(imx_dmach_t dma_ch, + struct scatterlist *sg, unsigned int sgcount) +{ + struct imx_dma_channel *imxdma = &imx_dma_channels[dma_ch]; + + imxdma->sg = sg; + imxdma->sgcount = sgcount; + imxdma->sgbc = 0; + return imx_dma_sg_next(dma_ch, 0); +} + +/** + * imx_dma_setup_single - setup i.MX DMA channel for linear memory to/from device transfer + * @dma_ch: i.MX DMA channel number + * @dma_address: the DMA/physical memory address of the linear data block + * to transfer + * @dma_length: length of the data block in bytes + * @dev_addr: physical device port address + * @dmamode: DMA transfer mode, %DMA_MODE_READ from the device to the memory + * or %DMA_MODE_WRITE from memory to the device + * + * The function setups DMA channel source and destination addresses for transfer + * specified by provided parameters. The scatter-gather emulation is disabled, + * because linear data block + * form the physical address range is transfered. + * Return value: if incorrect parameters are provided -%EINVAL. + * Zero indicates success. + */ +int +imx_dma_setup_single(imx_dmach_t dma_ch, dma_addr_t dma_address, + unsigned int dma_length, unsigned int dev_addr, + dmamode_t dmamode) +{ + struct imx_dma_channel *imxdma = &imx_dma_channels[dma_ch]; + + imxdma->sg = NULL; + imxdma->sgcount = 0; + imxdma->dma_mode = dmamode; + imxdma->resbytes = dma_length; + + if (!dma_address) { + printk(KERN_ERR "imxdma%d: imx_dma_setup_single null address\n", + dma_ch); + return -EINVAL; + } + + if (!dma_length) { + printk(KERN_ERR "imxdma%d: imx_dma_setup_single zero length\n", + dma_ch); + return -EINVAL; + } + + if ((dmamode & DMA_MODE_MASK) == DMA_MODE_READ) { + pr_debug("imxdma%d: mx_dma_setup_single2dev dma_addressg=0x%08x dma_length=%d dev_addr=0x%08x for read\n", + dma_ch, (unsigned int)dma_address, dma_length, + dev_addr); + SAR(dma_ch) = dev_addr; + DAR(dma_ch) = (unsigned int)dma_address; + } else if ((dmamode & DMA_MODE_MASK) == DMA_MODE_WRITE) { + pr_debug("imxdma%d: mx_dma_setup_single2dev dma_addressg=0x%08x dma_length=%d dev_addr=0x%08x for write\n", + dma_ch, (unsigned int)dma_address, dma_length, + dev_addr); + SAR(dma_ch) = (unsigned int)dma_address; + DAR(dma_ch) = dev_addr; + } else { + printk(KERN_ERR "imxdma%d: imx_dma_setup_single bad dmamode\n", + dma_ch); + return -EINVAL; + } + + CNTR(dma_ch) = dma_length; + + return 0; +} + +/** + * imx_dma_setup_sg - setup i.MX DMA channel SG list to/from device transfer + * @dma_ch: i.MX DMA channel number + * @sg: pointer to the scatter-gather list/vector + * @sgcount: scatter-gather list hungs count + * @dma_length: total length of the transfer request in bytes + * @dev_addr: physical device port address + * @dmamode: DMA transfer mode, %DMA_MODE_READ from the device to the memory + * or %DMA_MODE_WRITE from memory to the device + * + * The function setups DMA channel state and registers to be ready for transfer + * specified by provided parameters. The scatter-gather emulation is set up + * according to the parameters. + * + * The full preparation of the transfer requires setup of more register + * by the caller before imx_dma_enable() can be called. + * + * %BLR(dma_ch) holds transfer burst length in bytes, 0 means 64 bytes + * + * %RSSR(dma_ch) has to be set to the DMA request line source %DMA_REQ_xxx + * + * %CCR(dma_ch) has to specify transfer parameters, the next settings is typical + * for linear or simple scatter-gather transfers if %DMA_MODE_READ is specified + * + * %CCR_DMOD_LINEAR | %CCR_DSIZ_32 | %CCR_SMOD_FIFO | %CCR_SSIZ_x + * + * The typical setup for %DMA_MODE_WRITE is specified by next options combination + * + * %CCR_SMOD_LINEAR | %CCR_SSIZ_32 | %CCR_DMOD_FIFO | %CCR_DSIZ_x + * + * Be carefull there and do not mistakenly mix source and target device + * port sizes constants, they are really different: + * %CCR_SSIZ_8, %CCR_SSIZ_16, %CCR_SSIZ_32, + * %CCR_DSIZ_8, %CCR_DSIZ_16, %CCR_DSIZ_32 + * + * Return value: if incorrect parameters are provided -%EINVAL. + * Zero indicates success. + */ +int +imx_dma_setup_sg(imx_dmach_t dma_ch, + struct scatterlist *sg, unsigned int sgcount, unsigned int dma_length, + unsigned int dev_addr, dmamode_t dmamode) +{ + int res; + struct imx_dma_channel *imxdma = &imx_dma_channels[dma_ch]; + + imxdma->sg = NULL; + imxdma->sgcount = 0; + imxdma->dma_mode = dmamode; + imxdma->resbytes = dma_length; + + if (!sg || !sgcount) { + printk(KERN_ERR "imxdma%d: imx_dma_setup_sg epty sg list\n", + dma_ch); + return -EINVAL; + } + + if (!sg->length) { + printk(KERN_ERR "imxdma%d: imx_dma_setup_sg zero length\n", + dma_ch); + return -EINVAL; + } + + if ((dmamode & DMA_MODE_MASK) == DMA_MODE_READ) { + pr_debug("imxdma%d: mx_dma_setup_sg2dev sg=%p sgcount=%d total length=%d dev_addr=0x%08x for read\n", + dma_ch, sg, sgcount, dma_length, dev_addr); + SAR(dma_ch) = dev_addr; + } else if ((dmamode & DMA_MODE_MASK) == DMA_MODE_WRITE) { + pr_debug("imxdma%d: mx_dma_setup_sg2dev sg=%p sgcount=%d total length=%d dev_addr=0x%08x for write\n", + dma_ch, sg, sgcount, dma_length, dev_addr); + DAR(dma_ch) = dev_addr; + } else { + printk(KERN_ERR "imxdma%d: imx_dma_setup_sg bad dmamode\n", + dma_ch); + return -EINVAL; + } + + res = imx_dma_setup_sg_base(dma_ch, sg, sgcount); + if (res <= 0) { + printk(KERN_ERR "imxdma%d: no sg chunk ready\n", dma_ch); + return -EINVAL; + } + + return 0; +} + +/** + * imx_dma_setup_handlers - setup i.MX DMA channel end and error notification handlers + * @dma_ch: i.MX DMA channel number + * @irq_handler: the pointer to the function called if the transfer + * ends successfully + * @err_handler: the pointer to the function called if the premature + * end caused by error occurs + * @data: user specified value to be passed to the handlers + */ +int +imx_dma_setup_handlers(imx_dmach_t dma_ch, + void (*irq_handler) (int, void *, struct pt_regs *), + void (*err_handler) (int, void *, struct pt_regs *), + void *data) +{ + struct imx_dma_channel *imxdma = &imx_dma_channels[dma_ch]; + unsigned long flags; + + if (!imxdma->name) { + printk(KERN_CRIT "%s: called for not allocated channel %d\n", + __FUNCTION__, dma_ch); + return -ENODEV; + } + + local_irq_save(flags); + DISR = (1 << dma_ch); + imxdma->irq_handler = irq_handler; + imxdma->err_handler = err_handler; + imxdma->data = data; + local_irq_restore(flags); + return 0; +} + +/** + * imx_dma_enable - function to start i.MX DMA channel operation + * @dma_ch: i.MX DMA channel number + * + * The channel has to be allocated by driver through imx_dma_request() + * or imx_dma_request_by_prio() function. + * The transfer parameters has to be set to the channel registers through + * call of the imx_dma_setup_single() or imx_dma_setup_sg() function + * and registers %BLR(dma_ch), %RSSR(dma_ch) and %CCR(dma_ch) has to + * be set prior this function call by the channel user. + */ +void imx_dma_enable(imx_dmach_t dma_ch) +{ + struct imx_dma_channel *imxdma = &imx_dma_channels[dma_ch]; + unsigned long flags; + + pr_debug("imxdma%d: imx_dma_enable\n", dma_ch); + + if (!imxdma->name) { + printk(KERN_CRIT "%s: called for not allocated channel %d\n", + __FUNCTION__, dma_ch); + return; + } + + local_irq_save(flags); + DISR = (1 << dma_ch); + DIMR &= ~(1 << dma_ch); + CCR(dma_ch) |= CCR_CEN; + local_irq_restore(flags); +} + +/** + * imx_dma_disable - stop, finish i.MX DMA channel operatin + * @dma_ch: i.MX DMA channel number + */ +void imx_dma_disable(imx_dmach_t dma_ch) { unsigned long flags; - if (!dma_channels[dma_ch].name) { + pr_debug("imxdma%d: imx_dma_disable\n", dma_ch); + + local_irq_save(flags); + DIMR |= (1 << dma_ch); + CCR(dma_ch) &= ~CCR_CEN; + DISR = (1 << dma_ch); + local_irq_restore(flags); +} + +/** + * imx_dma_request - request/allocate specified channel number + * @dma_ch: i.MX DMA channel number + * @name: the driver/caller own non-%NULL identification + */ +int imx_dma_request(imx_dmach_t dma_ch, const char *name) +{ + struct imx_dma_channel *imxdma = &imx_dma_channels[dma_ch]; + unsigned long flags; + + /* basic sanity checks */ + if (!name) + return -EINVAL; + + if (dma_ch >= IMX_DMA_CHANNELS) { + printk(KERN_CRIT "%s: called for non-existed channel %d\n", + __FUNCTION__, dma_ch); + return -EINVAL; + } + + local_irq_save(flags); + if (imxdma->name) { + local_irq_restore(flags); + return -ENODEV; + } + + imxdma->name = name; + imxdma->irq_handler = NULL; + imxdma->err_handler = NULL; + imxdma->data = NULL; + imxdma->sg = NULL; + local_irq_restore(flags); + return 0; +} + +/** + * imx_dma_free - release previously acquired channel + * @dma_ch: i.MX DMA channel number + */ +void imx_dma_free(imx_dmach_t dma_ch) +{ + unsigned long flags; + struct imx_dma_channel *imxdma = &imx_dma_channels[dma_ch]; + + if (!imxdma->name) { printk(KERN_CRIT "%s: trying to free channel %d which is already freed\n", __FUNCTION__, dma_ch); @@ -92,27 +398,84 @@ imx_free_dma(int dma_ch) } local_irq_save(flags); - DIMR &= ~(1 << dma_ch); - dma_channels[dma_ch].name = NULL; + /* Disable interrupts */ + DIMR |= (1 << dma_ch); + CCR(dma_ch) &= ~CCR_CEN; + imxdma->name = NULL; local_irq_restore(flags); } -static irqreturn_t -dma_err_handler(int irq, void *dev_id, struct pt_regs *regs) +/** + * imx_dma_request_by_prio - find and request some of free channels best suiting requested priority + * @dma_ch: i.MX DMA channel number + * @name: the driver/caller own non-%NULL identification + * @prio: one of the hardware distinguished priority level: + * %DMA_PRIO_HIGH, %DMA_PRIO_MEDIUM, %DMA_PRIO_LOW + * + * This function tries to find free channel in the specified priority group + * if the priority cannot be achieved it tries to look for free channel + * in the higher and then even lower priority groups. + * + * Return value: If there is no free channel to allocate, -%ENODEV is returned. + * Zero value indicates successful channel allocation. + */ +int +imx_dma_request_by_prio(imx_dmach_t * pdma_ch, const char *name, + imx_dma_prio prio) +{ + int i; + int best; + + switch (prio) { + case (DMA_PRIO_HIGH): + best = 8; + break; + case (DMA_PRIO_MEDIUM): + best = 4; + break; + case (DMA_PRIO_LOW): + default: + best = 0; + break; + } + + for (i = best; i < IMX_DMA_CHANNELS; i++) { + if (!imx_dma_request(i, name)) { + *pdma_ch = i; + return 0; + } + } + + for (i = best - 1; i >= 0; i--) { + if (!imx_dma_request(i, name)) { + *pdma_ch = i; + return 0; + } + } + + printk(KERN_ERR "%s: no free DMA channel found\n", __FUNCTION__); + + return -ENODEV; +} + +static irqreturn_t dma_err_handler(int irq, void *dev_id, struct pt_regs *regs) { int i, disr = DISR; - struct dma_channel *channel; + struct imx_dma_channel *channel; unsigned int err_mask = DBTOSR | DRTOSR | DSESR | DBOSR; DISR = disr; - for (i = 0; i < 11; i++) { - channel = &dma_channels[i]; + for (i = 0; i < IMX_DMA_CHANNELS; i++) { + channel = &imx_dma_channels[i]; - if ( (err_mask & 1<name && channel->err_handler) { + if ((err_mask & 1 << i) && channel->name + && channel->err_handler) { channel->err_handler(i, channel->data, regs); continue; } + imx_dma_channels[i].sg = NULL; + if (DBTOSR & (1 << i)) { printk(KERN_WARNING "Burst timeout on channel %d (%s)\n", @@ -141,17 +504,27 @@ dma_err_handler(int irq, void *dev_id, struct pt_regs *regs) return IRQ_HANDLED; } -static irqreturn_t -dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs) { int i, disr = DISR; + pr_debug("imxdma: dma_irq_handler called, disr=0x%08x\n", + disr); + DISR = disr; - for (i = 0; i < 11; i++) { + for (i = 0; i < IMX_DMA_CHANNELS; i++) { if (disr & (1 << i)) { - struct dma_channel *channel = &dma_channels[i]; - if (channel->name && channel->irq_handler) { - channel->irq_handler(i, channel->data, regs); + struct imx_dma_channel *channel = &imx_dma_channels[i]; + if (channel->name) { + if (imx_dma_sg_next(i, CNTR(i))) { + CCR(i) &= ~CCR_CEN; + mb(); + CCR(i) |= CCR_CEN; + } else { + if (channel->irq_handler) + channel->irq_handler(i, + channel->data, regs); + } } else { /* * IRQ for an unregistered DMA channel: @@ -165,10 +538,10 @@ dma_irq_handler(int irq, void *dev_id, struct pt_regs *regs) return IRQ_HANDLED; } -static int __init -imx_dma_init(void) +static int __init imx_dma_init(void) { int ret; + int i; /* reset DMA module */ DCR = DCR_DRST; @@ -189,15 +562,27 @@ imx_dma_init(void) DCR = DCR_DEN; /* clear all interrupts */ - DISR = 0x3ff; + DISR = (1 << IMX_DMA_CHANNELS) - 1; /* enable interrupts */ - DIMR = 0; + DIMR = (1 << IMX_DMA_CHANNELS) - 1; + + for (i = 0; i < IMX_DMA_CHANNELS; i++) { + imx_dma_channels[i].sg = NULL; + imx_dma_channels[i].dma_num = i; + } return ret; } arch_initcall(imx_dma_init); -EXPORT_SYMBOL(imx_request_dma); -EXPORT_SYMBOL(imx_free_dma); +EXPORT_SYMBOL(imx_dma_setup_single); +EXPORT_SYMBOL(imx_dma_setup_sg); +EXPORT_SYMBOL(imx_dma_setup_handlers); +EXPORT_SYMBOL(imx_dma_enable); +EXPORT_SYMBOL(imx_dma_disable); +EXPORT_SYMBOL(imx_dma_request); +EXPORT_SYMBOL(imx_dma_free); +EXPORT_SYMBOL(imx_dma_request_by_prio); +EXPORT_SYMBOL(imx_dma_channels); diff --git a/arch/arm/mach-imx/generic.c b/arch/arm/mach-imx/generic.c index 37613ad68366..9d8331be2b58 100644 --- a/arch/arm/mach-imx/generic.c +++ b/arch/arm/mach-imx/generic.c @@ -33,6 +33,7 @@ #include #include +#include void imx_gpio_mode(int gpio_mode) { @@ -175,13 +176,25 @@ static struct resource imx_mmc_resources[] = { }, }; +static u64 imxmmmc_dmamask = 0xffffffffUL; + static struct platform_device imx_mmc_device = { .name = "imx-mmc", .id = 0, + .dev = { + .dma_mask = &imxmmmc_dmamask, + .coherent_dma_mask = 0xffffffff, + }, .num_resources = ARRAY_SIZE(imx_mmc_resources), .resource = imx_mmc_resources, }; +void __init imx_set_mmc_info(struct imxmmc_platform_data *info) +{ + imx_mmc_device.dev.platform_data = info; +} +EXPORT_SYMBOL(imx_set_mmc_info); + static struct resource imx_uart1_resources[] = { [0] = { .start = 0x00206000, diff --git a/arch/arm/mach-imx/mx1ads.c b/arch/arm/mach-imx/mx1ads.c index 8ab1b040288c..e34d0df90aed 100644 --- a/arch/arm/mach-imx/mx1ads.c +++ b/arch/arm/mach-imx/mx1ads.c @@ -25,6 +25,7 @@ #include #include +#include #include #include "generic.h" @@ -51,11 +52,28 @@ static struct platform_device *devices[] __initdata = { &cs89x0_device, }; +#ifdef CONFIG_MMC_IMX +static int mx1ads_mmc_card_present(void) +{ + /* MMC/SD Card Detect is PB 20 on MX1ADS V1.0.7 */ + return (SSR(1) & (1 << 20) ? 0 : 1); +} + +static struct imxmmc_platform_data mx1ads_mmc_info = { + .card_present = mx1ads_mmc_card_present, +}; +#endif + static void __init mx1ads_init(void) { #ifdef CONFIG_LEDS imx_gpio_mode(GPIO_PORTA | GPIO_OUT | 2); +#endif +#ifdef CONFIG_MMC_IMX + /* SD/MMC card detect */ + imx_gpio_mode(GPIO_PORTB | GPIO_GIUS | GPIO_IN | 20); + imx_set_mmc_info(&mx1ads_mmc_info); #endif platform_add_devices(devices, ARRAY_SIZE(devices)); } diff --git a/arch/arm/mach-ixp23xx/espresso.c b/arch/arm/mach-ixp23xx/espresso.c index 2327c9790416..bf688c128630 100644 --- a/arch/arm/mach-ixp23xx/espresso.c +++ b/arch/arm/mach-ixp23xx/espresso.c @@ -44,6 +44,15 @@ #include #include +static int __init espresso_pci_init(void) +{ + if (machine_is_espresso()) + ixp23xx_pci_slave_init(); + + return 0; +}; +subsys_initcall(espresso_pci_init); + static void __init espresso_init(void) { physmap_configure(0x90000000, 0x02000000, 2, NULL); diff --git a/arch/arm/mach-ixp23xx/pci.c b/arch/arm/mach-ixp23xx/pci.c index 5330ad78c1bb..ac72f94c5b4d 100644 --- a/arch/arm/mach-ixp23xx/pci.c +++ b/arch/arm/mach-ixp23xx/pci.c @@ -201,7 +201,7 @@ int clear_master_aborts(void) return 0; } -void __init ixp23xx_pci_preinit(void) +static void __init ixp23xx_pci_common_init(void) { #ifdef __ARMEB__ *IXP23XX_PCI_CONTROL |= 0x20000; /* set I/O swapping */ @@ -219,7 +219,18 @@ void __init ixp23xx_pci_preinit(void) *IXP23XX_PCI_CPP_ADDR_BITS &= ~(1 << 1); } else { *IXP23XX_PCI_CPP_ADDR_BITS |= (1 << 1); + + /* + * Enable coherency on A2 silicon. + */ + if (arch_is_coherent()) + *IXP23XX_CPP2XSI_CURR_XFER_REG3 &= ~IXP23XX_CPP2XSI_COH_OFF; } +} + +void __init ixp23xx_pci_preinit(void) +{ + ixp23xx_pci_common_init(); hook_fault_code(16+6, ixp23xx_pci_abort_handler, SIGBUS, "PCI config cycle to non-existent device"); @@ -273,3 +284,8 @@ int ixp23xx_pci_setup(int nr, struct pci_sys_data *sys) return 1; } + +void ixp23xx_pci_slave_init(void) +{ + ixp23xx_pci_common_init(); +} diff --git a/arch/arm/mach-omap1/Kconfig b/arch/arm/mach-omap1/Kconfig index 86a0f0d14345..f8d716ccc1df 100644 --- a/arch/arm/mach-omap1/Kconfig +++ b/arch/arm/mach-omap1/Kconfig @@ -69,12 +69,6 @@ config MACH_VOICEBLUE Support for Voiceblue GSM/VoIP gateway. Say Y here if you have such a board. -config MACH_NETSTAR - bool "NetStar" - depends on ARCH_OMAP1 && ARCH_OMAP15XX - help - Support for NetStar PBX. Say Y here if you have such a board. - config MACH_OMAP_PALMTE bool "Palm Tungsten E" depends on ARCH_OMAP1 && ARCH_OMAP15XX @@ -85,6 +79,20 @@ config MACH_OMAP_PALMTE informations. Say Y here if you have such a PDA, say NO otherwise. +config MACH_NOKIA770 + bool "Nokia 770" + depends on ARCH_OMAP1 && ARCH_OMAP16XX + help + Support for the Nokia 770 Internet Tablet. Say Y here if you + have such a device. + +config MACH_AMS_DELTA + bool "Amstrad E3 (Delta)" + depends on ARCH_OMAP1 && ARCH_OMAP15XX + help + Support for the Amstrad E3 (codename Delta) videophone. Say Y here + if you have such a device. + config MACH_OMAP_GENERIC bool "Generic OMAP board" depends on ARCH_OMAP1 && (ARCH_OMAP15XX || ARCH_OMAP16XX) diff --git a/arch/arm/mach-omap1/Makefile b/arch/arm/mach-omap1/Makefile index b0b00156faae..9ea719550ad3 100644 --- a/arch/arm/mach-omap1/Makefile +++ b/arch/arm/mach-omap1/Makefile @@ -3,7 +3,13 @@ # # Common support -obj-y := io.o id.o clock.o irq.o time.o mux.o serial.o devices.o +obj-y := io.o id.o clock.o irq.o mux.o serial.o devices.o + +obj-$(CONFIG_OMAP_MPU_TIMER) += time.o + +# Power Management +obj-$(CONFIG_PM) += pm.o sleep.o + led-y := leds.o # Specific board support @@ -14,8 +20,9 @@ obj-$(CONFIG_MACH_OMAP_PERSEUS2) += board-perseus2.o obj-$(CONFIG_MACH_OMAP_OSK) += board-osk.o obj-$(CONFIG_MACH_OMAP_H3) += board-h3.o obj-$(CONFIG_MACH_VOICEBLUE) += board-voiceblue.o -obj-$(CONFIG_MACH_NETSTAR) += board-netstar.o obj-$(CONFIG_MACH_OMAP_PALMTE) += board-palmte.o +obj-$(CONFIG_MACH_NOKIA770) += board-nokia770.o +obj-$(CONFIG_MACH_AMS_DELTA) += board-ams-delta.o ifeq ($(CONFIG_ARCH_OMAP15XX),y) # Innovator-1510 FPGA diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c new file mode 100644 index 000000000000..6178f046f128 --- /dev/null +++ b/arch/arm/mach-omap1/board-ams-delta.c @@ -0,0 +1,116 @@ +/* + * linux/arch/arm/mach-omap1/board-ams-delta.c + * + * Modified from board-generic.c + * + * Board specific inits for the Amstrad E3 (codename Delta) videophone + * + * Copyright (C) 2006 Jonathan McDowell + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static u8 ams_delta_latch1_reg; +static u16 ams_delta_latch2_reg; + +void ams_delta_latch1_write(u8 mask, u8 value) +{ + ams_delta_latch1_reg &= ~mask; + ams_delta_latch1_reg |= value; + *(volatile __u8 *) AMS_DELTA_LATCH1_VIRT = ams_delta_latch1_reg; +} + +void ams_delta_latch2_write(u16 mask, u16 value) +{ + ams_delta_latch2_reg &= ~mask; + ams_delta_latch2_reg |= value; + *(volatile __u16 *) AMS_DELTA_LATCH2_VIRT = ams_delta_latch2_reg; +} + +static void __init ams_delta_init_irq(void) +{ + omap1_init_common_hw(); + omap_init_irq(); + omap_gpio_init(); +} + +static struct map_desc ams_delta_io_desc[] __initdata = { + // AMS_DELTA_LATCH1 + { + .virtual = AMS_DELTA_LATCH1_VIRT, + .pfn = __phys_to_pfn(AMS_DELTA_LATCH1_PHYS), + .length = 0x01000000, + .type = MT_DEVICE + }, + // AMS_DELTA_LATCH2 + { + .virtual = AMS_DELTA_LATCH2_VIRT, + .pfn = __phys_to_pfn(AMS_DELTA_LATCH2_PHYS), + .length = 0x01000000, + .type = MT_DEVICE + }, + // AMS_DELTA_MODEM + { + .virtual = AMS_DELTA_MODEM_VIRT, + .pfn = __phys_to_pfn(AMS_DELTA_MODEM_PHYS), + .length = 0x01000000, + .type = MT_DEVICE + } +}; + +static struct omap_uart_config ams_delta_uart_config __initdata = { + .enabled_uarts = 1, +}; + +static struct omap_board_config_kernel ams_delta_config[] = { + { OMAP_TAG_UART, &ams_delta_uart_config }, +}; + +static void __init ams_delta_init(void) +{ + iotable_init(ams_delta_io_desc, ARRAY_SIZE(ams_delta_io_desc)); + + omap_board_config = ams_delta_config; + omap_board_config_size = ARRAY_SIZE(ams_delta_config); + omap_serial_init(); + + /* Clear latch2 (NAND, LCD, modem enable) */ + ams_delta_latch2_write(~0, 0); +} + +static void __init ams_delta_map_io(void) +{ + omap1_map_common_io(); +} + +MACHINE_START(AMS_DELTA, "Amstrad E3 (Delta)") + /* Maintainer: Jonathan McDowell */ + .phys_io = 0xfff00000, + .io_pg_offst = ((0xfef00000) >> 18) & 0xfffc, + .boot_params = 0x10000100, + .map_io = ams_delta_map_io, + .init_irq = ams_delta_init_irq, + .init_machine = ams_delta_init, + .timer = &omap_timer, +MACHINE_END + +EXPORT_SYMBOL(ams_delta_latch1_write); +EXPORT_SYMBOL(ams_delta_latch2_write); diff --git a/arch/arm/mach-omap1/board-generic.c b/arch/arm/mach-omap1/board-generic.c index a177e78b2b87..33d01adab1ed 100644 --- a/arch/arm/mach-omap1/board-generic.c +++ b/arch/arm/mach-omap1/board-generic.c @@ -88,7 +88,7 @@ static struct omap_board_config_kernel generic_config[] = { static void __init omap_generic_init(void) { #ifdef CONFIG_ARCH_OMAP15XX - if (cpu_is_omap1510()) { + if (cpu_is_omap15xx()) { generic_config[0].data = &generic1510_usb_config; } #endif diff --git a/arch/arm/mach-omap1/board-h2.c b/arch/arm/mach-omap1/board-h2.c index 89f0cc74a519..cd3a06dfc0a8 100644 --- a/arch/arm/mach-omap1/board-h2.c +++ b/arch/arm/mach-omap1/board-h2.c @@ -24,7 +24,9 @@ #include #include #include +#include #include +#include #include #include @@ -35,12 +37,55 @@ #include #include #include +#include #include +#include #include +#include +#include extern int omap_gpio_init(void); -static struct mtd_partition h2_partitions[] = { +static int h2_keymap[] = { + KEY(0, 0, KEY_LEFT), + KEY(0, 1, KEY_RIGHT), + KEY(0, 2, KEY_3), + KEY(0, 3, KEY_F10), + KEY(0, 4, KEY_F5), + KEY(0, 5, KEY_9), + KEY(1, 0, KEY_DOWN), + KEY(1, 1, KEY_UP), + KEY(1, 2, KEY_2), + KEY(1, 3, KEY_F9), + KEY(1, 4, KEY_F7), + KEY(1, 5, KEY_0), + KEY(2, 0, KEY_ENTER), + KEY(2, 1, KEY_6), + KEY(2, 2, KEY_1), + KEY(2, 3, KEY_F2), + KEY(2, 4, KEY_F6), + KEY(2, 5, KEY_HOME), + KEY(3, 0, KEY_8), + KEY(3, 1, KEY_5), + KEY(3, 2, KEY_F12), + KEY(3, 3, KEY_F3), + KEY(3, 4, KEY_F8), + KEY(3, 5, KEY_END), + KEY(4, 0, KEY_7), + KEY(4, 1, KEY_4), + KEY(4, 2, KEY_F11), + KEY(4, 3, KEY_F1), + KEY(4, 4, KEY_F4), + KEY(4, 5, KEY_ESC), + KEY(5, 0, KEY_F13), + KEY(5, 1, KEY_F14), + KEY(5, 2, KEY_F15), + KEY(5, 3, KEY_F16), + KEY(5, 4, KEY_SLEEP), + 0 +}; + +static struct mtd_partition h2_nor_partitions[] = { /* bootloader (U-Boot, etc) in first sector */ { .name = "bootloader", @@ -71,26 +116,26 @@ static struct mtd_partition h2_partitions[] = { } }; -static struct flash_platform_data h2_flash_data = { +static struct flash_platform_data h2_nor_data = { .map_name = "cfi_probe", .width = 2, - .parts = h2_partitions, - .nr_parts = ARRAY_SIZE(h2_partitions), + .parts = h2_nor_partitions, + .nr_parts = ARRAY_SIZE(h2_nor_partitions), }; -static struct resource h2_flash_resource = { +static struct resource h2_nor_resource = { /* This is on CS3, wherever it's mapped */ .flags = IORESOURCE_MEM, }; -static struct platform_device h2_flash_device = { +static struct platform_device h2_nor_device = { .name = "omapflash", .id = 0, .dev = { - .platform_data = &h2_flash_data, + .platform_data = &h2_nor_data, }, .num_resources = 1, - .resource = &h2_flash_resource, + .resource = &h2_nor_resource, }; static struct resource h2_smc91x_resources[] = { @@ -113,9 +158,119 @@ static struct platform_device h2_smc91x_device = { .resource = h2_smc91x_resources, }; +static struct resource h2_kp_resources[] = { + [0] = { + .start = INT_KEYBOARD, + .end = INT_KEYBOARD, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct omap_kp_platform_data h2_kp_data = { + .rows = 8, + .cols = 8, + .keymap = h2_keymap, + .rep = 1, +}; + +static struct platform_device h2_kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &h2_kp_data, + }, + .num_resources = ARRAY_SIZE(h2_kp_resources), + .resource = h2_kp_resources, +}; + +#define H2_IRDA_FIRSEL_GPIO_PIN 17 + +#if defined(CONFIG_OMAP_IR) || defined(CONFIG_OMAP_IR_MODULE) +static int h2_transceiver_mode(struct device *dev, int state) +{ + if (state & IR_SIRMODE) + omap_set_gpio_dataout(H2_IRDA_FIRSEL_GPIO_PIN, 0); + else /* MIR/FIR */ + omap_set_gpio_dataout(H2_IRDA_FIRSEL_GPIO_PIN, 1); + + return 0; +} +#endif + +static struct omap_irda_config h2_irda_data = { + .transceiver_cap = IR_SIRMODE | IR_MIRMODE | IR_FIRMODE, + .rx_channel = OMAP_DMA_UART3_RX, + .tx_channel = OMAP_DMA_UART3_TX, + .dest_start = UART3_THR, + .src_start = UART3_RHR, + .tx_trigger = 0, + .rx_trigger = 0, +}; + +static struct resource h2_irda_resources[] = { + [0] = { + .start = INT_UART3, + .end = INT_UART3, + .flags = IORESOURCE_IRQ, + }, +}; +static struct platform_device h2_irda_device = { + .name = "omapirda", + .id = 0, + .dev = { + .platform_data = &h2_irda_data, + }, + .num_resources = ARRAY_SIZE(h2_irda_resources), + .resource = h2_irda_resources, +}; + +static struct platform_device h2_lcd_device = { + .name = "lcd_h2", + .id = -1, +}; + +static struct omap_mcbsp_reg_cfg mcbsp_regs = { + .spcr2 = FREE | FRST | GRST | XRST | XINTM(3), + .spcr1 = RINTM(3) | RRST, + .rcr2 = RPHASE | RFRLEN2(OMAP_MCBSP_WORD_8) | + RWDLEN2(OMAP_MCBSP_WORD_16) | RDATDLY(1), + .rcr1 = RFRLEN1(OMAP_MCBSP_WORD_8) | RWDLEN1(OMAP_MCBSP_WORD_16), + .xcr2 = XPHASE | XFRLEN2(OMAP_MCBSP_WORD_8) | + XWDLEN2(OMAP_MCBSP_WORD_16) | XDATDLY(1) | XFIG, + .xcr1 = XFRLEN1(OMAP_MCBSP_WORD_8) | XWDLEN1(OMAP_MCBSP_WORD_16), + .srgr1 = FWID(15), + .srgr2 = GSYNC | CLKSP | FSGM | FPER(31), + + .pcr0 = CLKXM | CLKRM | FSXP | FSRP | CLKXP | CLKRP, + //.pcr0 = CLKXP | CLKRP, /* mcbsp: slave */ +}; + +static struct omap_alsa_codec_config alsa_config = { + .name = "H2 TSC2101", + .mcbsp_regs_alsa = &mcbsp_regs, + .codec_configure_dev = NULL, // tsc2101_configure, + .codec_set_samplerate = NULL, // tsc2101_set_samplerate, + .codec_clock_setup = NULL, // tsc2101_clock_setup, + .codec_clock_on = NULL, // tsc2101_clock_on, + .codec_clock_off = NULL, // tsc2101_clock_off, + .get_default_samplerate = NULL, // tsc2101_get_default_samplerate, +}; + +static struct platform_device h2_mcbsp1_device = { + .name = "omap_alsa_mcbsp", + .id = 1, + .dev = { + .platform_data = &alsa_config, + }, +}; + static struct platform_device *h2_devices[] __initdata = { - &h2_flash_device, + &h2_nor_device, &h2_smc91x_device, + &h2_irda_device, + &h2_kp_device, + &h2_lcd_device, + &h2_mcbsp1_device, }; static void __init h2_init_smc91x(void) @@ -164,7 +319,6 @@ static struct omap_uart_config h2_uart_config __initdata = { }; static struct omap_lcd_config h2_lcd_config __initdata = { - .panel_name = "h2", .ctrl_name = "internal", }; @@ -177,16 +331,34 @@ static struct omap_board_config_kernel h2_config[] = { static void __init h2_init(void) { - /* NOTE: revC boards support NAND-boot, which can put NOR on CS2B - * and NAND (either 16bit or 8bit) on CS3. + /* Here we assume the NOR boot config: NOR on CS3 (possibly swapped + * to address 0 by a dip switch), NAND on CS2B. The NAND driver will + * notice whether a NAND chip is enabled at probe time. + * + * FIXME revC boards (and H3) support NAND-boot, with a dip switch to + * put NOR on CS2B and NAND (which on H2 may be 16bit) on CS3. Try + * detecting that in code here, to avoid probing every possible flash + * configuration... */ - h2_flash_resource.end = h2_flash_resource.start = omap_cs3_phys(); - h2_flash_resource.end += SZ_32M - 1; + h2_nor_resource.end = h2_nor_resource.start = omap_cs3_phys(); + h2_nor_resource.end += SZ_32M - 1; + + omap_cfg_reg(L3_1610_FLASH_CS2B_OE); + omap_cfg_reg(M8_1610_FLASH_CS2B_WE); /* MMC: card detect and WP */ // omap_cfg_reg(U19_ARMIO1); /* CD */ omap_cfg_reg(BALLOUT_V8_ARMIO3); /* WP */ + /* Irda */ +#if defined(CONFIG_OMAP_IR) || defined(CONFIG_OMAP_IR_MODULE) + omap_writel(omap_readl(FUNC_MUX_CTRL_A) | 7, FUNC_MUX_CTRL_A); + if (!(omap_request_gpio(H2_IRDA_FIRSEL_GPIO_PIN))) { + omap_set_gpio_direction(H2_IRDA_FIRSEL_GPIO_PIN, 0); + h2_irda_data.transceiver_mode = h2_transceiver_mode; + } +#endif + platform_add_devices(h2_devices, ARRAY_SIZE(h2_devices)); omap_board_config = h2_config; omap_board_config_size = ARRAY_SIZE(h2_config); diff --git a/arch/arm/mach-omap1/board-h3.c b/arch/arm/mach-omap1/board-h3.c index d9f386265996..4b8d0ec73cb7 100644 --- a/arch/arm/mach-omap1/board-h3.c +++ b/arch/arm/mach-omap1/board-h3.c @@ -21,8 +21,11 @@ #include #include #include +#include #include +#include #include +#include #include #include @@ -33,15 +36,59 @@ #include #include +#include #include #include #include +#include #include +#include +#include #include extern int omap_gpio_init(void); -static struct mtd_partition h3_partitions[] = { +static int h3_keymap[] = { + KEY(0, 0, KEY_LEFT), + KEY(0, 1, KEY_RIGHT), + KEY(0, 2, KEY_3), + KEY(0, 3, KEY_F10), + KEY(0, 4, KEY_F5), + KEY(0, 5, KEY_9), + KEY(1, 0, KEY_DOWN), + KEY(1, 1, KEY_UP), + KEY(1, 2, KEY_2), + KEY(1, 3, KEY_F9), + KEY(1, 4, KEY_F7), + KEY(1, 5, KEY_0), + KEY(2, 0, KEY_ENTER), + KEY(2, 1, KEY_6), + KEY(2, 2, KEY_1), + KEY(2, 3, KEY_F2), + KEY(2, 4, KEY_F6), + KEY(2, 5, KEY_HOME), + KEY(3, 0, KEY_8), + KEY(3, 1, KEY_5), + KEY(3, 2, KEY_F12), + KEY(3, 3, KEY_F3), + KEY(3, 4, KEY_F8), + KEY(3, 5, KEY_END), + KEY(4, 0, KEY_7), + KEY(4, 1, KEY_4), + KEY(4, 2, KEY_F11), + KEY(4, 3, KEY_F1), + KEY(4, 4, KEY_F4), + KEY(4, 5, KEY_ESC), + KEY(5, 0, KEY_F13), + KEY(5, 1, KEY_F14), + KEY(5, 2, KEY_F15), + KEY(5, 3, KEY_F16), + KEY(5, 4, KEY_SLEEP), + 0 +}; + + +static struct mtd_partition nor_partitions[] = { /* bootloader (U-Boot, etc) in first sector */ { .name = "bootloader", @@ -72,26 +119,80 @@ static struct mtd_partition h3_partitions[] = { } }; -static struct flash_platform_data h3_flash_data = { +static struct flash_platform_data nor_data = { .map_name = "cfi_probe", .width = 2, - .parts = h3_partitions, - .nr_parts = ARRAY_SIZE(h3_partitions), + .parts = nor_partitions, + .nr_parts = ARRAY_SIZE(nor_partitions), }; -static struct resource h3_flash_resource = { +static struct resource nor_resource = { /* This is on CS3, wherever it's mapped */ .flags = IORESOURCE_MEM, }; -static struct platform_device flash_device = { +static struct platform_device nor_device = { .name = "omapflash", .id = 0, .dev = { - .platform_data = &h3_flash_data, + .platform_data = &nor_data, }, .num_resources = 1, - .resource = &h3_flash_resource, + .resource = &nor_resource, +}; + +static struct mtd_partition nand_partitions[] = { +#if 0 + /* REVISIT: enable these partitions if you make NAND BOOT work */ + { + .name = "xloader", + .offset = 0, + .size = 64 * 1024, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { + .name = "bootloader", + .offset = MTDPART_OFS_APPEND, + .size = 256 * 1024, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { + .name = "params", + .offset = MTDPART_OFS_APPEND, + .size = 192 * 1024, + }, + { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = 2 * SZ_1M, + }, +#endif + { + .name = "filesystem", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + }, +}; + +/* dip switches control NAND chip access: 8 bit, 16 bit, or neither */ +static struct nand_platform_data nand_data = { + .options = NAND_SAMSUNG_LP_OPTIONS, + .parts = nand_partitions, + .nr_parts = ARRAY_SIZE(nand_partitions), +}; + +static struct resource nand_resource = { + .flags = IORESOURCE_MEM, +}; + +static struct platform_device nand_device = { + .name = "omapnand", + .id = 0, + .dev = { + .platform_data = &nand_data, + }, + .num_resources = 1, + .resource = &nand_resource, }; static struct resource smc91x_resources[] = { @@ -138,10 +239,136 @@ static struct platform_device intlat_device = { .resource = intlat_resources, }; +static struct resource h3_kp_resources[] = { + [0] = { + .start = INT_KEYBOARD, + .end = INT_KEYBOARD, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct omap_kp_platform_data h3_kp_data = { + .rows = 8, + .cols = 8, + .keymap = h3_keymap, + .rep = 1, +}; + +static struct platform_device h3_kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &h3_kp_data, + }, + .num_resources = ARRAY_SIZE(h3_kp_resources), + .resource = h3_kp_resources, +}; + + +/* Select between the IrDA and aGPS module + */ +static int h3_select_irda(struct device *dev, int state) +{ + unsigned char expa; + int err = 0; + + if ((err = read_gpio_expa(&expa, 0x26))) { + printk(KERN_ERR "Error reading from I/O EXPANDER \n"); + return err; + } + + /* 'P6' enable/disable IRDA_TX and IRDA_RX */ + if (state & IR_SEL) { /* IrDA */ + if ((err = write_gpio_expa(expa | 0x40, 0x26))) { + printk(KERN_ERR "Error writing to I/O EXPANDER \n"); + return err; + } + } else { + if ((err = write_gpio_expa(expa & ~0x40, 0x26))) { + printk(KERN_ERR "Error writing to I/O EXPANDER \n"); + return err; + } + } + return err; +} + +static void set_trans_mode(void *data) +{ + int *mode = data; + unsigned char expa; + int err = 0; + + if ((err = read_gpio_expa(&expa, 0x27)) != 0) { + printk(KERN_ERR "Error reading from I/O expander\n"); + } + + expa &= ~0x03; + + if (*mode & IR_SIRMODE) { + expa |= 0x01; + } else { /* MIR/FIR */ + expa |= 0x03; + } + + if ((err = write_gpio_expa(expa, 0x27)) != 0) { + printk(KERN_ERR "Error writing to I/O expander\n"); + } +} + +static int h3_transceiver_mode(struct device *dev, int mode) +{ + struct omap_irda_config *irda_config = dev->platform_data; + + cancel_delayed_work(&irda_config->gpio_expa); + PREPARE_WORK(&irda_config->gpio_expa, set_trans_mode, &mode); + schedule_work(&irda_config->gpio_expa); + + return 0; +} + +static struct omap_irda_config h3_irda_data = { + .transceiver_cap = IR_SIRMODE | IR_MIRMODE | IR_FIRMODE, + .transceiver_mode = h3_transceiver_mode, + .select_irda = h3_select_irda, + .rx_channel = OMAP_DMA_UART3_RX, + .tx_channel = OMAP_DMA_UART3_TX, + .dest_start = UART3_THR, + .src_start = UART3_RHR, + .tx_trigger = 0, + .rx_trigger = 0, +}; + +static struct resource h3_irda_resources[] = { + [0] = { + .start = INT_UART3, + .end = INT_UART3, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device h3_irda_device = { + .name = "omapirda", + .id = 0, + .dev = { + .platform_data = &h3_irda_data, + }, + .num_resources = ARRAY_SIZE(h3_irda_resources), + .resource = h3_irda_resources, +}; + +static struct platform_device h3_lcd_device = { + .name = "lcd_h3", + .id = -1, +}; + static struct platform_device *devices[] __initdata = { - &flash_device, + &nor_device, + &nand_device, &smc91x_device, &intlat_device, + &h3_irda_device, + &h3_kp_device, + &h3_lcd_device, }; static struct omap_usb_config h3_usb_config __initdata = { @@ -171,7 +398,6 @@ static struct omap_uart_config h3_uart_config __initdata = { }; static struct omap_lcd_config h3_lcd_config __initdata = { - .panel_name = "h3", .ctrl_name = "internal", }; @@ -182,11 +408,36 @@ static struct omap_board_config_kernel h3_config[] = { { OMAP_TAG_LCD, &h3_lcd_config }, }; +#define H3_NAND_RB_GPIO_PIN 10 + +static int nand_dev_ready(struct nand_platform_data *data) +{ + return omap_get_gpio_datain(H3_NAND_RB_GPIO_PIN); +} + static void __init h3_init(void) { - h3_flash_resource.end = h3_flash_resource.start = omap_cs3_phys(); - h3_flash_resource.end += OMAP_CS3_SIZE - 1; - (void) platform_add_devices(devices, ARRAY_SIZE(devices)); + /* Here we assume the NOR boot config: NOR on CS3 (possibly swapped + * to address 0 by a dip switch), NAND on CS2B. The NAND driver will + * notice whether a NAND chip is enabled at probe time. + * + * H3 support NAND-boot, with a dip switch to put NOR on CS2B and NAND + * (which on H2 may be 16bit) on CS3. Try detecting that in code here, + * to avoid probing every possible flash configuration... + */ + nor_resource.end = nor_resource.start = omap_cs3_phys(); + nor_resource.end += SZ_32M - 1; + + nand_resource.end = nand_resource.start = OMAP_CS2B_PHYS; + nand_resource.end += SZ_4K - 1; + if (!(omap_request_gpio(H3_NAND_RB_GPIO_PIN))) + nand_data.dev_ready = nand_dev_ready; + + /* GPIO10 Func_MUX_CTRL reg bit 29:27, Configure V2 to mode1 as GPIO */ + /* GPIO10 pullup/down register, Enable pullup on GPIO10 */ + omap_cfg_reg(V2_1710_GPIO10); + + platform_add_devices(devices, ARRAY_SIZE(devices)); omap_board_config = h3_config; omap_board_config_size = ARRAY_SIZE(h3_config); omap_serial_init(); diff --git a/arch/arm/mach-omap1/board-innovator.c b/arch/arm/mach-omap1/board-innovator.c index a04e4332915e..e90c137a4cf3 100644 --- a/arch/arm/mach-omap1/board-innovator.c +++ b/arch/arm/mach-omap1/board-innovator.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -34,8 +35,22 @@ #include #include #include +#include #include +static int innovator_keymap[] = { + KEY(0, 0, KEY_F1), + KEY(0, 3, KEY_DOWN), + KEY(1, 1, KEY_F2), + KEY(1, 2, KEY_RIGHT), + KEY(2, 0, KEY_F3), + KEY(2, 1, KEY_F4), + KEY(2, 2, KEY_UP), + KEY(3, 2, KEY_ENTER), + KEY(3, 3, KEY_LEFT), + 0 +}; + static struct mtd_partition innovator_partitions[] = { /* bootloader (U-Boot, etc) in first sector */ { @@ -97,6 +112,31 @@ static struct platform_device innovator_flash_device = { .resource = &innovator_flash_resource, }; +static struct resource innovator_kp_resources[] = { + [0] = { + .start = INT_KEYBOARD, + .end = INT_KEYBOARD, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct omap_kp_platform_data innovator_kp_data = { + .rows = 8, + .cols = 8, + .keymap = innovator_keymap, +}; + +static struct platform_device innovator_kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &innovator_kp_data, + }, + .num_resources = ARRAY_SIZE(innovator_kp_resources), + .resource = innovator_kp_resources, +}; + + #ifdef CONFIG_ARCH_OMAP15XX /* Only FPGA needs to be mapped here. All others are done with ioremap */ @@ -129,9 +169,16 @@ static struct platform_device innovator1510_smc91x_device = { .resource = innovator1510_smc91x_resources, }; +static struct platform_device innovator1510_lcd_device = { + .name = "lcd_inn1510", + .id = -1, +}; + static struct platform_device *innovator1510_devices[] __initdata = { &innovator_flash_device, &innovator1510_smc91x_device, + &innovator_kp_device, + &innovator1510_lcd_device, }; #endif /* CONFIG_ARCH_OMAP15XX */ @@ -158,9 +205,16 @@ static struct platform_device innovator1610_smc91x_device = { .resource = innovator1610_smc91x_resources, }; +static struct platform_device innovator1610_lcd_device = { + .name = "inn1610_lcd", + .id = -1, +}; + static struct platform_device *innovator1610_devices[] __initdata = { &innovator_flash_device, &innovator1610_smc91x_device, + &innovator_kp_device, + &innovator1610_lcd_device, }; #endif /* CONFIG_ARCH_OMAP16XX */ @@ -206,7 +260,6 @@ static struct omap_usb_config innovator1510_usb_config __initdata = { }; static struct omap_lcd_config innovator1510_lcd_config __initdata = { - .panel_name = "inn1510", .ctrl_name = "internal", }; #endif @@ -228,7 +281,6 @@ static struct omap_usb_config h2_usb_config __initdata = { }; static struct omap_lcd_config innovator1610_lcd_config __initdata = { - .panel_name = "inn1610", .ctrl_name = "internal", }; #endif diff --git a/arch/arm/mach-omap1/board-netstar.c b/arch/arm/mach-omap1/board-netstar.c deleted file mode 100644 index 7520e602d7a2..000000000000 --- a/arch/arm/mach-omap1/board-netstar.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Modified from board-generic.c - * - * Copyright (C) 2004 2N Telekomunikace, Ladislav Michl - * - * Code for Netstar OMAP board. - * - * 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 -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -extern void __init omap_init_time(void); -extern int omap_gpio_init(void); - -static struct resource netstar_smc91x_resources[] = { - [0] = { - .start = OMAP_CS1_PHYS + 0x300, - .end = OMAP_CS1_PHYS + 0x300 + 16, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = OMAP_GPIO_IRQ(8), - .end = OMAP_GPIO_IRQ(8), - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device netstar_smc91x_device = { - .name = "smc91x", - .id = 0, - .num_resources = ARRAY_SIZE(netstar_smc91x_resources), - .resource = netstar_smc91x_resources, -}; - -static struct platform_device *netstar_devices[] __initdata = { - &netstar_smc91x_device, -}; - -static struct omap_uart_config netstar_uart_config __initdata = { - .enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)), -}; - -static struct omap_board_config_kernel netstar_config[] = { - { OMAP_TAG_UART, &netstar_uart_config }, -}; - -static void __init netstar_init_irq(void) -{ - omap1_init_common_hw(); - omap_init_irq(); - omap_gpio_init(); -} - -static void __init netstar_init(void) -{ - /* green LED */ - omap_request_gpio(4); - omap_set_gpio_direction(4, 0); - /* smc91x reset */ - omap_request_gpio(7); - omap_set_gpio_direction(7, 0); - omap_set_gpio_dataout(7, 1); - udelay(2); /* wait at least 100ns */ - omap_set_gpio_dataout(7, 0); - mdelay(50); /* 50ms until PHY ready */ - /* smc91x interrupt pin */ - omap_request_gpio(8); - - omap_request_gpio(12); - omap_request_gpio(13); - omap_request_gpio(14); - omap_request_gpio(15); - set_irq_type(OMAP_GPIO_IRQ(12), IRQT_FALLING); - set_irq_type(OMAP_GPIO_IRQ(13), IRQT_FALLING); - set_irq_type(OMAP_GPIO_IRQ(14), IRQT_FALLING); - set_irq_type(OMAP_GPIO_IRQ(15), IRQT_FALLING); - - platform_add_devices(netstar_devices, ARRAY_SIZE(netstar_devices)); - - /* Switch on green LED */ - omap_set_gpio_dataout(4, 0); - /* Switch off red LED */ - omap_writeb(0x00, OMAP_LPG1_PMR); /* Disable clock */ - omap_writeb(0x80, OMAP_LPG1_LCR); - - omap_board_config = netstar_config; - omap_board_config_size = ARRAY_SIZE(netstar_config); - omap_serial_init(); -} - -static void __init netstar_map_io(void) -{ - omap1_map_common_io(); -} - -#define MACHINE_PANICED 1 -#define MACHINE_REBOOTING 2 -#define MACHINE_REBOOT 4 -static unsigned long machine_state; - -static int panic_event(struct notifier_block *this, unsigned long event, - void *ptr) -{ - if (test_and_set_bit(MACHINE_PANICED, &machine_state)) - return NOTIFY_DONE; - - /* Switch off green LED */ - omap_set_gpio_dataout(4, 1); - /* Flash red LED */ - omap_writeb(0x78, OMAP_LPG1_LCR); - omap_writeb(0x01, OMAP_LPG1_PMR); /* Enable clock */ - - return NOTIFY_DONE; -} - -static struct notifier_block panic_block = { - .notifier_call = panic_event, -}; - -static int __init netstar_late_init(void) -{ - /* TODO: Setup front panel switch here */ - - /* Setup panic notifier */ - atomic_notifier_chain_register(&panic_notifier_list, &panic_block); - - return 0; -} - -postcore_initcall(netstar_late_init); - -MACHINE_START(NETSTAR, "NetStar OMAP5910") - /* Maintainer: Ladislav Michl */ - .phys_io = 0xfff00000, - .io_pg_offst = ((0xfef00000) >> 18) & 0xfffc, - .boot_params = 0x10000100, - .map_io = netstar_map_io, - .init_irq = netstar_init_irq, - .init_machine = netstar_init, - .timer = &omap_timer, -MACHINE_END diff --git a/arch/arm/mach-omap1/board-nokia770.c b/arch/arm/mach-omap1/board-nokia770.c new file mode 100644 index 000000000000..02b980d77b12 --- /dev/null +++ b/arch/arm/mach-omap1/board-nokia770.c @@ -0,0 +1,268 @@ +/* + * linux/arch/arm/mach-omap1/board-nokia770.c + * + * Modified from board-generic.c + * + * 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 +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void __init omap_nokia770_init_irq(void) +{ + /* On Nokia 770, the SleepX signal is masked with an + * MPUIO line by default. It has to be unmasked for it + * to become functional */ + + /* SleepX mask direction */ + omap_writew((omap_readw(0xfffb5008) & ~2), 0xfffb5008); + /* Unmask SleepX signal */ + omap_writew((omap_readw(0xfffb5004) & ~2), 0xfffb5004); + + omap1_init_common_hw(); + omap_init_irq(); +} + +static int nokia770_keymap[] = { + KEY(0, 1, GROUP_0 | KEY_UP), + KEY(0, 2, GROUP_1 | KEY_F5), + KEY(1, 0, GROUP_0 | KEY_LEFT), + KEY(1, 1, GROUP_0 | KEY_ENTER), + KEY(1, 2, GROUP_0 | KEY_RIGHT), + KEY(2, 0, GROUP_1 | KEY_ESC), + KEY(2, 1, GROUP_0 | KEY_DOWN), + KEY(2, 2, GROUP_1 | KEY_F4), + KEY(3, 0, GROUP_2 | KEY_F7), + KEY(3, 1, GROUP_2 | KEY_F8), + KEY(3, 2, GROUP_2 | KEY_F6), + 0 +}; + +static struct resource nokia770_kp_resources[] = { + [0] = { + .start = INT_KEYBOARD, + .end = INT_KEYBOARD, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct omap_kp_platform_data nokia770_kp_data = { + .rows = 8, + .cols = 8, + .keymap = nokia770_keymap +}; + +static struct platform_device nokia770_kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &nokia770_kp_data, + }, + .num_resources = ARRAY_SIZE(nokia770_kp_resources), + .resource = nokia770_kp_resources, +}; + +static struct platform_device *nokia770_devices[] __initdata = { + &nokia770_kp_device, +}; + +static struct ads7846_platform_data nokia770_ads7846_platform_data __initdata = { + .x_max = 0x0fff, + .y_max = 0x0fff, + .x_plate_ohms = 180, + .pressure_max = 255, + .debounce_max = 10, + .debounce_tol = 3, +}; + +static struct spi_board_info nokia770_spi_board_info[] __initdata = { + [0] = { + .modalias = "lcd_lph8923", + .bus_num = 2, + .chip_select = 3, + .max_speed_hz = 12000000, + }, + [1] = { + .modalias = "ads7846", + .bus_num = 2, + .chip_select = 0, + .max_speed_hz = 2500000, + .irq = OMAP_GPIO_IRQ(15), + .platform_data = &nokia770_ads7846_platform_data, + }, +}; + + +/* assume no Mini-AB port */ + +static struct omap_usb_config nokia770_usb_config __initdata = { + .otg = 1, + .register_host = 1, + .register_dev = 1, + .hmc_mode = 16, + .pins[0] = 6, +}; + +static struct omap_mmc_config nokia770_mmc_config __initdata = { + .mmc[0] = { + .enabled = 0, + .wire4 = 0, + .wp_pin = -1, + .power_pin = -1, + .switch_pin = -1, + }, + .mmc[1] = { + .enabled = 0, + .wire4 = 0, + .wp_pin = -1, + .power_pin = -1, + .switch_pin = -1, + }, +}; + +static struct omap_board_config_kernel nokia770_config[] = { + { OMAP_TAG_USB, NULL }, + { OMAP_TAG_MMC, &nokia770_mmc_config }, +}; + +/* + * audio power control + */ +#define HEADPHONE_GPIO 14 +#define AMPLIFIER_CTRL_GPIO 58 + +static struct clk *dspxor_ck; +static DECLARE_MUTEX(audio_pwr_sem); +/* + * audio_pwr_state + * +--+-------------------------+---------------------------------------+ + * |-1|down |power-up request -> 0 | + * +--+-------------------------+---------------------------------------+ + * | 0|up |power-down(1) request -> 1 | + * | | |power-down(2) request -> (ignore) | + * +--+-------------------------+---------------------------------------+ + * | 1|up, |power-up request -> 0 | + * | |received down(1) request |power-down(2) request -> -1 | + * +--+-------------------------+---------------------------------------+ + */ +static int audio_pwr_state = -1; + +/* + * audio_pwr_up / down should be called under audio_pwr_sem + */ +static void nokia770_audio_pwr_up(void) +{ + clk_enable(dspxor_ck); + + /* Turn on codec */ + tlv320aic23_power_up(); + + if (omap_get_gpio_datain(HEADPHONE_GPIO)) + /* HP not connected, turn on amplifier */ + omap_set_gpio_dataout(AMPLIFIER_CTRL_GPIO, 1); + else + /* HP connected, do not turn on amplifier */ + printk("HP connected\n"); +} + +static void codec_delayed_power_down(void *arg) +{ + down(&audio_pwr_sem); + if (audio_pwr_state == -1) + tlv320aic23_power_down(); + clk_disable(dspxor_ck); + up(&audio_pwr_sem); +} + +static DECLARE_WORK(codec_power_down_work, codec_delayed_power_down, NULL); + +static void nokia770_audio_pwr_down(void) +{ + /* Turn off amplifier */ + omap_set_gpio_dataout(AMPLIFIER_CTRL_GPIO, 0); + + /* Turn off codec: schedule delayed work */ + schedule_delayed_work(&codec_power_down_work, HZ / 20); /* 50ms */ +} + +void nokia770_audio_pwr_up_request(int stage) +{ + down(&audio_pwr_sem); + if (audio_pwr_state == -1) + nokia770_audio_pwr_up(); + /* force audio_pwr_state = 0, even if it was 1. */ + audio_pwr_state = 0; + up(&audio_pwr_sem); +} + +void nokia770_audio_pwr_down_request(int stage) +{ + down(&audio_pwr_sem); + switch (stage) { + case 1: + if (audio_pwr_state == 0) + audio_pwr_state = 1; + break; + case 2: + if (audio_pwr_state == 1) { + nokia770_audio_pwr_down(); + audio_pwr_state = -1; + } + break; + } + up(&audio_pwr_sem); +} + +static void __init omap_nokia770_init(void) +{ + nokia770_config[0].data = &nokia770_usb_config; + + platform_add_devices(nokia770_devices, ARRAY_SIZE(nokia770_devices)); + spi_register_board_info(nokia770_spi_board_info, + ARRAY_SIZE(nokia770_spi_board_info)); + omap_board_config = nokia770_config; + omap_board_config_size = ARRAY_SIZE(nokia770_config); + omap_serial_init(); + omap_dsp_audio_pwr_up_request = nokia770_audio_pwr_up_request; + omap_dsp_audio_pwr_down_request = nokia770_audio_pwr_down_request; + dspxor_ck = clk_get(0, "dspxor_ck"); +} + +static void __init omap_nokia770_map_io(void) +{ + omap1_map_common_io(); +} + +MACHINE_START(NOKIA770, "Nokia 770") + .phys_io = 0xfff00000, + .io_pg_offst = ((0xfef00000) >> 18) & 0xfffc, + .boot_params = 0x10000100, + .map_io = omap_nokia770_map_io, + .init_irq = omap_nokia770_init_irq, + .init_machine = omap_nokia770_init, + .timer = &omap_timer, +MACHINE_END diff --git a/arch/arm/mach-omap1/board-osk.c b/arch/arm/mach-omap1/board-osk.c index 543fa136106d..1160093e8ef6 100644 --- a/arch/arm/mach-omap1/board-osk.c +++ b/arch/arm/mach-omap1/board-osk.c @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -44,7 +45,24 @@ #include #include #include +#include #include +#include +#include + +static int osk_keymap[] = { + KEY(0, 0, KEY_F1), + KEY(0, 3, KEY_UP), + KEY(1, 1, KEY_LEFTCTRL), + KEY(1, 2, KEY_LEFT), + KEY(2, 0, KEY_SPACE), + KEY(2, 1, KEY_ESC), + KEY(2, 2, KEY_DOWN), + KEY(3, 2, KEY_ENTER), + KEY(3, 3, KEY_RIGHT), + 0 +}; + static struct mtd_partition osk_partitions[] = { /* bootloader (U-Boot, etc) in first sector */ @@ -133,9 +151,69 @@ static struct platform_device osk5912_cf_device = { .resource = osk5912_cf_resources, }; +#define DEFAULT_BITPERSAMPLE 16 + +static struct omap_mcbsp_reg_cfg mcbsp_regs = { + .spcr2 = FREE | FRST | GRST | XRST | XINTM(3), + .spcr1 = RINTM(3) | RRST, + .rcr2 = RPHASE | RFRLEN2(OMAP_MCBSP_WORD_8) | + RWDLEN2(OMAP_MCBSP_WORD_16) | RDATDLY(0), + .rcr1 = RFRLEN1(OMAP_MCBSP_WORD_8) | RWDLEN1(OMAP_MCBSP_WORD_16), + .xcr2 = XPHASE | XFRLEN2(OMAP_MCBSP_WORD_8) | + XWDLEN2(OMAP_MCBSP_WORD_16) | XDATDLY(0) | XFIG, + .xcr1 = XFRLEN1(OMAP_MCBSP_WORD_8) | XWDLEN1(OMAP_MCBSP_WORD_16), + .srgr1 = FWID(DEFAULT_BITPERSAMPLE - 1), + .srgr2 = GSYNC | CLKSP | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1), + /*.pcr0 = FSXM | FSRM | CLKXM | CLKRM | CLKXP | CLKRP,*/ /* mcbsp: master */ + .pcr0 = CLKXP | CLKRP, /* mcbsp: slave */ +}; + +static struct omap_alsa_codec_config alsa_config = { + .name = "OSK AIC23", + .mcbsp_regs_alsa = &mcbsp_regs, + .codec_configure_dev = NULL, // aic23_configure, + .codec_set_samplerate = NULL, // aic23_set_samplerate, + .codec_clock_setup = NULL, // aic23_clock_setup, + .codec_clock_on = NULL, // aic23_clock_on, + .codec_clock_off = NULL, // aic23_clock_off, + .get_default_samplerate = NULL, // aic23_get_default_samplerate, +}; + static struct platform_device osk5912_mcbsp1_device = { - .name = "omap_mcbsp", - .id = 1, + .name = "omap_alsa_mcbsp", + .id = 1, + .dev = { + .platform_data = &alsa_config, + }, +}; + +static struct resource osk5912_kp_resources[] = { + [0] = { + .start = INT_KEYBOARD, + .end = INT_KEYBOARD, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct omap_kp_platform_data osk_kp_data = { + .rows = 8, + .cols = 8, + .keymap = osk_keymap, +}; + +static struct platform_device osk5912_kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &osk_kp_data, + }, + .num_resources = ARRAY_SIZE(osk5912_kp_resources), + .resource = osk5912_kp_resources, +}; + +static struct platform_device osk5912_lcd_device = { + .name = "lcd_osk", + .id = -1, }; static struct platform_device *osk5912_devices[] __initdata = { @@ -143,6 +221,8 @@ static struct platform_device *osk5912_devices[] __initdata = { &osk5912_smc91x_device, &osk5912_cf_device, &osk5912_mcbsp1_device, + &osk5912_kp_device, + &osk5912_lcd_device, }; static void __init osk_init_smc91x(void) @@ -197,7 +277,6 @@ static struct omap_uart_config osk_uart_config __initdata = { }; static struct omap_lcd_config osk_lcd_config __initdata = { - .panel_name = "osk", .ctrl_name = "internal", }; @@ -255,8 +334,18 @@ static void __init osk_mistral_init(void) static void __init osk_mistral_init(void) { } #endif +#define EMIFS_CS3_VAL (0x88013141) + static void __init osk_init(void) { + /* Workaround for wrong CS3 (NOR flash) timing + * There are some U-Boot versions out there which configure + * wrong CS3 memory timings. This mainly leads to CRC + * or similiar errors if you use NOR flash (e.g. with JFFS2) + */ + if (EMIFS_CCS(3) != EMIFS_CS3_VAL) + EMIFS_CCS(3) = EMIFS_CS3_VAL; + osk_flash_resource.end = osk_flash_resource.start = omap_cs3_phys(); osk_flash_resource.end += SZ_32M - 1; platform_add_devices(osk5912_devices, ARRAY_SIZE(osk5912_devices)); diff --git a/arch/arm/mach-omap1/board-palmte.c b/arch/arm/mach-omap1/board-palmte.c index e488f7236775..4bc8a62909b9 100644 --- a/arch/arm/mach-omap1/board-palmte.c +++ b/arch/arm/mach-omap1/board-palmte.c @@ -38,6 +38,15 @@ static void __init omap_generic_init_irq(void) omap_init_irq(); } +static struct platform_device palmte_lcd_device = { + .name = "lcd_palmte", + .id = -1, +}; + +static struct platform_device *devices[] __initdata = { + &palmte_lcd_device, +}; + static struct omap_usb_config palmte_usb_config __initdata = { .register_dev = 1, .hmc_mode = 0, @@ -55,7 +64,6 @@ static struct omap_mmc_config palmte_mmc_config __initdata = { }; static struct omap_lcd_config palmte_lcd_config __initdata = { - .panel_name = "palmte", .ctrl_name = "internal", }; @@ -69,6 +77,8 @@ static void __init omap_generic_init(void) { omap_board_config = palmte_config; omap_board_config_size = ARRAY_SIZE(palmte_config); + + platform_add_devices(devices, ARRAY_SIZE(devices)); } static void __init omap_generic_map_io(void) diff --git a/arch/arm/mach-omap1/board-perseus2.c b/arch/arm/mach-omap1/board-perseus2.c index 3913a3cc0ce6..64b45d8ae357 100644 --- a/arch/arm/mach-omap1/board-perseus2.c +++ b/arch/arm/mach-omap1/board-perseus2.c @@ -16,7 +16,9 @@ #include #include #include +#include #include +#include #include #include @@ -28,9 +30,44 @@ #include #include #include +#include #include #include +static int p2_keymap[] = { + KEY(0,0,KEY_UP), + KEY(0,1,KEY_RIGHT), + KEY(0,2,KEY_LEFT), + KEY(0,3,KEY_DOWN), + KEY(0,4,KEY_CENTER), + KEY(0,5,KEY_0_5), + KEY(1,0,KEY_SOFT2), + KEY(1,1,KEY_SEND), + KEY(1,2,KEY_END), + KEY(1,3,KEY_VOLUMEDOWN), + KEY(1,4,KEY_VOLUMEUP), + KEY(1,5,KEY_RECORD), + KEY(2,0,KEY_SOFT1), + KEY(2,1,KEY_3), + KEY(2,2,KEY_6), + KEY(2,3,KEY_9), + KEY(2,4,KEY_SHARP), + KEY(2,5,KEY_2_5), + KEY(3,0,KEY_BACK), + KEY(3,1,KEY_2), + KEY(3,2,KEY_5), + KEY(3,3,KEY_8), + KEY(3,4,KEY_0), + KEY(3,5,KEY_HEADSETHOOK), + KEY(4,0,KEY_HOME), + KEY(4,1,KEY_1), + KEY(4,2,KEY_4), + KEY(4,3,KEY_7), + KEY(4,4,KEY_STAR), + KEY(4,5,KEY_POWER), + 0 +}; + static struct resource smc91x_resources[] = { [0] = { .start = H2P2_DBG_FPGA_ETHR_START, /* Physical */ @@ -44,7 +81,7 @@ static struct resource smc91x_resources[] = { }, }; -static struct mtd_partition p2_partitions[] = { +static struct mtd_partition nor_partitions[] = { /* bootloader (U-Boot, etc) in first sector */ { .name = "bootloader", @@ -75,27 +112,47 @@ static struct mtd_partition p2_partitions[] = { }, }; -static struct flash_platform_data p2_flash_data = { +static struct flash_platform_data nor_data = { .map_name = "cfi_probe", .width = 2, - .parts = p2_partitions, - .nr_parts = ARRAY_SIZE(p2_partitions), + .parts = nor_partitions, + .nr_parts = ARRAY_SIZE(nor_partitions), }; -static struct resource p2_flash_resource = { +static struct resource nor_resource = { .start = OMAP_CS0_PHYS, .end = OMAP_CS0_PHYS + SZ_32M - 1, .flags = IORESOURCE_MEM, }; -static struct platform_device p2_flash_device = { +static struct platform_device nor_device = { .name = "omapflash", .id = 0, .dev = { - .platform_data = &p2_flash_data, + .platform_data = &nor_data, }, .num_resources = 1, - .resource = &p2_flash_resource, + .resource = &nor_resource, +}; + +static struct nand_platform_data nand_data = { + .options = NAND_SAMSUNG_LP_OPTIONS, +}; + +static struct resource nand_resource = { + .start = OMAP_CS3_PHYS, + .end = OMAP_CS3_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device nand_device = { + .name = "omapnand", + .id = 0, + .dev = { + .platform_data = &nand_data, + }, + .num_resources = 1, + .resource = &nand_resource, }; static struct platform_device smc91x_device = { @@ -105,17 +162,55 @@ static struct platform_device smc91x_device = { .resource = smc91x_resources, }; -static struct platform_device *devices[] __initdata = { - &p2_flash_device, - &smc91x_device, +static struct resource kp_resources[] = { + [0] = { + .start = INT_730_MPUIO_KEYPAD, + .end = INT_730_MPUIO_KEYPAD, + .flags = IORESOURCE_IRQ, + }, }; +static struct omap_kp_platform_data kp_data = { + .rows = 8, + .cols = 8, + .keymap = p2_keymap, +}; + +static struct platform_device kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &kp_data, + }, + .num_resources = ARRAY_SIZE(kp_resources), + .resource = kp_resources, +}; + +static struct platform_device lcd_device = { + .name = "lcd_p2", + .id = -1, +}; + +static struct platform_device *devices[] __initdata = { + &nor_device, + &nand_device, + &smc91x_device, + &kp_device, + &lcd_device, +}; + +#define P2_NAND_RB_GPIO_PIN 62 + +static int nand_dev_ready(struct nand_platform_data *data) +{ + return omap_get_gpio_datain(P2_NAND_RB_GPIO_PIN); +} + static struct omap_uart_config perseus2_uart_config __initdata = { .enabled_uarts = ((1 << 0) | (1 << 1)), }; static struct omap_lcd_config perseus2_lcd_config __initdata = { - .panel_name = "p2", .ctrl_name = "internal", }; @@ -126,7 +221,13 @@ static struct omap_board_config_kernel perseus2_config[] = { static void __init omap_perseus2_init(void) { - (void) platform_add_devices(devices, ARRAY_SIZE(devices)); + if (!(omap_request_gpio(P2_NAND_RB_GPIO_PIN))) + nand_data.dev_ready = nand_dev_ready; + + omap_cfg_reg(L3_1610_FLASH_CS2B_OE); + omap_cfg_reg(M8_1610_FLASH_CS2B_WE); + + platform_add_devices(devices, ARRAY_SIZE(devices)); omap_board_config = perseus2_config; omap_board_config_size = ARRAY_SIZE(perseus2_config); diff --git a/arch/arm/mach-omap1/board-voiceblue.c b/arch/arm/mach-omap1/board-voiceblue.c index 52e4a9d69642..447a586eb334 100644 --- a/arch/arm/mach-omap1/board-voiceblue.c +++ b/arch/arm/mach-omap1/board-voiceblue.c @@ -155,9 +155,9 @@ static struct omap_uart_config voiceblue_uart_config __initdata = { }; static struct omap_board_config_kernel voiceblue_config[] = { - { OMAP_TAG_USB, &voiceblue_usb_config }, - { OMAP_TAG_MMC, &voiceblue_mmc_config }, - { OMAP_TAG_UART, &voiceblue_uart_config }, + { OMAP_TAG_USB, &voiceblue_usb_config }, + { OMAP_TAG_MMC, &voiceblue_mmc_config }, + { OMAP_TAG_UART, &voiceblue_uart_config }, }; static void __init voiceblue_init_irq(void) @@ -235,7 +235,7 @@ static struct notifier_block panic_block = { static int __init voiceblue_setup(void) { /* Setup panic notifier */ - atomic_notifier_chain_register(&panic_notifier_list, &panic_block); + notifier_chain_register(&panic_notifier_list, &panic_block); return 0; } diff --git a/arch/arm/mach-omap1/clock.c b/arch/arm/mach-omap1/clock.c index 75110ba10424..619db18144ea 100644 --- a/arch/arm/mach-omap1/clock.c +++ b/arch/arm/mach-omap1/clock.c @@ -345,7 +345,7 @@ static unsigned calc_ext_dsor(unsigned long rate) */ for (dsor = 2; dsor < 96; ++dsor) { if ((dsor & 1) && dsor > 8) - continue; + continue; if (rate >= 96000000 / dsor) break; } @@ -687,6 +687,11 @@ int __init omap1_clk_init(void) clk_register(*clkp); continue; } + + if (((*clkp)->flags &CLOCK_IN_OMAP310) && cpu_is_omap310()) { + clk_register(*clkp); + continue; + } } info = omap_get_config(OMAP_TAG_CLOCK, struct omap_clock_config); @@ -784,7 +789,7 @@ int __init omap1_clk_init(void) clk_enable(&armxor_ck.clk); clk_enable(&armtim_ck.clk); /* This should be done by timer code */ - if (cpu_is_omap1510()) + if (cpu_is_omap15xx()) clk_enable(&arm_gpio_ck); return 0; diff --git a/arch/arm/mach-omap1/clock.h b/arch/arm/mach-omap1/clock.h index 4f18d1b94449..b7c68819c4e7 100644 --- a/arch/arm/mach-omap1/clock.h +++ b/arch/arm/mach-omap1/clock.h @@ -151,7 +151,7 @@ static struct clk ck_ref = { .name = "ck_ref", .rate = 12000000, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - ALWAYS_ENABLED, + CLOCK_IN_OMAP310 | ALWAYS_ENABLED, .enable = &omap1_clk_enable_generic, .disable = &omap1_clk_disable_generic, }; @@ -160,7 +160,7 @@ static struct clk ck_dpll1 = { .name = "ck_dpll1", .parent = &ck_ref, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - RATE_PROPAGATES | ALWAYS_ENABLED, + CLOCK_IN_OMAP310 | RATE_PROPAGATES | ALWAYS_ENABLED, .enable = &omap1_clk_enable_generic, .disable = &omap1_clk_disable_generic, }; @@ -183,7 +183,8 @@ static struct clk arm_ck = { .name = "arm_ck", .parent = &ck_dpll1, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - RATE_CKCTL | RATE_PROPAGATES | ALWAYS_ENABLED, + CLOCK_IN_OMAP310 | RATE_CKCTL | RATE_PROPAGATES | + ALWAYS_ENABLED, .rate_offset = CKCTL_ARMDIV_OFFSET, .recalc = &omap1_ckctl_recalc, .enable = &omap1_clk_enable_generic, @@ -195,7 +196,8 @@ static struct arm_idlect1_clk armper_ck = { .name = "armper_ck", .parent = &ck_dpll1, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - RATE_CKCTL | CLOCK_IDLE_CONTROL, + CLOCK_IN_OMAP310 | RATE_CKCTL | + CLOCK_IDLE_CONTROL, .enable_reg = (void __iomem *)ARM_IDLECT2, .enable_bit = EN_PERCK, .rate_offset = CKCTL_PERDIV_OFFSET, @@ -209,7 +211,7 @@ static struct arm_idlect1_clk armper_ck = { static struct clk arm_gpio_ck = { .name = "arm_gpio_ck", .parent = &ck_dpll1, - .flags = CLOCK_IN_OMAP1510, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, .enable_reg = (void __iomem *)ARM_IDLECT2, .enable_bit = EN_GPIOCK, .recalc = &followparent_recalc, @@ -222,7 +224,7 @@ static struct arm_idlect1_clk armxor_ck = { .name = "armxor_ck", .parent = &ck_ref, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - CLOCK_IDLE_CONTROL, + CLOCK_IN_OMAP310 | CLOCK_IDLE_CONTROL, .enable_reg = (void __iomem *)ARM_IDLECT2, .enable_bit = EN_XORPCK, .recalc = &followparent_recalc, @@ -237,7 +239,7 @@ static struct arm_idlect1_clk armtim_ck = { .name = "armtim_ck", .parent = &ck_ref, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - CLOCK_IDLE_CONTROL, + CLOCK_IN_OMAP310 | CLOCK_IDLE_CONTROL, .enable_reg = (void __iomem *)ARM_IDLECT2, .enable_bit = EN_TIMCK, .recalc = &followparent_recalc, @@ -252,7 +254,7 @@ static struct arm_idlect1_clk armwdt_ck = { .name = "armwdt_ck", .parent = &ck_ref, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - CLOCK_IDLE_CONTROL, + CLOCK_IN_OMAP310 | CLOCK_IDLE_CONTROL, .enable_reg = (void __iomem *)ARM_IDLECT2, .enable_bit = EN_WDTCK, .recalc = &omap1_watchdog_recalc, @@ -344,9 +346,9 @@ static struct arm_idlect1_clk tc_ck = { .name = "tc_ck", .parent = &ck_dpll1, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - CLOCK_IN_OMAP730 | RATE_CKCTL | - RATE_PROPAGATES | ALWAYS_ENABLED | - CLOCK_IDLE_CONTROL, + CLOCK_IN_OMAP730 | CLOCK_IN_OMAP310 | + RATE_CKCTL | RATE_PROPAGATES | + ALWAYS_ENABLED | CLOCK_IDLE_CONTROL, .rate_offset = CKCTL_TCDIV_OFFSET, .recalc = &omap1_ckctl_recalc, .enable = &omap1_clk_enable_generic, @@ -358,7 +360,8 @@ static struct arm_idlect1_clk tc_ck = { static struct clk arminth_ck1510 = { .name = "arminth_ck", .parent = &tc_ck.clk, - .flags = CLOCK_IN_OMAP1510 | ALWAYS_ENABLED, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | + ALWAYS_ENABLED, .recalc = &followparent_recalc, /* Note: On 1510 the frequency follows TC_CK * @@ -372,7 +375,8 @@ static struct clk tipb_ck = { /* No-idle controlled by "tc_ck" */ .name = "tibp_ck", .parent = &tc_ck.clk, - .flags = CLOCK_IN_OMAP1510 | ALWAYS_ENABLED, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | + ALWAYS_ENABLED, .recalc = &followparent_recalc, .enable = &omap1_clk_enable_generic, .disable = &omap1_clk_disable_generic, @@ -417,7 +421,7 @@ static struct clk dma_ck = { .name = "dma_ck", .parent = &tc_ck.clk, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - ALWAYS_ENABLED, + CLOCK_IN_OMAP310 | ALWAYS_ENABLED, .recalc = &followparent_recalc, .enable = &omap1_clk_enable_generic, .disable = &omap1_clk_disable_generic, @@ -437,7 +441,7 @@ static struct arm_idlect1_clk api_ck = { .name = "api_ck", .parent = &tc_ck.clk, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - CLOCK_IDLE_CONTROL, + CLOCK_IN_OMAP310 | CLOCK_IDLE_CONTROL, .enable_reg = (void __iomem *)ARM_IDLECT2, .enable_bit = EN_APICK, .recalc = &followparent_recalc, @@ -451,7 +455,8 @@ static struct arm_idlect1_clk lb_ck = { .clk = { .name = "lb_ck", .parent = &tc_ck.clk, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IDLE_CONTROL, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | + CLOCK_IDLE_CONTROL, .enable_reg = (void __iomem *)ARM_IDLECT2, .enable_bit = EN_LBCK, .recalc = &followparent_recalc, @@ -495,8 +500,8 @@ static struct arm_idlect1_clk lcd_ck_1510 = { .clk = { .name = "lcd_ck", .parent = &ck_dpll1, - .flags = CLOCK_IN_OMAP1510 | RATE_CKCTL | - CLOCK_IDLE_CONTROL, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | + RATE_CKCTL | CLOCK_IDLE_CONTROL, .enable_reg = (void __iomem *)ARM_IDLECT2, .enable_bit = EN_LCDCK, .rate_offset = CKCTL_LCDDIV_OFFSET, @@ -512,8 +517,9 @@ static struct clk uart1_1510 = { /* Direct from ULPD, no real parent */ .parent = &armper_ck.clk, .rate = 12000000, - .flags = CLOCK_IN_OMAP1510 | ENABLE_REG_32BIT | - ALWAYS_ENABLED | CLOCK_NO_IDLE_PARENT, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | + ENABLE_REG_32BIT | ALWAYS_ENABLED | + CLOCK_NO_IDLE_PARENT, .enable_reg = (void __iomem *)MOD_CONF_CTRL_0, .enable_bit = 29, /* Chooses between 12MHz and 48MHz */ .set_rate = &omap1_set_uart_rate, @@ -544,8 +550,8 @@ static struct clk uart2_ck = { .parent = &armper_ck.clk, .rate = 12000000, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - ENABLE_REG_32BIT | ALWAYS_ENABLED | - CLOCK_NO_IDLE_PARENT, + CLOCK_IN_OMAP310 | ENABLE_REG_32BIT | + ALWAYS_ENABLED | CLOCK_NO_IDLE_PARENT, .enable_reg = (void __iomem *)MOD_CONF_CTRL_0, .enable_bit = 30, /* Chooses between 12MHz and 48MHz */ .set_rate = &omap1_set_uart_rate, @@ -559,8 +565,9 @@ static struct clk uart3_1510 = { /* Direct from ULPD, no real parent */ .parent = &armper_ck.clk, .rate = 12000000, - .flags = CLOCK_IN_OMAP1510 | ENABLE_REG_32BIT | - ALWAYS_ENABLED | CLOCK_NO_IDLE_PARENT, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | + ENABLE_REG_32BIT | ALWAYS_ENABLED | + CLOCK_NO_IDLE_PARENT, .enable_reg = (void __iomem *)MOD_CONF_CTRL_0, .enable_bit = 31, /* Chooses between 12MHz and 48MHz */ .set_rate = &omap1_set_uart_rate, @@ -590,7 +597,7 @@ static struct clk usb_clko = { /* 6 MHz output on W4_USB_CLKO */ /* Direct from ULPD, no parent */ .rate = 6000000, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - RATE_FIXED | ENABLE_REG_32BIT, + CLOCK_IN_OMAP310 | RATE_FIXED | ENABLE_REG_32BIT, .enable_reg = (void __iomem *)ULPD_CLOCK_CTRL, .enable_bit = USB_MCLK_EN_BIT, .enable = &omap1_clk_enable_generic, @@ -601,7 +608,7 @@ static struct clk usb_hhc_ck1510 = { .name = "usb_hhc_ck", /* Direct from ULPD, no parent */ .rate = 48000000, /* Actually 2 clocks, 12MHz and 48MHz */ - .flags = CLOCK_IN_OMAP1510 | + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | RATE_FIXED | ENABLE_REG_32BIT, .enable_reg = (void __iomem *)MOD_CONF_CTRL_0, .enable_bit = USB_HOST_HHC_UHOST_EN, @@ -637,7 +644,9 @@ static struct clk mclk_1510 = { .name = "mclk", /* Direct from ULPD, no parent. May be enabled by ext hardware. */ .rate = 12000000, - .flags = CLOCK_IN_OMAP1510 | RATE_FIXED, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | RATE_FIXED, + .enable_reg = (void __iomem *)SOFT_REQ_REG, + .enable_bit = 6, .enable = &omap1_clk_enable_generic, .disable = &omap1_clk_disable_generic, }; @@ -659,7 +668,7 @@ static struct clk bclk_1510 = { .name = "bclk", /* Direct from ULPD, no parent. May be enabled by ext hardware. */ .rate = 12000000, - .flags = CLOCK_IN_OMAP1510 | RATE_FIXED, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | RATE_FIXED, .enable = &omap1_clk_enable_generic, .disable = &omap1_clk_disable_generic, }; @@ -678,12 +687,14 @@ static struct clk bclk_16xx = { }; static struct clk mmc1_ck = { - .name = "mmc1_ck", + .name = "mmc_ck", + .id = 1, /* Functional clock is direct from ULPD, interface clock is ARMPER */ .parent = &armper_ck.clk, .rate = 48000000, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - RATE_FIXED | ENABLE_REG_32BIT | CLOCK_NO_IDLE_PARENT, + CLOCK_IN_OMAP310 | RATE_FIXED | ENABLE_REG_32BIT | + CLOCK_NO_IDLE_PARENT, .enable_reg = (void __iomem *)MOD_CONF_CTRL_0, .enable_bit = 23, .enable = &omap1_clk_enable_generic, @@ -691,7 +702,8 @@ static struct clk mmc1_ck = { }; static struct clk mmc2_ck = { - .name = "mmc2_ck", + .name = "mmc_ck", + .id = 2, /* Functional clock is direct from ULPD, interface clock is ARMPER */ .parent = &armper_ck.clk, .rate = 48000000, @@ -706,7 +718,7 @@ static struct clk mmc2_ck = { static struct clk virtual_ck_mpu = { .name = "mpu", .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - VIRTUAL_CLOCK | ALWAYS_ENABLED, + CLOCK_IN_OMAP310 | VIRTUAL_CLOCK | ALWAYS_ENABLED, .parent = &arm_ck, /* Is smarter alias for */ .recalc = &followparent_recalc, .set_rate = &omap1_select_table_rate, @@ -715,6 +727,20 @@ static struct clk virtual_ck_mpu = { .disable = &omap1_clk_disable_generic, }; +/* virtual functional clock domain for I2C. Just for making sure that ARMXOR_CK +remains active during MPU idle whenever this is enabled */ +static struct clk i2c_fck = { + .name = "i2c_fck", + .id = 1, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | + VIRTUAL_CLOCK | CLOCK_NO_IDLE_PARENT | + ALWAYS_ENABLED, + .parent = &armxor_ck.clk, + .recalc = &followparent_recalc, + .enable = &omap1_clk_enable_generic, + .disable = &omap1_clk_disable_generic, +}; + static struct clk * onchip_clks[] = { /* non-ULPD clocks */ &ck_ref, @@ -763,6 +789,7 @@ static struct clk * onchip_clks[] = { &mmc2_ck, /* Virtual clocks */ &virtual_ck_mpu, + &i2c_fck, }; #endif diff --git a/arch/arm/mach-omap1/devices.c b/arch/arm/mach-omap1/devices.c index ecbc47514adc..847329cafc5c 100644 --- a/arch/arm/mach-omap1/devices.c +++ b/arch/arm/mach-omap1/devices.c @@ -25,10 +25,6 @@ #include #include -extern void omap_nop_release(struct device *dev); - -/*-------------------------------------------------------------------------*/ - #if defined(CONFIG_OMAP1610_IR) || defined(CONFIG_OMAP161O_IR_MODULE) static u64 irda_dmamask = 0xffffffff; @@ -37,7 +33,6 @@ static struct platform_device omap1610ir_device = { .name = "omap1610-ir", .id = -1, .dev = { - .release = omap_nop_release, .dma_mask = &irda_dmamask, }, }; @@ -84,9 +79,6 @@ static struct resource rtc_resources[] = { static struct platform_device omap_rtc_device = { .name = "omap_rtc", .id = -1, - .dev = { - .release = omap_nop_release, - }, .num_resources = ARRAY_SIZE(rtc_resources), .resource = rtc_resources, }; @@ -99,6 +91,42 @@ static void omap_init_rtc(void) static inline void omap_init_rtc(void) {} #endif +#if defined(CONFIG_OMAP_STI) + +#define OMAP1_STI_BASE IO_ADDRESS(0xfffea000) +#define OMAP1_STI_CHANNEL_BASE (OMAP1_STI_BASE + 0x400) + +static struct resource sti_resources[] = { + { + .start = OMAP1_STI_BASE, + .end = OMAP1_STI_BASE + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = OMAP1_STI_CHANNEL_BASE, + .end = OMAP1_STI_CHANNEL_BASE + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_1610_STI, + .flags = IORESOURCE_IRQ, + } +}; + +static struct platform_device sti_device = { + .name = "sti", + .id = -1, + .num_resources = ARRAY_SIZE(sti_resources), + .resource = sti_resources, +}; + +static inline void omap_init_sti(void) +{ + platform_device_register(&sti_device); +} +#else +static inline void omap_init_sti(void) {} +#endif /*-------------------------------------------------------------------------*/ @@ -129,6 +157,7 @@ static int __init omap1_init_devices(void) */ omap_init_irda(); omap_init_rtc(); + omap_init_sti(); return 0; } diff --git a/arch/arm/mach-omap1/io.c b/arch/arm/mach-omap1/io.c index 82d556be79c5..be3a2a4ee2b8 100644 --- a/arch/arm/mach-omap1/io.c +++ b/arch/arm/mach-omap1/io.c @@ -18,6 +18,7 @@ #include #include #include +#include extern int omap1_clk_init(void); extern void omap_check_revision(void); @@ -110,7 +111,7 @@ void __init omap1_map_common_io(void) } #endif #ifdef CONFIG_ARCH_OMAP15XX - if (cpu_is_omap1510()) { + if (cpu_is_omap15xx()) { iotable_init(omap1510_io_desc, ARRAY_SIZE(omap1510_io_desc)); } #endif @@ -121,6 +122,7 @@ void __init omap1_map_common_io(void) #endif omap_sram_init(); + omapfb_reserve_mem(); } /* diff --git a/arch/arm/mach-omap1/irq.c b/arch/arm/mach-omap1/irq.c index ed65a7d2e941..a0431c00fa81 100644 --- a/arch/arm/mach-omap1/irq.c +++ b/arch/arm/mach-omap1/irq.c @@ -60,7 +60,7 @@ struct omap_irq_bank { unsigned long wake_enable; }; -static unsigned int irq_bank_count = 0; +static unsigned int irq_bank_count; static struct omap_irq_bank *irq_banks; static inline unsigned int irq_bank_readl(int bank, int offset) @@ -142,28 +142,28 @@ static void omap_irq_set_cfg(int irq, int fiq, int priority, int trigger) #ifdef CONFIG_ARCH_OMAP730 static struct omap_irq_bank omap730_irq_banks[] = { - { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3f8e22f }, - { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xfdb9c1f2 }, + { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3f8e22f }, + { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xfdb9c1f2 }, { .base_reg = OMAP_IH2_BASE + 0x100, .trigger_map = 0x800040f3 }, }; #endif #ifdef CONFIG_ARCH_OMAP15XX static struct omap_irq_bank omap1510_irq_banks[] = { - { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3febfff }, - { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xffbfffed }, + { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3febfff }, + { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xffbfffed }, }; static struct omap_irq_bank omap310_irq_banks[] = { - { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3faefc3 }, - { .base_reg = OMAP_IH2_BASE, .trigger_map = 0x65b3c061 }, + { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3faefc3 }, + { .base_reg = OMAP_IH2_BASE, .trigger_map = 0x65b3c061 }, }; #endif #if defined(CONFIG_ARCH_OMAP16XX) static struct omap_irq_bank omap1610_irq_banks[] = { - { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3fefe8f }, - { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xfdb7c1fd }, + { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3fefe8f }, + { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xfdb7c1fd }, { .base_reg = OMAP_IH2_BASE + 0x100, .trigger_map = 0xffffb7ff }, { .base_reg = OMAP_IH2_BASE + 0x200, .trigger_map = 0xffffffff }, }; diff --git a/arch/arm/mach-omap1/mux.c b/arch/arm/mach-omap1/mux.c index d4b8d624e742..10fe0b3efcac 100644 --- a/arch/arm/mach-omap1/mux.c +++ b/arch/arm/mach-omap1/mux.c @@ -35,16 +35,20 @@ #ifdef CONFIG_ARCH_OMAP730 struct pin_config __initdata_or_module omap730_pins[] = { -MUX_CFG_730("E2_730_KBR0", 12, 21, 0, 0, 20, 1, NA, 0, 0) -MUX_CFG_730("J7_730_KBR1", 12, 25, 0, 0, 24, 1, NA, 0, 0) -MUX_CFG_730("E1_730_KBR2", 12, 29, 0, 0, 28, 1, NA, 0, 0) -MUX_CFG_730("F3_730_KBR3", 13, 1, 0, 0, 0, 1, NA, 0, 0) -MUX_CFG_730("D2_730_KBR4", 13, 5, 0, 0, 4, 1, NA, 0, 0) -MUX_CFG_730("C2_730_KBC0", 13, 9, 0, 0, 8, 1, NA, 0, 0) -MUX_CFG_730("D3_730_KBC1", 13, 13, 0, 0, 12, 1, NA, 0, 0) -MUX_CFG_730("E4_730_KBC2", 13, 17, 0, 0, 16, 1, NA, 0, 0) -MUX_CFG_730("F4_730_KBC3", 13, 21, 0, 0, 20, 1, NA, 0, 0) -MUX_CFG_730("E3_730_KBC4", 13, 25, 0, 0, 24, 1, NA, 0, 0) +MUX_CFG_730("E2_730_KBR0", 12, 21, 0, 20, 1, 0) +MUX_CFG_730("J7_730_KBR1", 12, 25, 0, 24, 1, 0) +MUX_CFG_730("E1_730_KBR2", 12, 29, 0, 28, 1, 0) +MUX_CFG_730("F3_730_KBR3", 13, 1, 0, 0, 1, 0) +MUX_CFG_730("D2_730_KBR4", 13, 5, 0, 4, 1, 0) +MUX_CFG_730("C2_730_KBC0", 13, 9, 0, 8, 1, 0) +MUX_CFG_730("D3_730_KBC1", 13, 13, 0, 12, 1, 0) +MUX_CFG_730("E4_730_KBC2", 13, 17, 0, 16, 1, 0) +MUX_CFG_730("F4_730_KBC3", 13, 21, 0, 20, 1, 0) +MUX_CFG_730("E3_730_KBC4", 13, 25, 0, 24, 1, 0) + +MUX_CFG_730("AA17_730_USB_DM", 2, 21, 0, 20, 0, 0) +MUX_CFG_730("W16_730_USB_PU_EN", 2, 25, 0, 24, 0, 0) +MUX_CFG_730("W17_730_USB_VBUSI", 2, 29, 0, 28, 0, 0) }; #endif @@ -73,8 +77,8 @@ MUX_CFG("UART3_BCLK", A, 0, 0, 2, 6, 0, NA, 0, 0) MUX_CFG("Y15_1610_UART3_RTS", A, 0, 1, 2, 6, 0, NA, 0, 0) /* PWT & PWL, conflicts with UART3 */ -MUX_CFG("PWT", 6, 0, 2, 0, 30, 0, NA, 0, 0) -MUX_CFG("PWL", 6, 3, 1, 0, 31, 1, NA, 0, 0) +MUX_CFG("PWT", 6, 0, 2, 0, 30, 0, NA, 0, 0) +MUX_CFG("PWL", 6, 3, 1, 0, 31, 1, NA, 0, 0) /* USB internal master generic */ MUX_CFG("R18_USB_VBUS", 7, 9, 2, 1, 11, 0, NA, 0, 1) @@ -151,7 +155,7 @@ MUX_CFG("MCBSP3_CLKX", 9, 3, 1, 1, 29, 0, NA, 0, 1) /* Misc ballouts */ MUX_CFG("BALLOUT_V8_ARMIO3", B, 18, 0, 2, 25, 1, NA, 0, 1) -MUX_CFG("N20_HDQ", 6, 18, 1, 1, 4, 0, 1, 4, 0) +MUX_CFG("N20_HDQ", 6, 18, 1, 1, 4, 0, 1, 4, 0) /* OMAP-1610 MMC2 */ MUX_CFG("W8_1610_MMC2_DAT0", B, 21, 6, 2, 23, 1, 2, 1, 1) diff --git a/arch/arm/mach-omap1/pm.c b/arch/arm/mach-omap1/pm.c new file mode 100644 index 000000000000..ddf6b07dc9c7 --- /dev/null +++ b/arch/arm/mach-omap1/pm.c @@ -0,0 +1,770 @@ +/* + * linux/arch/arm/mach-omap1/pm.c + * + * OMAP Power Management Routines + * + * Original code for the SA11x0: + * Copyright (c) 2001 Cliff Brake + * + * Modified for the PXA250 by Nicolas Pitre: + * Copyright (c) 2002 Monta Vista Software, Inc. + * + * Modified for the OMAP1510 by David Singleton: + * Copyright (c) 2002 Monta Vista Software, Inc. + * + * Cleanup 2004 for OMAP1510/1610 by Dirk Behme + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned int arm_sleep_save[ARM_SLEEP_SAVE_SIZE]; +static unsigned short dsp_sleep_save[DSP_SLEEP_SAVE_SIZE]; +static unsigned short ulpd_sleep_save[ULPD_SLEEP_SAVE_SIZE]; +static unsigned int mpui730_sleep_save[MPUI730_SLEEP_SAVE_SIZE]; +static unsigned int mpui1510_sleep_save[MPUI1510_SLEEP_SAVE_SIZE]; +static unsigned int mpui1610_sleep_save[MPUI1610_SLEEP_SAVE_SIZE]; + +static unsigned short enable_dyn_sleep = 1; + +static ssize_t omap_pm_sleep_while_idle_show(struct subsystem * subsys, char *buf) +{ + return sprintf(buf, "%hu\n", enable_dyn_sleep); +} + +static ssize_t omap_pm_sleep_while_idle_store(struct subsystem * subsys, + const char * buf, + size_t n) +{ + unsigned short value; + if (sscanf(buf, "%hu", &value) != 1 || + (value != 0 && value != 1)) { + printk(KERN_ERR "idle_sleep_store: Invalid value\n"); + return -EINVAL; + } + enable_dyn_sleep = value; + return n; +} + +static struct subsys_attribute sleep_while_idle_attr = { + .attr = { + .name = __stringify(sleep_while_idle), + .mode = 0644, + }, + .show = omap_pm_sleep_while_idle_show, + .store = omap_pm_sleep_while_idle_store, +}; + +extern struct subsystem power_subsys; +static void (*omap_sram_idle)(void) = NULL; +static void (*omap_sram_suspend)(unsigned long r0, unsigned long r1) = NULL; + +/* + * Let's power down on idle, but only if we are really + * idle, because once we start down the path of + * going idle we continue to do idle even if we get + * a clock tick interrupt . . + */ +void omap_pm_idle(void) +{ + extern __u32 arm_idlect1_mask; + __u32 use_idlect1 = arm_idlect1_mask; +#ifndef CONFIG_OMAP_MPU_TIMER + int do_sleep; +#endif + + local_irq_disable(); + local_fiq_disable(); + if (need_resched()) { + local_fiq_enable(); + local_irq_enable(); + return; + } + + /* + * Since an interrupt may set up a timer, we don't want to + * reprogram the hardware timer with interrupts enabled. + * Re-enable interrupts only after returning from idle. + */ + timer_dyn_reprogram(); + +#ifdef CONFIG_OMAP_MPU_TIMER +#warning Enable 32kHz OS timer in order to allow sleep states in idle + use_idlect1 = use_idlect1 & ~(1 << 9); +#else + + do_sleep = 0; + while (enable_dyn_sleep) { + +#ifdef CONFIG_CBUS_TAHVO_USB + extern int vbus_active; + /* Clock requirements? */ + if (vbus_active) + break; +#endif + do_sleep = 1; + break; + } + +#ifdef CONFIG_OMAP_DM_TIMER + use_idlect1 = omap_dm_timer_modify_idlect_mask(use_idlect1); +#endif + + if (omap_dma_running()) { + use_idlect1 &= ~(1 << 6); + if (omap_lcd_dma_ext_running()) + use_idlect1 &= ~(1 << 12); + } + + /* We should be able to remove the do_sleep variable and multiple + * tests above as soon as drivers, timer and DMA code have been fixed. + * Even the sleep block count should become obsolete. */ + if ((use_idlect1 != ~0) || !do_sleep) { + + __u32 saved_idlect1 = omap_readl(ARM_IDLECT1); + if (cpu_is_omap15xx()) + use_idlect1 &= OMAP1510_BIG_SLEEP_REQUEST; + else + use_idlect1 &= OMAP1610_IDLECT1_SLEEP_VAL; + omap_writel(use_idlect1, ARM_IDLECT1); + __asm__ volatile ("mcr p15, 0, r0, c7, c0, 4"); + omap_writel(saved_idlect1, ARM_IDLECT1); + + local_fiq_enable(); + local_irq_enable(); + return; + } + omap_sram_suspend(omap_readl(ARM_IDLECT1), + omap_readl(ARM_IDLECT2)); +#endif + + local_fiq_enable(); + local_irq_enable(); +} + +/* + * Configuration of the wakeup event is board specific. For the + * moment we put it into this helper function. Later it may move + * to board specific files. + */ +static void omap_pm_wakeup_setup(void) +{ + u32 level1_wake = 0; + u32 level2_wake = OMAP_IRQ_BIT(INT_UART2); + + /* + * Turn off all interrupts except GPIO bank 1, L1-2nd level cascade, + * and the L2 wakeup interrupts: keypad and UART2. Note that the + * drivers must still separately call omap_set_gpio_wakeup() to + * wake up to a GPIO interrupt. + */ + if (cpu_is_omap730()) + level1_wake = OMAP_IRQ_BIT(INT_730_GPIO_BANK1) | + OMAP_IRQ_BIT(INT_730_IH2_IRQ); + else if (cpu_is_omap15xx()) + level1_wake = OMAP_IRQ_BIT(INT_GPIO_BANK1) | + OMAP_IRQ_BIT(INT_1510_IH2_IRQ); + else if (cpu_is_omap16xx()) + level1_wake = OMAP_IRQ_BIT(INT_GPIO_BANK1) | + OMAP_IRQ_BIT(INT_1610_IH2_IRQ); + + omap_writel(~level1_wake, OMAP_IH1_MIR); + + if (cpu_is_omap730()) { + omap_writel(~level2_wake, OMAP_IH2_0_MIR); + omap_writel(~(OMAP_IRQ_BIT(INT_730_WAKE_UP_REQ) | + OMAP_IRQ_BIT(INT_730_MPUIO_KEYPAD)), + OMAP_IH2_1_MIR); + } else if (cpu_is_omap15xx()) { + level2_wake |= OMAP_IRQ_BIT(INT_KEYBOARD); + omap_writel(~level2_wake, OMAP_IH2_MIR); + } else if (cpu_is_omap16xx()) { + level2_wake |= OMAP_IRQ_BIT(INT_KEYBOARD); + omap_writel(~level2_wake, OMAP_IH2_0_MIR); + + /* INT_1610_WAKE_UP_REQ is needed for GPIO wakeup... */ + omap_writel(~OMAP_IRQ_BIT(INT_1610_WAKE_UP_REQ), + OMAP_IH2_1_MIR); + omap_writel(~0x0, OMAP_IH2_2_MIR); + omap_writel(~0x0, OMAP_IH2_3_MIR); + } + + /* New IRQ agreement, recalculate in cascade order */ + omap_writel(1, OMAP_IH2_CONTROL); + omap_writel(1, OMAP_IH1_CONTROL); +} + +#define EN_DSPCK 13 /* ARM_CKCTL */ +#define EN_APICK 6 /* ARM_IDLECT2 */ +#define DSP_EN 1 /* ARM_RSTCT1 */ + +void omap_pm_suspend(void) +{ + unsigned long arg0 = 0, arg1 = 0; + + printk("PM: OMAP%x is trying to enter deep sleep...\n", system_rev); + + omap_serial_wake_trigger(1); + + if (machine_is_omap_osk()) { + /* Stop LED1 (D9) blink */ + tps65010_set_led(LED1, OFF); + } + + omap_writew(0xffff, ULPD_SOFT_DISABLE_REQ_REG); + + /* + * Step 1: turn off interrupts (FIXME: NOTE: already disabled) + */ + + local_irq_disable(); + local_fiq_disable(); + + /* + * Step 2: save registers + * + * The omap is a strange/beautiful device. The caches, memory + * and register state are preserved across power saves. + * We have to save and restore very little register state to + * idle the omap. + * + * Save interrupt, MPUI, ARM and UPLD control registers. + */ + + if (cpu_is_omap730()) { + MPUI730_SAVE(OMAP_IH1_MIR); + MPUI730_SAVE(OMAP_IH2_0_MIR); + MPUI730_SAVE(OMAP_IH2_1_MIR); + MPUI730_SAVE(MPUI_CTRL); + MPUI730_SAVE(MPUI_DSP_BOOT_CONFIG); + MPUI730_SAVE(MPUI_DSP_API_CONFIG); + MPUI730_SAVE(EMIFS_CONFIG); + MPUI730_SAVE(EMIFF_SDRAM_CONFIG); + + } else if (cpu_is_omap15xx()) { + MPUI1510_SAVE(OMAP_IH1_MIR); + MPUI1510_SAVE(OMAP_IH2_MIR); + MPUI1510_SAVE(MPUI_CTRL); + MPUI1510_SAVE(MPUI_DSP_BOOT_CONFIG); + MPUI1510_SAVE(MPUI_DSP_API_CONFIG); + MPUI1510_SAVE(EMIFS_CONFIG); + MPUI1510_SAVE(EMIFF_SDRAM_CONFIG); + } else if (cpu_is_omap16xx()) { + MPUI1610_SAVE(OMAP_IH1_MIR); + MPUI1610_SAVE(OMAP_IH2_0_MIR); + MPUI1610_SAVE(OMAP_IH2_1_MIR); + MPUI1610_SAVE(OMAP_IH2_2_MIR); + MPUI1610_SAVE(OMAP_IH2_3_MIR); + MPUI1610_SAVE(MPUI_CTRL); + MPUI1610_SAVE(MPUI_DSP_BOOT_CONFIG); + MPUI1610_SAVE(MPUI_DSP_API_CONFIG); + MPUI1610_SAVE(EMIFS_CONFIG); + MPUI1610_SAVE(EMIFF_SDRAM_CONFIG); + } + + ARM_SAVE(ARM_CKCTL); + ARM_SAVE(ARM_IDLECT1); + ARM_SAVE(ARM_IDLECT2); + if (!(cpu_is_omap15xx())) + ARM_SAVE(ARM_IDLECT3); + ARM_SAVE(ARM_EWUPCT); + ARM_SAVE(ARM_RSTCT1); + ARM_SAVE(ARM_RSTCT2); + ARM_SAVE(ARM_SYSST); + ULPD_SAVE(ULPD_CLOCK_CTRL); + ULPD_SAVE(ULPD_STATUS_REQ); + + /* (Step 3 removed - we now allow deep sleep by default) */ + + /* + * Step 4: OMAP DSP Shutdown + */ + + /* stop DSP */ + omap_writew(omap_readw(ARM_RSTCT1) & ~(1 << DSP_EN), ARM_RSTCT1); + + /* shut down dsp_ck */ + omap_writew(omap_readw(ARM_CKCTL) & ~(1 << EN_DSPCK), ARM_CKCTL); + + /* temporarily enabling api_ck to access DSP registers */ + omap_writew(omap_readw(ARM_IDLECT2) | 1 << EN_APICK, ARM_IDLECT2); + + /* save DSP registers */ + DSP_SAVE(DSP_IDLECT2); + + /* Stop all DSP domain clocks */ + __raw_writew(0, DSP_IDLECT2); + + /* + * Step 5: Wakeup Event Setup + */ + + omap_pm_wakeup_setup(); + + /* + * Step 6: ARM and Traffic controller shutdown + */ + + /* disable ARM watchdog */ + omap_writel(0x00F5, OMAP_WDT_TIMER_MODE); + omap_writel(0x00A0, OMAP_WDT_TIMER_MODE); + + /* + * Step 6b: ARM and Traffic controller shutdown + * + * Step 6 continues here. Prepare jump to power management + * assembly code in internal SRAM. + * + * Since the omap_cpu_suspend routine has been copied to + * SRAM, we'll do an indirect procedure call to it and pass the + * contents of arm_idlect1 and arm_idlect2 so it can restore + * them when it wakes up and it will return. + */ + + arg0 = arm_sleep_save[ARM_SLEEP_SAVE_ARM_IDLECT1]; + arg1 = arm_sleep_save[ARM_SLEEP_SAVE_ARM_IDLECT2]; + + /* + * Step 6c: ARM and Traffic controller shutdown + * + * Jump to assembly code. The processor will stay there + * until wake up. + */ + omap_sram_suspend(arg0, arg1); + + /* + * If we are here, processor is woken up! + */ + + /* + * Restore DSP clocks + */ + + /* again temporarily enabling api_ck to access DSP registers */ + omap_writew(omap_readw(ARM_IDLECT2) | 1 << EN_APICK, ARM_IDLECT2); + + /* Restore DSP domain clocks */ + DSP_RESTORE(DSP_IDLECT2); + + /* + * Restore ARM state, except ARM_IDLECT1/2 which omap_cpu_suspend did + */ + + if (!(cpu_is_omap15xx())) + ARM_RESTORE(ARM_IDLECT3); + ARM_RESTORE(ARM_CKCTL); + ARM_RESTORE(ARM_EWUPCT); + ARM_RESTORE(ARM_RSTCT1); + ARM_RESTORE(ARM_RSTCT2); + ARM_RESTORE(ARM_SYSST); + ULPD_RESTORE(ULPD_CLOCK_CTRL); + ULPD_RESTORE(ULPD_STATUS_REQ); + + if (cpu_is_omap730()) { + MPUI730_RESTORE(EMIFS_CONFIG); + MPUI730_RESTORE(EMIFF_SDRAM_CONFIG); + MPUI730_RESTORE(OMAP_IH1_MIR); + MPUI730_RESTORE(OMAP_IH2_0_MIR); + MPUI730_RESTORE(OMAP_IH2_1_MIR); + } else if (cpu_is_omap15xx()) { + MPUI1510_RESTORE(MPUI_CTRL); + MPUI1510_RESTORE(MPUI_DSP_BOOT_CONFIG); + MPUI1510_RESTORE(MPUI_DSP_API_CONFIG); + MPUI1510_RESTORE(EMIFS_CONFIG); + MPUI1510_RESTORE(EMIFF_SDRAM_CONFIG); + MPUI1510_RESTORE(OMAP_IH1_MIR); + MPUI1510_RESTORE(OMAP_IH2_MIR); + } else if (cpu_is_omap16xx()) { + MPUI1610_RESTORE(MPUI_CTRL); + MPUI1610_RESTORE(MPUI_DSP_BOOT_CONFIG); + MPUI1610_RESTORE(MPUI_DSP_API_CONFIG); + MPUI1610_RESTORE(EMIFS_CONFIG); + MPUI1610_RESTORE(EMIFF_SDRAM_CONFIG); + + MPUI1610_RESTORE(OMAP_IH1_MIR); + MPUI1610_RESTORE(OMAP_IH2_0_MIR); + MPUI1610_RESTORE(OMAP_IH2_1_MIR); + MPUI1610_RESTORE(OMAP_IH2_2_MIR); + MPUI1610_RESTORE(OMAP_IH2_3_MIR); + } + + omap_writew(0, ULPD_SOFT_DISABLE_REQ_REG); + + /* + * Reenable interrupts + */ + + local_irq_enable(); + local_fiq_enable(); + + omap_serial_wake_trigger(0); + + printk("PM: OMAP%x is re-starting from deep sleep...\n", system_rev); + + if (machine_is_omap_osk()) { + /* Let LED1 (D9) blink again */ + tps65010_set_led(LED1, BLINK); + } +} + +#if defined(DEBUG) && defined(CONFIG_PROC_FS) +static int g_read_completed; + +/* + * Read system PM registers for debugging + */ +static int omap_pm_read_proc( + char *page_buffer, + char **my_first_byte, + off_t virtual_start, + int length, + int *eof, + void *data) +{ + int my_buffer_offset = 0; + char * const my_base = page_buffer; + + ARM_SAVE(ARM_CKCTL); + ARM_SAVE(ARM_IDLECT1); + ARM_SAVE(ARM_IDLECT2); + if (!(cpu_is_omap15xx())) + ARM_SAVE(ARM_IDLECT3); + ARM_SAVE(ARM_EWUPCT); + ARM_SAVE(ARM_RSTCT1); + ARM_SAVE(ARM_RSTCT2); + ARM_SAVE(ARM_SYSST); + + ULPD_SAVE(ULPD_IT_STATUS); + ULPD_SAVE(ULPD_CLOCK_CTRL); + ULPD_SAVE(ULPD_SOFT_REQ); + ULPD_SAVE(ULPD_STATUS_REQ); + ULPD_SAVE(ULPD_DPLL_CTRL); + ULPD_SAVE(ULPD_POWER_CTRL); + + if (cpu_is_omap730()) { + MPUI730_SAVE(MPUI_CTRL); + MPUI730_SAVE(MPUI_DSP_STATUS); + MPUI730_SAVE(MPUI_DSP_BOOT_CONFIG); + MPUI730_SAVE(MPUI_DSP_API_CONFIG); + MPUI730_SAVE(EMIFF_SDRAM_CONFIG); + MPUI730_SAVE(EMIFS_CONFIG); + } else if (cpu_is_omap15xx()) { + MPUI1510_SAVE(MPUI_CTRL); + MPUI1510_SAVE(MPUI_DSP_STATUS); + MPUI1510_SAVE(MPUI_DSP_BOOT_CONFIG); + MPUI1510_SAVE(MPUI_DSP_API_CONFIG); + MPUI1510_SAVE(EMIFF_SDRAM_CONFIG); + MPUI1510_SAVE(EMIFS_CONFIG); + } else if (cpu_is_omap16xx()) { + MPUI1610_SAVE(MPUI_CTRL); + MPUI1610_SAVE(MPUI_DSP_STATUS); + MPUI1610_SAVE(MPUI_DSP_BOOT_CONFIG); + MPUI1610_SAVE(MPUI_DSP_API_CONFIG); + MPUI1610_SAVE(EMIFF_SDRAM_CONFIG); + MPUI1610_SAVE(EMIFS_CONFIG); + } + + if (virtual_start == 0) { + g_read_completed = 0; + + my_buffer_offset += sprintf(my_base + my_buffer_offset, + "ARM_CKCTL_REG: 0x%-8x \n" + "ARM_IDLECT1_REG: 0x%-8x \n" + "ARM_IDLECT2_REG: 0x%-8x \n" + "ARM_IDLECT3_REG: 0x%-8x \n" + "ARM_EWUPCT_REG: 0x%-8x \n" + "ARM_RSTCT1_REG: 0x%-8x \n" + "ARM_RSTCT2_REG: 0x%-8x \n" + "ARM_SYSST_REG: 0x%-8x \n" + "ULPD_IT_STATUS_REG: 0x%-4x \n" + "ULPD_CLOCK_CTRL_REG: 0x%-4x \n" + "ULPD_SOFT_REQ_REG: 0x%-4x \n" + "ULPD_DPLL_CTRL_REG: 0x%-4x \n" + "ULPD_STATUS_REQ_REG: 0x%-4x \n" + "ULPD_POWER_CTRL_REG: 0x%-4x \n", + ARM_SHOW(ARM_CKCTL), + ARM_SHOW(ARM_IDLECT1), + ARM_SHOW(ARM_IDLECT2), + ARM_SHOW(ARM_IDLECT3), + ARM_SHOW(ARM_EWUPCT), + ARM_SHOW(ARM_RSTCT1), + ARM_SHOW(ARM_RSTCT2), + ARM_SHOW(ARM_SYSST), + ULPD_SHOW(ULPD_IT_STATUS), + ULPD_SHOW(ULPD_CLOCK_CTRL), + ULPD_SHOW(ULPD_SOFT_REQ), + ULPD_SHOW(ULPD_DPLL_CTRL), + ULPD_SHOW(ULPD_STATUS_REQ), + ULPD_SHOW(ULPD_POWER_CTRL)); + + if (cpu_is_omap730()) { + my_buffer_offset += sprintf(my_base + my_buffer_offset, + "MPUI730_CTRL_REG 0x%-8x \n" + "MPUI730_DSP_STATUS_REG: 0x%-8x \n" + "MPUI730_DSP_BOOT_CONFIG_REG: 0x%-8x \n" + "MPUI730_DSP_API_CONFIG_REG: 0x%-8x \n" + "MPUI730_SDRAM_CONFIG_REG: 0x%-8x \n" + "MPUI730_EMIFS_CONFIG_REG: 0x%-8x \n", + MPUI730_SHOW(MPUI_CTRL), + MPUI730_SHOW(MPUI_DSP_STATUS), + MPUI730_SHOW(MPUI_DSP_BOOT_CONFIG), + MPUI730_SHOW(MPUI_DSP_API_CONFIG), + MPUI730_SHOW(EMIFF_SDRAM_CONFIG), + MPUI730_SHOW(EMIFS_CONFIG)); + } else if (cpu_is_omap15xx()) { + my_buffer_offset += sprintf(my_base + my_buffer_offset, + "MPUI1510_CTRL_REG 0x%-8x \n" + "MPUI1510_DSP_STATUS_REG: 0x%-8x \n" + "MPUI1510_DSP_BOOT_CONFIG_REG: 0x%-8x \n" + "MPUI1510_DSP_API_CONFIG_REG: 0x%-8x \n" + "MPUI1510_SDRAM_CONFIG_REG: 0x%-8x \n" + "MPUI1510_EMIFS_CONFIG_REG: 0x%-8x \n", + MPUI1510_SHOW(MPUI_CTRL), + MPUI1510_SHOW(MPUI_DSP_STATUS), + MPUI1510_SHOW(MPUI_DSP_BOOT_CONFIG), + MPUI1510_SHOW(MPUI_DSP_API_CONFIG), + MPUI1510_SHOW(EMIFF_SDRAM_CONFIG), + MPUI1510_SHOW(EMIFS_CONFIG)); + } else if (cpu_is_omap16xx()) { + my_buffer_offset += sprintf(my_base + my_buffer_offset, + "MPUI1610_CTRL_REG 0x%-8x \n" + "MPUI1610_DSP_STATUS_REG: 0x%-8x \n" + "MPUI1610_DSP_BOOT_CONFIG_REG: 0x%-8x \n" + "MPUI1610_DSP_API_CONFIG_REG: 0x%-8x \n" + "MPUI1610_SDRAM_CONFIG_REG: 0x%-8x \n" + "MPUI1610_EMIFS_CONFIG_REG: 0x%-8x \n", + MPUI1610_SHOW(MPUI_CTRL), + MPUI1610_SHOW(MPUI_DSP_STATUS), + MPUI1610_SHOW(MPUI_DSP_BOOT_CONFIG), + MPUI1610_SHOW(MPUI_DSP_API_CONFIG), + MPUI1610_SHOW(EMIFF_SDRAM_CONFIG), + MPUI1610_SHOW(EMIFS_CONFIG)); + } + + g_read_completed++; + } else if (g_read_completed >= 1) { + *eof = 1; + return 0; + } + g_read_completed++; + + *my_first_byte = page_buffer; + return my_buffer_offset; +} + +static void omap_pm_init_proc(void) +{ + struct proc_dir_entry *entry; + + entry = create_proc_read_entry("driver/omap_pm", + S_IWUSR | S_IRUGO, NULL, + omap_pm_read_proc, NULL); +} + +#endif /* DEBUG && CONFIG_PROC_FS */ + +static void (*saved_idle)(void) = NULL; + +/* + * omap_pm_prepare - Do preliminary suspend work. + * @state: suspend state we're entering. + * + */ +static int omap_pm_prepare(suspend_state_t state) +{ + int error = 0; + + /* We cannot sleep in idle until we have resumed */ + saved_idle = pm_idle; + pm_idle = NULL; + + switch (state) + { + case PM_SUSPEND_STANDBY: + case PM_SUSPEND_MEM: + break; + + case PM_SUSPEND_DISK: + return -ENOTSUPP; + + default: + return -EINVAL; + } + + return error; +} + + +/* + * omap_pm_enter - Actually enter a sleep state. + * @state: State we're entering. + * + */ + +static int omap_pm_enter(suspend_state_t state) +{ + switch (state) + { + case PM_SUSPEND_STANDBY: + case PM_SUSPEND_MEM: + omap_pm_suspend(); + break; + + case PM_SUSPEND_DISK: + return -ENOTSUPP; + + default: + return -EINVAL; + } + + return 0; +} + + +/** + * omap_pm_finish - Finish up suspend sequence. + * @state: State we're coming out of. + * + * This is called after we wake back up (or if entering the sleep state + * failed). + */ + +static int omap_pm_finish(suspend_state_t state) +{ + pm_idle = saved_idle; + return 0; +} + + +static irqreturn_t omap_wakeup_interrupt(int irq, void * dev, + struct pt_regs * regs) +{ + return IRQ_HANDLED; +} + +static struct irqaction omap_wakeup_irq = { + .name = "peripheral wakeup", + .flags = SA_INTERRUPT, + .handler = omap_wakeup_interrupt +}; + + + +static struct pm_ops omap_pm_ops ={ + .pm_disk_mode = 0, + .prepare = omap_pm_prepare, + .enter = omap_pm_enter, + .finish = omap_pm_finish, +}; + +static int __init omap_pm_init(void) +{ + printk("Power Management for TI OMAP.\n"); + + /* + * We copy the assembler sleep/wakeup routines to SRAM. + * These routines need to be in SRAM as that's the only + * memory the MPU can see when it wakes up. + */ + if (cpu_is_omap730()) { + omap_sram_idle = omap_sram_push(omap730_idle_loop_suspend, + omap730_idle_loop_suspend_sz); + omap_sram_suspend = omap_sram_push(omap730_cpu_suspend, + omap730_cpu_suspend_sz); + } else if (cpu_is_omap15xx()) { + omap_sram_idle = omap_sram_push(omap1510_idle_loop_suspend, + omap1510_idle_loop_suspend_sz); + omap_sram_suspend = omap_sram_push(omap1510_cpu_suspend, + omap1510_cpu_suspend_sz); + } else if (cpu_is_omap16xx()) { + omap_sram_idle = omap_sram_push(omap1610_idle_loop_suspend, + omap1610_idle_loop_suspend_sz); + omap_sram_suspend = omap_sram_push(omap1610_cpu_suspend, + omap1610_cpu_suspend_sz); + } + + if (omap_sram_idle == NULL || omap_sram_suspend == NULL) { + printk(KERN_ERR "PM not initialized: Missing SRAM support\n"); + return -ENODEV; + } + + pm_idle = omap_pm_idle; + + if (cpu_is_omap730()) + setup_irq(INT_730_WAKE_UP_REQ, &omap_wakeup_irq); + else if (cpu_is_omap16xx()) + setup_irq(INT_1610_WAKE_UP_REQ, &omap_wakeup_irq); + + /* Program new power ramp-up time + * (0 for most boards since we don't lower voltage when in deep sleep) + */ + omap_writew(ULPD_SETUP_ANALOG_CELL_3_VAL, ULPD_SETUP_ANALOG_CELL_3); + + /* Setup ULPD POWER_CTRL_REG - enter deep sleep whenever possible */ + omap_writew(ULPD_POWER_CTRL_REG_VAL, ULPD_POWER_CTRL); + + /* Configure IDLECT3 */ + if (cpu_is_omap730()) + omap_writel(OMAP730_IDLECT3_VAL, OMAP730_IDLECT3); + else if (cpu_is_omap16xx()) + omap_writel(OMAP1610_IDLECT3_VAL, OMAP1610_IDLECT3); + + pm_set_ops(&omap_pm_ops); + +#if defined(DEBUG) && defined(CONFIG_PROC_FS) + omap_pm_init_proc(); +#endif + + subsys_create_file(&power_subsys, &sleep_while_idle_attr); + + if (cpu_is_omap16xx()) { + /* configure LOW_PWR pin */ + omap_cfg_reg(T20_1610_LOW_PWR); + } + + return 0; +} +__initcall(omap_pm_init); diff --git a/arch/arm/mach-omap1/serial.c b/arch/arm/mach-omap1/serial.c index e924e0c6a4ce..9b4cd698bec8 100644 --- a/arch/arm/mach-omap1/serial.c +++ b/arch/arm/mach-omap1/serial.c @@ -30,9 +30,9 @@ #include #endif -static struct clk * uart1_ck = NULL; -static struct clk * uart2_ck = NULL; -static struct clk * uart3_ck = NULL; +static struct clk * uart1_ck; +static struct clk * uart2_ck; +static struct clk * uart3_ck; static inline unsigned int omap_serial_in(struct plat_serial8250_port *up, int offset) diff --git a/arch/arm/plat-omap/sleep.S b/arch/arm/mach-omap1/sleep.S similarity index 93% rename from arch/arm/plat-omap/sleep.S rename to arch/arm/mach-omap1/sleep.S index 4cd7d292f854..e58295e2d3b2 100644 --- a/arch/arm/plat-omap/sleep.S +++ b/arch/arm/mach-omap1/sleep.S @@ -1,5 +1,5 @@ /* - * linux/arch/arm/plat-omap/sleep.S + * linux/arch/arm/mach-omap1/sleep.S * * Low-level OMAP730/1510/1610 sleep/wakeUp support * @@ -383,60 +383,133 @@ ENTRY(omap1610_cpu_suspend) mcr p15, 0, r0, c7, c10, 4 nop - @ load base address of Traffic Controller + @ Load base address of Traffic Controller mov r6, #TCMIF_ASM_BASE & 0xff000000 orr r6, r6, #TCMIF_ASM_BASE & 0x00ff0000 orr r6, r6, #TCMIF_ASM_BASE & 0x0000ff00 - @ prepare to put SDRAM into self-refresh manually + @ Prepare to put SDRAM into self-refresh manually ldr r7, [r6, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] orr r9, r7, #SELF_REFRESH_MODE & 0xff000000 orr r9, r9, #SELF_REFRESH_MODE & 0x000000ff str r9, [r6, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] - @ prepare to put EMIFS to Sleep + @ Prepare to put EMIFS to Sleep ldr r8, [r6, #EMIFS_CONFIG_ASM_OFFSET & 0xff] orr r9, r8, #IDLE_EMIFS_REQUEST & 0xff str r9, [r6, #EMIFS_CONFIG_ASM_OFFSET & 0xff] - @ load base address of ARM_IDLECT1 and ARM_IDLECT2 + @ Load base address of ARM_IDLECT1 and ARM_IDLECT2 mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000 orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000 orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00 - @ turn off clock domains - @ do not disable PERCK (0x04) + @ Turn off clock domains + @ Do not disable PERCK (0x04) mov r5, #OMAP1610_IDLECT2_SLEEP_VAL & 0xff orr r5, r5, #OMAP1610_IDLECT2_SLEEP_VAL & 0xff00 strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] - @ request ARM idle + @ Request ARM idle mov r3, #OMAP1610_IDLECT1_SLEEP_VAL & 0xff orr r3, r3, #OMAP1610_IDLECT1_SLEEP_VAL & 0xff00 strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] - @ disable instruction cache - mrc p15, 0, r9, c1, c0, 0 - bic r2, r9, #0x1000 - mcr p15, 0, r2, c1, c0, 0 - nop - /* * Let's wait for the next wake up event to wake us up. r0 can't be * used here because r0 holds ARM_IDLECT1 */ mov r2, #0 mcr p15, 0, r2, c7, c0, 4 @ wait for interrupt + + @ Errata (HEL3SU467, section 1.4.4) specifies nop-instructions + @ according to this formula: + @ 2 + (4*DPLL_MULT)/DPLL_DIV/ARMDIV + @ Max DPLL_MULT = 18 + @ DPLL_DIV = 1 + @ ARMDIV = 1 + @ => 74 nop-instructions + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop @10 + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop @20 + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop @30 + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop @40 + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop @50 + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop @60 + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop @70 + nop + nop + nop + nop @74 /* * omap1610_cpu_suspend()'s resume point. * * It will just start executing here, so we'll restore stuff from the * stack. */ - @ re-enable Icache - mcr p15, 0, r9, c1, c0, 0 - - @ reset the ARM_IDLECT1 and ARM_IDLECT2. + @ Restore the ARM_IDLECT1 and ARM_IDLECT2. strh r1, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] strh r0, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] @@ -444,7 +517,7 @@ ENTRY(omap1610_cpu_suspend) str r7, [r6, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] str r8, [r6, #EMIFS_CONFIG_ASM_OFFSET & 0xff] - @ restore regs and return + @ Restore regs and return ldmfd sp!, {r0 - r12, pc} ENTRY(omap1610_cpu_suspend_sz) diff --git a/arch/arm/mach-omap1/time.c b/arch/arm/mach-omap1/time.c index cdbf4d7620c6..a85fe6066bc4 100644 --- a/arch/arm/mach-omap1/time.c +++ b/arch/arm/mach-omap1/time.c @@ -51,8 +51,6 @@ struct sys_timer omap_timer; -#ifdef CONFIG_OMAP_MPU_TIMER - /* * --------------------------------------------------------------------------- * MPU timer @@ -222,195 +220,6 @@ unsigned long long sched_clock(void) return cycles_2_ns(ticks64); } -#endif /* CONFIG_OMAP_MPU_TIMER */ - -#ifdef CONFIG_OMAP_32K_TIMER - -#ifdef CONFIG_ARCH_OMAP15XX -#error OMAP 32KHz timer does not currently work on 15XX! -#endif - -/* - * --------------------------------------------------------------------------- - * 32KHz OS timer - * - * This currently works only on 16xx, as 1510 does not have the continuous - * 32KHz synchronous timer. The 32KHz synchronous timer is used to keep track - * of time in addition to the 32KHz OS timer. Using only the 32KHz OS timer - * on 1510 would be possible, but the timer would not be as accurate as - * with the 32KHz synchronized timer. - * --------------------------------------------------------------------------- - */ -#define OMAP_32K_TIMER_BASE 0xfffb9000 -#define OMAP_32K_TIMER_CR 0x08 -#define OMAP_32K_TIMER_TVR 0x00 -#define OMAP_32K_TIMER_TCR 0x04 - -#define OMAP_32K_TICKS_PER_HZ (32768 / HZ) - -/* - * TRM says 1 / HZ = ( TVR + 1) / 32768, so TRV = (32768 / HZ) - 1 - * so with HZ = 100, TVR = 327.68. - */ -#define OMAP_32K_TIMER_TICK_PERIOD ((32768 / HZ) - 1) -#define TIMER_32K_SYNCHRONIZED 0xfffbc410 - -#define JIFFIES_TO_HW_TICKS(nr_jiffies, clock_rate) \ - (((nr_jiffies) * (clock_rate)) / HZ) - -static inline void omap_32k_timer_write(int val, int reg) -{ - omap_writew(val, reg + OMAP_32K_TIMER_BASE); -} - -static inline unsigned long omap_32k_timer_read(int reg) -{ - return omap_readl(reg + OMAP_32K_TIMER_BASE) & 0xffffff; -} - -/* - * The 32KHz synchronized timer is an additional timer on 16xx. - * It is always running. - */ -static inline unsigned long omap_32k_sync_timer_read(void) -{ - return omap_readl(TIMER_32K_SYNCHRONIZED); -} - -static inline void omap_32k_timer_start(unsigned long load_val) -{ - omap_32k_timer_write(load_val, OMAP_32K_TIMER_TVR); - omap_32k_timer_write(0x0f, OMAP_32K_TIMER_CR); -} - -static inline void omap_32k_timer_stop(void) -{ - omap_32k_timer_write(0x0, OMAP_32K_TIMER_CR); -} - -/* - * Rounds down to nearest usec. Note that this will overflow for larger values. - */ -static inline unsigned long omap_32k_ticks_to_usecs(unsigned long ticks_32k) -{ - return (ticks_32k * 5*5*5*5*5*5) >> 9; -} - -/* - * Rounds down to nearest nsec. - */ -static inline unsigned long long -omap_32k_ticks_to_nsecs(unsigned long ticks_32k) -{ - return (unsigned long long) ticks_32k * 1000 * 5*5*5*5*5*5 >> 9; -} - -static unsigned long omap_32k_last_tick = 0; - -/* - * Returns elapsed usecs since last 32k timer interrupt - */ -static unsigned long omap_32k_timer_gettimeoffset(void) -{ - unsigned long now = omap_32k_sync_timer_read(); - return omap_32k_ticks_to_usecs(now - omap_32k_last_tick); -} - -/* - * Returns current time from boot in nsecs. It's OK for this to wrap - * around for now, as it's just a relative time stamp. - */ -unsigned long long sched_clock(void) -{ - return omap_32k_ticks_to_nsecs(omap_32k_sync_timer_read()); -} - -/* - * Timer interrupt for 32KHz timer. When dynamic tick is enabled, this - * function is also called from other interrupts to remove latency - * issues with dynamic tick. In the dynamic tick case, we need to lock - * with irqsave. - */ -static irqreturn_t omap_32k_timer_interrupt(int irq, void *dev_id, - struct pt_regs *regs) -{ - unsigned long flags; - unsigned long now; - - write_seqlock_irqsave(&xtime_lock, flags); - now = omap_32k_sync_timer_read(); - - while (now - omap_32k_last_tick >= OMAP_32K_TICKS_PER_HZ) { - omap_32k_last_tick += OMAP_32K_TICKS_PER_HZ; - timer_tick(regs); - } - - /* Restart timer so we don't drift off due to modulo or dynamic tick. - * By default we program the next timer to be continuous to avoid - * latencies during high system load. During dynamic tick operation the - * continuous timer can be overridden from pm_idle to be longer. - */ - omap_32k_timer_start(omap_32k_last_tick + OMAP_32K_TICKS_PER_HZ - now); - write_sequnlock_irqrestore(&xtime_lock, flags); - - return IRQ_HANDLED; -} - -#ifdef CONFIG_NO_IDLE_HZ -/* - * Programs the next timer interrupt needed. Called when dynamic tick is - * enabled, and to reprogram the ticks to skip from pm_idle. Note that - * we can keep the timer continuous, and don't need to set it to run in - * one-shot mode. This is because the timer will get reprogrammed again - * after next interrupt. - */ -void omap_32k_timer_reprogram(unsigned long next_tick) -{ - omap_32k_timer_start(JIFFIES_TO_HW_TICKS(next_tick, 32768) + 1); -} - -static struct irqaction omap_32k_timer_irq; -extern struct timer_update_handler timer_update; - -static int omap_32k_timer_enable_dyn_tick(void) -{ - /* No need to reprogram timer, just use the next interrupt */ - return 0; -} - -static int omap_32k_timer_disable_dyn_tick(void) -{ - omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD); - return 0; -} - -static struct dyn_tick_timer omap_dyn_tick_timer = { - .enable = omap_32k_timer_enable_dyn_tick, - .disable = omap_32k_timer_disable_dyn_tick, - .reprogram = omap_32k_timer_reprogram, - .handler = omap_32k_timer_interrupt, -}; -#endif /* CONFIG_NO_IDLE_HZ */ - -static struct irqaction omap_32k_timer_irq = { - .name = "32KHz timer", - .flags = SA_INTERRUPT | SA_TIMER, - .handler = omap_32k_timer_interrupt, -}; - -static __init void omap_init_32k_timer(void) -{ - -#ifdef CONFIG_NO_IDLE_HZ - omap_timer.dyn_tick = &omap_dyn_tick_timer; -#endif - - setup_irq(INT_OS_TIMER, &omap_32k_timer_irq); - omap_timer.offset = omap_32k_timer_gettimeoffset; - omap_32k_last_tick = omap_32k_sync_timer_read(); - omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD); -} -#endif /* CONFIG_OMAP_32K_TIMER */ /* * --------------------------------------------------------------------------- @@ -419,13 +228,7 @@ static __init void omap_init_32k_timer(void) */ static void __init omap_timer_init(void) { -#if defined(CONFIG_OMAP_MPU_TIMER) omap_init_mpu_timer(); -#elif defined(CONFIG_OMAP_32K_TIMER) - omap_init_32k_timer(); -#else -#error No system timer selected in Kconfig! -#endif } struct sys_timer omap_timer = { diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig index 578880943cf2..537dd2e6d380 100644 --- a/arch/arm/mach-omap2/Kconfig +++ b/arch/arm/mach-omap2/Kconfig @@ -20,3 +20,6 @@ config MACH_OMAP_H4 bool "OMAP 2420 H4 board" depends on ARCH_OMAP2 && ARCH_OMAP24XX +config MACH_OMAP_APOLLON + bool "OMAP 2420 Apollon board" + depends on ARCH_OMAP2 && ARCH_OMAP24XX diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index 42041166435c..111eaa64258f 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -3,11 +3,15 @@ # # Common support -obj-y := irq.o id.o io.o sram-fn.o clock.o mux.o devices.o serial.o +obj-y := irq.o id.o io.o sram-fn.o memory.o prcm.o clock.o mux.o devices.o serial.o obj-$(CONFIG_OMAP_MPU_TIMER) += timer-gp.o +# Power Management +obj-$(CONFIG_PM) += pm.o sleep.o + # Specific board support obj-$(CONFIG_MACH_OMAP_GENERIC) += board-generic.o obj-$(CONFIG_MACH_OMAP_H4) += board-h4.o +obj-$(CONFIG_MACH_OMAP_APOLLON) += board-apollon.o diff --git a/arch/arm/mach-omap2/board-apollon.c b/arch/arm/mach-omap2/board-apollon.c new file mode 100644 index 000000000000..6c6ba172cdf6 --- /dev/null +++ b/arch/arm/mach-omap2/board-apollon.c @@ -0,0 +1,285 @@ +/* + * linux/arch/arm/mach-omap/omap2/board-apollon.c + * + * Copyright (C) 2005,2006 Samsung Electronics + * Author: Kyungmin Park + * + * Modified from mach-omap/omap2/board-h4.c + * + * Code for apollon OMAP2 board. Should work on many OMAP2 systems where + * the bootloader passes the board-specific data to the kernel. + * Do not put any board specific code to this file; create a new machine + * type if you need custom low-level initializations. + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "prcm-regs.h" + +/* LED & Switch macros */ +#define LED0_GPIO13 13 +#define LED1_GPIO14 14 +#define LED2_GPIO15 15 +#define SW_ENTER_GPIO16 16 +#define SW_UP_GPIO17 17 +#define SW_DOWN_GPIO58 58 + +static struct mtd_partition apollon_partitions[] = { + { + .name = "X-Loader + U-Boot", + .offset = 0, + .size = SZ_128K, + .mask_flags = MTD_WRITEABLE, + }, + { + .name = "params", + .offset = MTDPART_OFS_APPEND, + .size = SZ_128K, + }, + { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = SZ_2M, + }, + { + .name = "rootfs", + .offset = MTDPART_OFS_APPEND, + .size = SZ_16M, + }, + { + .name = "filesystem00", + .offset = MTDPART_OFS_APPEND, + .size = SZ_32M, + }, + { + .name = "filesystem01", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + }, +}; + +static struct flash_platform_data apollon_flash_data = { + .parts = apollon_partitions, + .nr_parts = ARRAY_SIZE(apollon_partitions), +}; + +static struct resource apollon_flash_resource = { + .start = APOLLON_CS0_BASE, + .end = APOLLON_CS0_BASE + SZ_128K, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device apollon_onenand_device = { + .name = "onenand", + .id = -1, + .dev = { + .platform_data = &apollon_flash_data, + }, + .num_resources = ARRAY_SIZE(&apollon_flash_resource), + .resource = &apollon_flash_resource, +}; + +static struct resource apollon_smc91x_resources[] = { + [0] = { + .start = APOLLON_ETHR_START, /* Physical */ + .end = APOLLON_ETHR_START + 0xf, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = OMAP_GPIO_IRQ(APOLLON_ETHR_GPIO_IRQ), + .end = OMAP_GPIO_IRQ(APOLLON_ETHR_GPIO_IRQ), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device apollon_smc91x_device = { + .name = "smc91x", + .id = -1, + .num_resources = ARRAY_SIZE(apollon_smc91x_resources), + .resource = apollon_smc91x_resources, +}; + +static struct platform_device apollon_lcd_device = { + .name = "apollon_lcd", + .id = -1, +}; + +static struct platform_device *apollon_devices[] __initdata = { + &apollon_onenand_device, + &apollon_smc91x_device, + &apollon_lcd_device, +}; + +static inline void __init apollon_init_smc91x(void) +{ + /* Make sure CS1 timings are correct */ + GPMC_CONFIG1_1 = 0x00011203; + GPMC_CONFIG2_1 = 0x001f1f01; + GPMC_CONFIG3_1 = 0x00080803; + GPMC_CONFIG4_1 = 0x1c091c09; + GPMC_CONFIG5_1 = 0x041f1f1f; + GPMC_CONFIG6_1 = 0x000004c4; + GPMC_CONFIG7_1 = 0x00000f40 | (APOLLON_CS1_BASE >> 24); + udelay(100); + + omap_cfg_reg(W4__24XX_GPIO74); + if (omap_request_gpio(APOLLON_ETHR_GPIO_IRQ) < 0) { + printk(KERN_ERR "Failed to request GPIO%d for smc91x IRQ\n", + APOLLON_ETHR_GPIO_IRQ); + return; + } + omap_set_gpio_direction(APOLLON_ETHR_GPIO_IRQ, 1); +} + +static void __init omap_apollon_init_irq(void) +{ + omap2_init_common_hw(); + omap_init_irq(); + omap_gpio_init(); + apollon_init_smc91x(); +} + +static struct omap_uart_config apollon_uart_config __initdata = { + .enabled_uarts = (1 << 0) | (0 << 1) | (0 << 2), +}; + +static struct omap_mmc_config apollon_mmc_config __initdata = { + .mmc [0] = { + .enabled = 0, + .wire4 = 0, + .wp_pin = -1, + .power_pin = -1, + .switch_pin = -1, + }, +}; + +static struct omap_lcd_config apollon_lcd_config __initdata = { + .ctrl_name = "internal", +}; + +static struct omap_board_config_kernel apollon_config[] = { + { OMAP_TAG_UART, &apollon_uart_config }, + { OMAP_TAG_MMC, &apollon_mmc_config }, + { OMAP_TAG_LCD, &apollon_lcd_config }, +}; + +static void __init apollon_led_init(void) +{ + /* LED0 - AA10 */ + omap_cfg_reg(AA10_242X_GPIO13); + omap_request_gpio(LED0_GPIO13); + omap_set_gpio_direction(LED0_GPIO13, 0); + omap_set_gpio_dataout(LED0_GPIO13, 0); + /* LED1 - AA6 */ + omap_cfg_reg(AA6_242X_GPIO14); + omap_request_gpio(LED1_GPIO14); + omap_set_gpio_direction(LED1_GPIO14, 0); + omap_set_gpio_dataout(LED1_GPIO14, 0); + /* LED2 - AA4 */ + omap_cfg_reg(AA4_242X_GPIO15); + omap_request_gpio(LED2_GPIO15); + omap_set_gpio_direction(LED2_GPIO15, 0); + omap_set_gpio_dataout(LED2_GPIO15, 0); +} + +static irqreturn_t apollon_sw_interrupt(int irq, void *ignored, struct pt_regs *regs) +{ + static unsigned int led0, led1, led2; + + if (irq == OMAP_GPIO_IRQ(SW_ENTER_GPIO16)) + omap_set_gpio_dataout(LED0_GPIO13, led0 ^= 1); + else if (irq == OMAP_GPIO_IRQ(SW_UP_GPIO17)) + omap_set_gpio_dataout(LED1_GPIO14, led1 ^= 1); + else if (irq == OMAP_GPIO_IRQ(SW_DOWN_GPIO58)) + omap_set_gpio_dataout(LED2_GPIO15, led2 ^= 1); + + return IRQ_HANDLED; +} + +static void __init apollon_sw_init(void) +{ + /* Enter SW - Y11 */ + omap_cfg_reg(Y11_242X_GPIO16); + omap_request_gpio(SW_ENTER_GPIO16); + omap_set_gpio_direction(SW_ENTER_GPIO16, 1); + /* Up SW - AA12 */ + omap_cfg_reg(AA12_242X_GPIO17); + omap_request_gpio(SW_UP_GPIO17); + omap_set_gpio_direction(SW_UP_GPIO17, 1); + /* Down SW - AA8 */ + omap_cfg_reg(AA8_242X_GPIO58); + omap_request_gpio(SW_DOWN_GPIO58); + omap_set_gpio_direction(SW_DOWN_GPIO58, 1); + + set_irq_type(OMAP_GPIO_IRQ(SW_ENTER_GPIO16), IRQT_RISING); + if (request_irq(OMAP_GPIO_IRQ(SW_ENTER_GPIO16), &apollon_sw_interrupt, + SA_SHIRQ, "enter sw", + &apollon_sw_interrupt)) + return; + set_irq_type(OMAP_GPIO_IRQ(SW_UP_GPIO17), IRQT_RISING); + if (request_irq(OMAP_GPIO_IRQ(SW_UP_GPIO17), &apollon_sw_interrupt, + SA_SHIRQ, "up sw", + &apollon_sw_interrupt)) + return; + set_irq_type(OMAP_GPIO_IRQ(SW_DOWN_GPIO58), IRQT_RISING); + if (request_irq(OMAP_GPIO_IRQ(SW_DOWN_GPIO58), &apollon_sw_interrupt, + SA_SHIRQ, "down sw", + &apollon_sw_interrupt)) + return; +} + +static void __init omap_apollon_init(void) +{ + apollon_led_init(); + apollon_sw_init(); + + /* REVISIT: where's the correct place */ + omap_cfg_reg(W19_24XX_SYS_NIRQ); + + /* + * Make sure the serial ports are muxed on at this point. + * You have to mux them off in device drivers later on + * if not needed. + */ + platform_add_devices(apollon_devices, ARRAY_SIZE(apollon_devices)); + omap_board_config = apollon_config; + omap_board_config_size = ARRAY_SIZE(apollon_config); + omap_serial_init(); +} + +static void __init omap_apollon_map_io(void) +{ + omap2_map_common_io(); +} + +MACHINE_START(OMAP_APOLLON, "OMAP24xx Apollon") + /* Maintainer: Kyungmin Park */ + .phys_io = 0x48000000, + .io_pg_offst = ((0xd8000000) >> 18) & 0xfffc, + .boot_params = 0x80000100, + .map_io = omap_apollon_map_io, + .init_irq = omap_apollon_init_irq, + .init_machine = omap_apollon_init, + .timer = &omap_timer, +MACHINE_END diff --git a/arch/arm/mach-omap2/board-h4.c b/arch/arm/mach-omap2/board-h4.c index a300d634d8a5..4933fce766c8 100644 --- a/arch/arm/mach-omap2/board-h4.c +++ b/arch/arm/mach-omap2/board-h4.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include @@ -25,15 +27,57 @@ #include #include +#include #include #include +#include #include #include -#include +#include +#include +#include +#include "prcm-regs.h" #include #include +static unsigned int row_gpios[6] = { 88, 89, 124, 11, 6, 96 }; +static unsigned int col_gpios[7] = { 90, 91, 100, 36, 12, 97, 98 }; + +static int h4_keymap[] = { + KEY(0, 0, KEY_LEFT), + KEY(0, 1, KEY_RIGHT), + KEY(0, 2, KEY_A), + KEY(0, 3, KEY_B), + KEY(0, 4, KEY_C), + KEY(1, 0, KEY_DOWN), + KEY(1, 1, KEY_UP), + KEY(1, 2, KEY_E), + KEY(1, 3, KEY_F), + KEY(1, 4, KEY_G), + KEY(2, 0, KEY_ENTER), + KEY(2, 1, KEY_I), + KEY(2, 2, KEY_J), + KEY(2, 3, KEY_K), + KEY(2, 4, KEY_3), + KEY(3, 0, KEY_M), + KEY(3, 1, KEY_N), + KEY(3, 2, KEY_O), + KEY(3, 3, KEY_P), + KEY(3, 4, KEY_Q), + KEY(4, 0, KEY_R), + KEY(4, 1, KEY_4), + KEY(4, 2, KEY_T), + KEY(4, 3, KEY_U), + KEY(4, 4, KEY_ENTER), + KEY(5, 0, KEY_V), + KEY(5, 1, KEY_W), + KEY(5, 2, KEY_L), + KEY(5, 3, KEY_S), + KEY(5, 4, KEY_ENTER), + 0 +}; + static struct mtd_partition h4_partitions[] = { /* bootloader (U-Boot, etc) in first sector */ { @@ -108,9 +152,123 @@ static struct platform_device h4_smc91x_device = { .resource = h4_smc91x_resources, }; +/* Select between the IrDA and aGPS module + */ +static int h4_select_irda(struct device *dev, int state) +{ + unsigned char expa; + int err = 0; + + if ((err = read_gpio_expa(&expa, 0x21))) { + printk(KERN_ERR "Error reading from I/O expander\n"); + return err; + } + + /* 'P6' enable/disable IRDA_TX and IRDA_RX */ + if (state & IR_SEL) { /* IrDa */ + if ((err = write_gpio_expa(expa | 0x01, 0x21))) { + printk(KERN_ERR "Error writing to I/O expander\n"); + return err; + } + } else { + if ((err = write_gpio_expa(expa & ~0x01, 0x21))) { + printk(KERN_ERR "Error writing to I/O expander\n"); + return err; + } + } + return err; +} + +static void set_trans_mode(void *data) +{ + int *mode = data; + unsigned char expa; + int err = 0; + + if ((err = read_gpio_expa(&expa, 0x20)) != 0) { + printk(KERN_ERR "Error reading from I/O expander\n"); + } + + expa &= ~0x01; + + if (!(*mode & IR_SIRMODE)) { /* MIR/FIR */ + expa |= 0x01; + } + + if ((err = write_gpio_expa(expa, 0x20)) != 0) { + printk(KERN_ERR "Error writing to I/O expander\n"); + } +} + +static int h4_transceiver_mode(struct device *dev, int mode) +{ + struct omap_irda_config *irda_config = dev->platform_data; + + cancel_delayed_work(&irda_config->gpio_expa); + PREPARE_WORK(&irda_config->gpio_expa, set_trans_mode, &mode); + schedule_work(&irda_config->gpio_expa); + + return 0; +} + +static struct omap_irda_config h4_irda_data = { + .transceiver_cap = IR_SIRMODE | IR_MIRMODE | IR_FIRMODE, + .transceiver_mode = h4_transceiver_mode, + .select_irda = h4_select_irda, + .rx_channel = OMAP24XX_DMA_UART3_RX, + .tx_channel = OMAP24XX_DMA_UART3_TX, + .dest_start = OMAP_UART3_BASE, + .src_start = OMAP_UART3_BASE, + .tx_trigger = OMAP24XX_DMA_UART3_TX, + .rx_trigger = OMAP24XX_DMA_UART3_RX, +}; + +static struct resource h4_irda_resources[] = { + [0] = { + .start = INT_24XX_UART3_IRQ, + .end = INT_24XX_UART3_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device h4_irda_device = { + .name = "omapirda", + .id = -1, + .dev = { + .platform_data = &h4_irda_data, + }, + .num_resources = 1, + .resource = h4_irda_resources, +}; + +static struct omap_kp_platform_data h4_kp_data = { + .rows = 6, + .cols = 7, + .keymap = h4_keymap, + .rep = 1, + .row_gpios = row_gpios, + .col_gpios = col_gpios, +}; + +static struct platform_device h4_kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &h4_kp_data, + }, +}; + +static struct platform_device h4_lcd_device = { + .name = "lcd_h4", + .id = -1, +}; + static struct platform_device *h4_devices[] __initdata = { &h4_smc91x_device, &h4_flash_device, + &h4_irda_device, + &h4_kp_device, + &h4_lcd_device, }; static inline void __init h4_init_smc91x(void) @@ -157,7 +315,6 @@ static struct omap_mmc_config h4_mmc_config __initdata = { }; static struct omap_lcd_config h4_lcd_config __initdata = { - .panel_name = "h4", .ctrl_name = "internal", }; @@ -174,6 +331,19 @@ static void __init omap_h4_init(void) * You have to mux them off in device drivers later on * if not needed. */ +#if defined(CONFIG_OMAP_IR) || defined(CONFIG_OMAP_IR_MODULE) + omap_cfg_reg(K15_24XX_UART3_TX); + omap_cfg_reg(K14_24XX_UART3_RX); +#endif + +#if defined(CONFIG_KEYBOARD_OMAP) || defined(CONFIG_KEYBOARD_OMAP_MODULE) + if (omap_has_menelaus()) { + row_gpios[5] = 0; + col_gpios[2] = 15; + col_gpios[6] = 18; + } +#endif + platform_add_devices(h4_devices, ARRAY_SIZE(h4_devices)); omap_board_config = h4_config; omap_board_config_size = ARRAY_SIZE(h4_config); diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c index 180f675c9064..72eb4bf571ac 100644 --- a/arch/arm/mach-omap2/clock.c +++ b/arch/arm/mach-omap2/clock.c @@ -28,14 +28,14 @@ #include #include -#include +#include "prcm-regs.h" +#include "memory.h" #include "clock.h" //#define DOWN_VARIABLE_DPLL 1 /* Experimental */ static struct prcm_config *curr_prcm_set; -static struct memory_timings mem_timings; static u32 curr_perf_level = PRCM_FULL_SPEED; /*------------------------------------------------------------------------- @@ -54,11 +54,13 @@ static void omap2_sys_clk_recalc(struct clk * clk) static u32 omap2_get_dpll_rate(struct clk * tclk) { - int dpll_clk, dpll_mult, dpll_div, amult; + long long dpll_clk; + int dpll_mult, dpll_div, amult; dpll_mult = (CM_CLKSEL1_PLL >> 12) & 0x03ff; /* 10 bits */ dpll_div = (CM_CLKSEL1_PLL >> 8) & 0x0f; /* 4 bits */ - dpll_clk = (tclk->parent->rate * dpll_mult) / (dpll_div + 1); + dpll_clk = (long long)tclk->parent->rate * dpll_mult; + do_div(dpll_clk, dpll_div + 1); amult = CM_CLKSEL2_PLL & 0x3; dpll_clk *= amult; @@ -385,75 +387,23 @@ static u32 omap2_dll_force_needed(void) return 0; } -static void omap2_init_memory_params(u32 force_lock_to_unlock_mode) -{ - unsigned long dll_cnt; - u32 fast_dll = 0; - - mem_timings.m_type = !((SDRC_MR_0 & 0x3) == 0x1); /* DDR = 1, SDR = 0 */ - - /* 2422 es2.05 and beyond has a single SIP DDR instead of 2 like others. - * In the case of 2422, its ok to use CS1 instead of CS0. - */ - -#if 0 /* FIXME: Enable after 24xx cpu detection works */ - ctype = get_cpu_type(); - if (cpu_is_omap2422()) - mem_timings.base_cs = 1; - else -#endif - mem_timings.base_cs = 0; - - if (mem_timings.m_type != M_DDR) - return; - - /* With DDR we need to determine the low frequency DLL value */ - if (((mem_timings.fast_dll_ctrl & (1 << 2)) == M_LOCK_CTRL)) - mem_timings.dll_mode = M_UNLOCK; - else - mem_timings.dll_mode = M_LOCK; - - if (mem_timings.base_cs == 0) { - fast_dll = SDRC_DLLA_CTRL; - dll_cnt = SDRC_DLLA_STATUS & 0xff00; - } else { - fast_dll = SDRC_DLLB_CTRL; - dll_cnt = SDRC_DLLB_STATUS & 0xff00; - } - if (force_lock_to_unlock_mode) { - fast_dll &= ~0xff00; - fast_dll |= dll_cnt; /* Current lock mode */ - } - mem_timings.fast_dll_ctrl = fast_dll; - - /* No disruptions, DDR will be offline & C-ABI not followed */ - omap2_sram_ddr_init(&mem_timings.slow_dll_ctrl, - mem_timings.fast_dll_ctrl, - mem_timings.base_cs, - force_lock_to_unlock_mode); - mem_timings.slow_dll_ctrl &= 0xff00; /* Keep lock value */ - - /* Turn status into unlock ctrl */ - mem_timings.slow_dll_ctrl |= - ((mem_timings.fast_dll_ctrl & 0xF) | (1 << 2)); - - /* 90 degree phase for anything below 133Mhz */ - mem_timings.slow_dll_ctrl |= (1 << 1); -} - static u32 omap2_reprogram_sdrc(u32 level, u32 force) { + u32 slow_dll_ctrl, fast_dll_ctrl, m_type; u32 prev = curr_perf_level, flags; if ((curr_perf_level == level) && !force) return prev; + m_type = omap2_memory_get_type(); + slow_dll_ctrl = omap2_memory_get_slow_dll_ctrl(); + fast_dll_ctrl = omap2_memory_get_fast_dll_ctrl(); + if (level == PRCM_HALF_SPEED) { local_irq_save(flags); PRCM_VOLTSETUP = 0xffff; omap2_sram_reprogram_sdrc(PRCM_HALF_SPEED, - mem_timings.slow_dll_ctrl, - mem_timings.m_type); + slow_dll_ctrl, m_type); curr_perf_level = PRCM_HALF_SPEED; local_irq_restore(flags); } @@ -461,8 +411,7 @@ static u32 omap2_reprogram_sdrc(u32 level, u32 force) local_irq_save(flags); PRCM_VOLTSETUP = 0xffff; omap2_sram_reprogram_sdrc(PRCM_FULL_SPEED, - mem_timings.fast_dll_ctrl, - mem_timings.m_type); + fast_dll_ctrl, m_type); curr_perf_level = PRCM_FULL_SPEED; local_irq_restore(flags); } @@ -650,7 +599,7 @@ static u32 omap2_get_clksel(u32 *div_sel, u32 *field_mask, case 13: /* dss2 */ mask = 0x1; break; case 25: /* usb */ - mask = 0xf; break; + mask = 0x7; break; } } diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h index 6cab20b1d3c1..6c78d471fab7 100644 --- a/arch/arm/mach-omap2/clock.h +++ b/arch/arm/mach-omap2/clock.h @@ -33,20 +33,6 @@ static u32 omap2_clksel_get_divisor(struct clk *clk); #define RATE_IN_242X (1 << 0) #define RATE_IN_243X (1 << 1) -/* Memory timings */ -#define M_DDR 1 -#define M_LOCK_CTRL (1 << 2) -#define M_UNLOCK 0 -#define M_LOCK 1 - -struct memory_timings { - u32 m_type; /* ddr = 1, sdr = 0 */ - u32 dll_mode; /* use lock mode = 1, unlock mode = 0 */ - u32 slow_dll_ctrl; /* unlock mode, dll value for slow speed */ - u32 fast_dll_ctrl; /* unlock mode, dll value for fast speed */ - u32 base_cs; /* base chip select to use for calculations */ -}; - /* Key dividers which make up a PRCM set. Ratio's for a PRCM are mandated. * xtal_speed, dpll_speed, mpu_speed, CM_CLKSEL_MPU,CM_CLKSEL_DSP * CM_CLKSEL_GFX, CM_CLKSEL1_CORE, CM_CLKSEL1_PLL CM_CLKSEL2_PLL, CM_CLKSEL_MDM @@ -731,6 +717,16 @@ static struct clk sys_clkout2 = { .recalc = &omap2_clksel_recalc, }; +static struct clk emul_ck = { + .name = "emul_ck", + .parent = &func_54m_ck, + .flags = CLOCK_IN_OMAP242X, + .enable_reg = (void __iomem *)&PRCM_CLKEMUL_CTRL, + .enable_bit = 0, + .recalc = &omap2_propagate_rate, + +}; + /* * MPU clock domain * Clocks: @@ -1702,7 +1698,8 @@ static struct clk hdq_fck = { }; static struct clk i2c2_ick = { - .name = "i2c2_ick", + .name = "i2c_ick", + .id = 2, .parent = &l4_ck, .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, .enable_reg = (void __iomem *)&CM_ICLKEN1_CORE, @@ -1711,7 +1708,8 @@ static struct clk i2c2_ick = { }; static struct clk i2c2_fck = { - .name = "i2c2_fck", + .name = "i2c_fck", + .id = 2, .parent = &func_12m_ck, .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, .enable_reg = (void __iomem *)&CM_FCLKEN1_CORE, @@ -1729,7 +1727,8 @@ static struct clk i2chs2_fck = { }; static struct clk i2c1_ick = { - .name = "i2c1_ick", + .name = "i2c_ick", + .id = 1, .parent = &l4_ck, .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, .enable_reg = (void __iomem *)&CM_ICLKEN1_CORE, @@ -1738,7 +1737,8 @@ static struct clk i2c1_ick = { }; static struct clk i2c1_fck = { - .name = "i2c1_fck", + .name = "i2c_fck", + .id = 1, .parent = &func_12m_ck, .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, .enable_reg = (void __iomem *)&CM_FCLKEN1_CORE, @@ -1971,6 +1971,7 @@ static struct clk *onchip_clks[] = { &wdt1_osc_ck, &sys_clkout, &sys_clkout2, + &emul_ck, /* mpu domain clocks */ &mpu_ck, /* dsp domain clocks */ diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c index 7181edb89352..fb7f91da1aad 100644 --- a/arch/arm/mach-omap2/devices.c +++ b/arch/arm/mach-omap2/devices.c @@ -25,10 +25,6 @@ #include #include -extern void omap_nop_release(struct device *dev); - -/*-------------------------------------------------------------------------*/ - #if defined(CONFIG_I2C_OMAP) || defined(CONFIG_I2C_OMAP_MODULE) #define OMAP2_I2C_BASE2 0x48072000 @@ -49,9 +45,6 @@ static struct resource i2c_resources2[] = { static struct platform_device omap_i2c_device2 = { .name = "i2c_omap", .id = 2, - .dev = { - .release = omap_nop_release, - }, .num_resources = ARRAY_SIZE(i2c_resources2), .resource = i2c_resources2, }; @@ -74,6 +67,44 @@ static void omap_init_i2c(void) {} #endif +#if defined(CONFIG_OMAP_STI) + +#define OMAP2_STI_BASE IO_ADDRESS(0x48068000) +#define OMAP2_STI_CHANNEL_BASE 0x54000000 +#define OMAP2_STI_IRQ 4 + +static struct resource sti_resources[] = { + { + .start = OMAP2_STI_BASE, + .end = OMAP2_STI_BASE + 0x7ff, + .flags = IORESOURCE_MEM, + }, + { + .start = OMAP2_STI_CHANNEL_BASE, + .end = OMAP2_STI_CHANNEL_BASE + SZ_64K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = OMAP2_STI_IRQ, + .flags = IORESOURCE_IRQ, + } +}; + +static struct platform_device sti_device = { + .name = "sti", + .id = -1, + .num_resources = ARRAY_SIZE(sti_resources), + .resource = sti_resources, +}; + +static inline void omap_init_sti(void) +{ + platform_device_register(&sti_device); +} +#else +static inline void omap_init_sti(void) {} +#endif + /*-------------------------------------------------------------------------*/ static int __init omap2_init_devices(void) @@ -82,6 +113,7 @@ static int __init omap2_init_devices(void) * in alphabetical order so they're easier to sort through. */ omap_init_i2c(); + omap_init_sti(); return 0; } diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c index 8ea67bf196a5..7d5711611f2f 100644 --- a/arch/arm/mach-omap2/io.c +++ b/arch/arm/mach-omap2/io.c @@ -16,9 +16,13 @@ #include #include -#include +#include #include + +#include + #include +#include extern void omap_sram_init(void); extern int omap2_clk_init(void); @@ -43,11 +47,24 @@ static struct map_desc omap2_io_desc[] __initdata = { } }; -void __init omap_map_common_io(void) +void __init omap2_map_common_io(void) { iotable_init(omap2_io_desc, ARRAY_SIZE(omap2_io_desc)); + + /* Normally devicemaps_init() would flush caches and tlb after + * mdesc->map_io(), but we must also do it here because of the CPU + * revision check below. + */ + local_flush_tlb_all(); + flush_cache_all(); + omap2_check_revision(); omap_sram_init(); + omapfb_reserve_mem(); +} + +void __init omap2_init_common_hw(void) +{ omap2_mux_init(); omap2_clk_init(); } diff --git a/arch/arm/mach-omap2/memory.c b/arch/arm/mach-omap2/memory.c new file mode 100644 index 000000000000..1d925d69fc35 --- /dev/null +++ b/arch/arm/mach-omap2/memory.c @@ -0,0 +1,102 @@ +/* + * linux/arch/arm/mach-omap2/memory.c + * + * Memory timing related functions for OMAP24XX + * + * Copyright (C) 2005 Texas Instruments Inc. + * Richard Woodruff + * + * Copyright (C) 2005 Nokia Corporation + * Tony Lindgren + * + * 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 +#include +#include +#include +#include + +#include + +#include +#include + +#include "prcm-regs.h" +#include "memory.h" + +static struct memory_timings mem_timings; + +u32 omap2_memory_get_slow_dll_ctrl(void) +{ + return mem_timings.slow_dll_ctrl; +} + +u32 omap2_memory_get_fast_dll_ctrl(void) +{ + return mem_timings.fast_dll_ctrl; +} + +u32 omap2_memory_get_type(void) +{ + return mem_timings.m_type; +} + +void omap2_init_memory_params(u32 force_lock_to_unlock_mode) +{ + unsigned long dll_cnt; + u32 fast_dll = 0; + + mem_timings.m_type = !((SDRC_MR_0 & 0x3) == 0x1); /* DDR = 1, SDR = 0 */ + + /* 2422 es2.05 and beyond has a single SIP DDR instead of 2 like others. + * In the case of 2422, its ok to use CS1 instead of CS0. + */ + if (cpu_is_omap2422()) + mem_timings.base_cs = 1; + else + mem_timings.base_cs = 0; + + if (mem_timings.m_type != M_DDR) + return; + + /* With DDR we need to determine the low frequency DLL value */ + if (((mem_timings.fast_dll_ctrl & (1 << 2)) == M_LOCK_CTRL)) + mem_timings.dll_mode = M_UNLOCK; + else + mem_timings.dll_mode = M_LOCK; + + if (mem_timings.base_cs == 0) { + fast_dll = SDRC_DLLA_CTRL; + dll_cnt = SDRC_DLLA_STATUS & 0xff00; + } else { + fast_dll = SDRC_DLLB_CTRL; + dll_cnt = SDRC_DLLB_STATUS & 0xff00; + } + if (force_lock_to_unlock_mode) { + fast_dll &= ~0xff00; + fast_dll |= dll_cnt; /* Current lock mode */ + } + /* set fast timings with DLL filter disabled */ + mem_timings.fast_dll_ctrl = (fast_dll | (3 << 8)); + + /* No disruptions, DDR will be offline & C-ABI not followed */ + omap2_sram_ddr_init(&mem_timings.slow_dll_ctrl, + mem_timings.fast_dll_ctrl, + mem_timings.base_cs, + force_lock_to_unlock_mode); + mem_timings.slow_dll_ctrl &= 0xff00; /* Keep lock value */ + + /* Turn status into unlock ctrl */ + mem_timings.slow_dll_ctrl |= + ((mem_timings.fast_dll_ctrl & 0xF) | (1 << 2)); + + /* 90 degree phase for anything below 133Mhz + disable DLL filter */ + mem_timings.slow_dll_ctrl |= ((1 << 1) | (3 << 8)); +} diff --git a/arch/arm/mach-omap2/memory.h b/arch/arm/mach-omap2/memory.h new file mode 100644 index 000000000000..d212eea83a05 --- /dev/null +++ b/arch/arm/mach-omap2/memory.h @@ -0,0 +1,34 @@ +/* + * linux/arch/arm/mach-omap2/memory.h + * + * Interface for memory timing related functions for OMAP24XX + * + * Copyright (C) 2005 Texas Instruments Inc. + * Richard Woodruff + * + * Copyright (C) 2005 Nokia Corporation + * Tony Lindgren + * + * 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. + */ + +/* Memory timings */ +#define M_DDR 1 +#define M_LOCK_CTRL (1 << 2) +#define M_UNLOCK 0 +#define M_LOCK 1 + +struct memory_timings { + u32 m_type; /* ddr = 1, sdr = 0 */ + u32 dll_mode; /* use lock mode = 1, unlock mode = 0 */ + u32 slow_dll_ctrl; /* unlock mode, dll value for slow speed */ + u32 fast_dll_ctrl; /* unlock mode, dll value for fast speed */ + u32 base_cs; /* base chip select to use for calculations */ +}; + +extern void omap2_init_memory_params(u32 force_lock_to_unlock_mode); +extern u32 omap2_memory_get_slow_dll_ctrl(void); +extern u32 omap2_memory_get_fast_dll_ctrl(void); +extern u32 omap2_memory_get_type(void); diff --git a/arch/arm/mach-omap2/mux.c b/arch/arm/mach-omap2/mux.c index ea4654815dd1..1197dc38c20a 100644 --- a/arch/arm/mach-omap2/mux.c +++ b/arch/arm/mach-omap2/mux.c @@ -50,9 +50,54 @@ MUX_CFG_24XX("H19_24XX_I2C2_SDA", 0x114, 0, 0, 0, 1) /* Menelaus interrupt */ MUX_CFG_24XX("W19_24XX_SYS_NIRQ", 0x12c, 0, 1, 1, 1) +/* 24xx clocks */ +MUX_CFG_24XX("W14_24XX_SYS_CLKOUT", 0x137, 0, 1, 1, 1) + +/* 24xx McBSP */ +MUX_CFG_24XX("Y15_24XX_MCBSP2_CLKX", 0x124, 1, 1, 0, 1) +MUX_CFG_24XX("R14_24XX_MCBSP2_FSX", 0x125, 1, 1, 0, 1) +MUX_CFG_24XX("W15_24XX_MCBSP2_DR", 0x126, 1, 1, 0, 1) +MUX_CFG_24XX("V15_24XX_MCBSP2_DX", 0x127, 1, 1, 0, 1) + /* 24xx GPIO */ +MUX_CFG_24XX("M21_242X_GPIO11", 0x0c9, 3, 1, 1, 1) +MUX_CFG_24XX("AA10_242X_GPIO13", 0x0e5, 3, 0, 0, 1) +MUX_CFG_24XX("AA6_242X_GPIO14", 0x0e6, 3, 0, 0, 1) +MUX_CFG_24XX("AA4_242X_GPIO15", 0x0e7, 3, 0, 0, 1) +MUX_CFG_24XX("Y11_242X_GPIO16", 0x0e8, 3, 0, 0, 1) +MUX_CFG_24XX("AA12_242X_GPIO17", 0x0e9, 3, 0, 0, 1) +MUX_CFG_24XX("AA8_242X_GPIO58", 0x0ea, 3, 0, 0, 1) MUX_CFG_24XX("Y20_24XX_GPIO60", 0x12c, 3, 0, 0, 1) +MUX_CFG_24XX("W4__24XX_GPIO74", 0x0f2, 3, 0, 0, 1) MUX_CFG_24XX("M15_24XX_GPIO92", 0x10a, 3, 0, 0, 1) +MUX_CFG_24XX("V14_24XX_GPIO117", 0x128, 3, 1, 0, 1) + +/* TSC IRQ */ +MUX_CFG_24XX("P20_24XX_TSC_IRQ", 0x108, 0, 0, 0, 1) + +/* UART3 */ +MUX_CFG_24XX("K15_24XX_UART3_TX", 0x118, 0, 0, 0, 1) +MUX_CFG_24XX("K14_24XX_UART3_RX", 0x119, 0, 0, 0, 1) + +/* Keypad GPIO*/ +MUX_CFG_24XX("T19_24XX_KBR0", 0x106, 3, 1, 1, 1) +MUX_CFG_24XX("R19_24XX_KBR1", 0x107, 3, 1, 1, 1) +MUX_CFG_24XX("V18_24XX_KBR2", 0x139, 3, 1, 1, 1) +MUX_CFG_24XX("M21_24XX_KBR3", 0xc9, 3, 1, 1, 1) +MUX_CFG_24XX("E5__24XX_KBR4", 0x138, 3, 1, 1, 1) +MUX_CFG_24XX("M18_24XX_KBR5", 0x10e, 3, 1, 1, 1) +MUX_CFG_24XX("R20_24XX_KBC0", 0x108, 3, 0, 0, 1) +MUX_CFG_24XX("M14_24XX_KBC1", 0x109, 3, 0, 0, 1) +MUX_CFG_24XX("H19_24XX_KBC2", 0x114, 3, 0, 0, 1) +MUX_CFG_24XX("V17_24XX_KBC3", 0x135, 3, 0, 0, 1) +MUX_CFG_24XX("P21_24XX_KBC4", 0xca, 3, 0, 0, 1) +MUX_CFG_24XX("L14_24XX_KBC5", 0x10f, 3, 0, 0, 1) +MUX_CFG_24XX("N19_24XX_KBC6", 0x110, 3, 0, 0, 1) + +/* 24xx Menelaus Keypad GPIO */ +MUX_CFG_24XX("B3__24XX_KBR5", 0x30, 3, 1, 1, 1) +MUX_CFG_24XX("AA4_24XX_KBC2", 0xe7, 3, 0, 0, 1) +MUX_CFG_24XX("B13_24XX_KBC6", 0x110, 3, 0, 0, 1) }; diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c new file mode 100644 index 000000000000..562168fa2b16 --- /dev/null +++ b/arch/arm/mach-omap2/pm.c @@ -0,0 +1,149 @@ +/* + * linux/arch/arm/mach-omap2/pm.c + * + * OMAP2 Power Management Routines + * + * Copyright (C) 2006 Nokia Corporation + * Tony Lindgren + * + * Copyright (C) 2005 Texas Instruments, Inc. + * Richard Woodruff + * + * Based on pm.c for omap1 + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static struct clk *vclk; +static void (*omap2_sram_idle)(void); +static void (*omap2_sram_suspend)(int dllctrl, int cpu_rev); +static void (*saved_idle)(void); + +void omap2_pm_idle(void) +{ + local_irq_disable(); + local_fiq_disable(); + if (need_resched()) { + local_fiq_enable(); + local_irq_enable(); + return; + } + + /* + * Since an interrupt may set up a timer, we don't want to + * reprogram the hardware timer with interrupts enabled. + * Re-enable interrupts only after returning from idle. + */ + timer_dyn_reprogram(); + + omap2_sram_idle(); + local_fiq_enable(); + local_irq_enable(); +} + +static int omap2_pm_prepare(suspend_state_t state) +{ + int error = 0; + + /* We cannot sleep in idle until we have resumed */ + saved_idle = pm_idle; + pm_idle = NULL; + + switch (state) + { + case PM_SUSPEND_STANDBY: + case PM_SUSPEND_MEM: + break; + + case PM_SUSPEND_DISK: + return -ENOTSUPP; + + default: + return -EINVAL; + } + + return error; +} + +static int omap2_pm_enter(suspend_state_t state) +{ + switch (state) + { + case PM_SUSPEND_STANDBY: + case PM_SUSPEND_MEM: + /* FIXME: Add suspend */ + break; + + case PM_SUSPEND_DISK: + return -ENOTSUPP; + + default: + return -EINVAL; + } + + return 0; +} + +static int omap2_pm_finish(suspend_state_t state) +{ + pm_idle = saved_idle; + return 0; +} + +static struct pm_ops omap_pm_ops = { + .pm_disk_mode = 0, + .prepare = omap2_pm_prepare, + .enter = omap2_pm_enter, + .finish = omap2_pm_finish, +}; + +int __init omap2_pm_init(void) +{ + printk("Power Management for TI OMAP.\n"); + + vclk = clk_get(NULL, "virt_prcm_set"); + if (IS_ERR(vclk)) { + printk(KERN_ERR "Could not get PM vclk\n"); + return -ENODEV; + } + + /* + * We copy the assembler sleep/wakeup routines to SRAM. + * These routines need to be in SRAM as that's the only + * memory the MPU can see when it wakes up. + */ + omap2_sram_idle = omap_sram_push(omap24xx_idle_loop_suspend, + omap24xx_idle_loop_suspend_sz); + + omap2_sram_suspend = omap_sram_push(omap24xx_cpu_suspend, + omap24xx_cpu_suspend_sz); + + pm_set_ops(&omap_pm_ops); + pm_idle = omap2_pm_idle; + + return 0; +} + +__initcall(omap2_pm_init); diff --git a/arch/arm/mach-omap2/prcm.h b/arch/arm/mach-omap2/prcm-regs.h similarity index 70% rename from arch/arm/mach-omap2/prcm.h rename to arch/arm/mach-omap2/prcm-regs.h index 2eb89b936c83..22ac7be4f782 100644 --- a/arch/arm/mach-omap2/prcm.h +++ b/arch/arm/mach-omap2/prcm-regs.h @@ -1,5 +1,7 @@ /* - * prcm.h - Access definations for use in OMAP24XX clock and power management + * linux/arch/arm/mach-omap2/prcm-reg.h + * + * OMAP24XX Power Reset and Clock Management (PRCM) registers * * Copyright (C) 2005 Texas Instruments, Inc. * @@ -18,8 +20,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef __ASM_ARM_ARCH_DPM_PRCM_H -#define __ASM_ARM_ARCH_DPM_PRCM_H +#ifndef __ARCH_ARM_MACH_OMAP2_PRCM_H +#define __ARCH_ARM_MACH_OMAP2_PRCM_H /* SET_PERFORMANCE_LEVEL PARAMETERS */ #define PRCM_HALF_SPEED 1 @@ -159,54 +161,63 @@ #define CM_FCLKEN_MDM PRCM_REG32(0xC00) #define CM_ICLKEN_MDM PRCM_REG32(0xC10) #define CM_IDLEST_MDM PRCM_REG32(0xC20) +#define CM_AUTOIDLE_MDM PRCM_REG32(0xC30) #define CM_CLKSEL_MDM PRCM_REG32(0xC40) +#define CM_CLKSTCTRL_MDM PRCM_REG32(0xC48) +#define RM_RSTCTRL_MDM PRCM_REG32(0xC50) +#define RM_RSTST_MDM PRCM_REG32(0xC58) +#define PM_WKEN_MDM PRCM_REG32(0xCA0) +#define PM_WKST_MDM PRCM_REG32(0xCB0) +#define PM_WKDEP_MDM PRCM_REG32(0xCC8) +#define PM_PWSTCTRL_MDM PRCM_REG32(0xCE0) +#define PM_PWSTST_MDM PRCM_REG32(0xCE4) -/* FIXME: Move to header for 2430 */ -#define DISP_BASE (OMAP24XX_L4_IO_BASE+0x50000) +#define OMAP24XX_L4_IO_BASE 0x48000000 + +#define DISP_BASE (OMAP24XX_L4_IO_BASE + 0x50000) #define DISP_REG32(offset) __REG32(DISP_BASE + (offset)) -#define GPMC_BASE (OMAP24XX_GPMC_BASE) -#define GPMC_REG32(offset) __REG32(GPMC_BASE + (offset)) +#define OMAP24XX_GPMC_BASE (L3_24XX_BASE + 0xa000) +#define GPMC_REG32(offset) __REG32(OMAP24XX_GPMC_BASE + (offset)) -#define GPT1_BASE (OMAP24XX_GPT1) +/* FIXME: Move these to timer code */ +#define GPT1_BASE (0x48028000) #define GPT1_REG32(offset) __REG32(GPT1_BASE + (offset)) /* Misc sysconfig */ #define DISPC_SYSCONFIG DISP_REG32(0x410) -#define SPI_BASE (OMAP24XX_L4_IO_BASE+0x98000) +#define SPI_BASE (OMAP24XX_L4_IO_BASE + 0x98000) #define MCSPI1_SYSCONFIG __REG32(SPI_BASE + 0x10) -#define MCSPI2_SYSCONFIG __REG32(SPI_BASE+0x2000 + 0x10) +#define MCSPI2_SYSCONFIG __REG32(SPI_BASE + 0x2000 + 0x10) +#define MCSPI3_SYSCONFIG __REG32(OMAP24XX_L4_IO_BASE + 0xb8010) -//#define DSP_MMU_SYSCONFIG 0x5A000010 -#define CAMERA_MMU_SYSCONFIG __REG32(DISP_BASE+0x2C10) -//#define IVA_MMU_SYSCONFIG 0x5D000010 -//#define DSP_DMA_SYSCONFIG 0x00FCC02C -#define CAMERA_DMA_SYSCONFIG __REG32(DISP_BASE+0x282C) -#define SYSTEM_DMA_SYSCONFIG __REG32(DISP_BASE+0x602C) +#define CAMERA_MMU_SYSCONFIG __REG32(DISP_BASE + 0x2C10) +#define CAMERA_DMA_SYSCONFIG __REG32(DISP_BASE + 0x282C) +#define SYSTEM_DMA_SYSCONFIG __REG32(DISP_BASE + 0x602C) #define GPMC_SYSCONFIG GPMC_REG32(0x010) -#define MAILBOXES_SYSCONFIG __REG32(OMAP24XX_L4_IO_BASE+0x94010) -#define UART1_SYSCONFIG __REG32(OMAP24XX_L4_IO_BASE+0x6A054) -#define UART2_SYSCONFIG __REG32(OMAP24XX_L4_IO_BASE+0x6C054) -#define UART3_SYSCONFIG __REG32(OMAP24XX_L4_IO_BASE+0x6E054) -//#define IVA_SYSCONFIG 0x5C060010 -#define SDRC_SYSCONFIG __REG32(OMAP24XX_SDRC_BASE+0x10) -#define SMS_SYSCONFIG __REG32(OMAP24XX_SMS_BASE+0x10) -#define SSI_SYSCONFIG __REG32(DISP_BASE+0x8010) -//#define VLYNQ_SYSCONFIG 0x67FFFE10 +#define MAILBOXES_SYSCONFIG __REG32(OMAP24XX_L4_IO_BASE + 0x94010) +#define UART1_SYSCONFIG __REG32(OMAP24XX_L4_IO_BASE + 0x6A054) +#define UART2_SYSCONFIG __REG32(OMAP24XX_L4_IO_BASE + 0x6C054) +#define UART3_SYSCONFIG __REG32(OMAP24XX_L4_IO_BASE + 0x6E054) +#define SDRC_SYSCONFIG __REG32(OMAP24XX_SDRC_BASE + 0x10) +#define OMAP24XX_SMS_BASE (L3_24XX_BASE + 0x8000) +#define SMS_SYSCONFIG __REG32(OMAP24XX_SMS_BASE + 0x10) +#define SSI_SYSCONFIG __REG32(DISP_BASE + 0x8010) /* rkw - good cannidates for PM_ to start what nm was trying */ -#define OMAP24XX_GPT2 (OMAP24XX_L4_IO_BASE+0x2A000) -#define OMAP24XX_GPT3 (OMAP24XX_L4_IO_BASE+0x78000) -#define OMAP24XX_GPT4 (OMAP24XX_L4_IO_BASE+0x7A000) -#define OMAP24XX_GPT5 (OMAP24XX_L4_IO_BASE+0x7C000) -#define OMAP24XX_GPT6 (OMAP24XX_L4_IO_BASE+0x7E000) -#define OMAP24XX_GPT7 (OMAP24XX_L4_IO_BASE+0x80000) -#define OMAP24XX_GPT8 (OMAP24XX_L4_IO_BASE+0x82000) -#define OMAP24XX_GPT9 (OMAP24XX_L4_IO_BASE+0x84000) -#define OMAP24XX_GPT10 (OMAP24XX_L4_IO_BASE+0x86000) -#define OMAP24XX_GPT11 (OMAP24XX_L4_IO_BASE+0x88000) -#define OMAP24XX_GPT12 (OMAP24XX_L4_IO_BASE+0x8A000) +#define OMAP24XX_GPT2 (OMAP24XX_L4_IO_BASE + 0x2A000) +#define OMAP24XX_GPT3 (OMAP24XX_L4_IO_BASE + 0x78000) +#define OMAP24XX_GPT4 (OMAP24XX_L4_IO_BASE + 0x7A000) +#define OMAP24XX_GPT5 (OMAP24XX_L4_IO_BASE + 0x7C000) +#define OMAP24XX_GPT6 (OMAP24XX_L4_IO_BASE + 0x7E000) +#define OMAP24XX_GPT7 (OMAP24XX_L4_IO_BASE + 0x80000) +#define OMAP24XX_GPT8 (OMAP24XX_L4_IO_BASE + 0x82000) +#define OMAP24XX_GPT9 (OMAP24XX_L4_IO_BASE + 0x84000) +#define OMAP24XX_GPT10 (OMAP24XX_L4_IO_BASE + 0x86000) +#define OMAP24XX_GPT11 (OMAP24XX_L4_IO_BASE + 0x88000) +#define OMAP24XX_GPT12 (OMAP24XX_L4_IO_BASE + 0x8A000) +/* FIXME: Move these to timer code */ #define GPTIMER1_SYSCONFIG GPT1_REG32(0x010) #define GPTIMER2_SYSCONFIG __REG32(OMAP24XX_GPT2 + 0x10) #define GPTIMER3_SYSCONFIG __REG32(OMAP24XX_GPT3 + 0x10) @@ -220,12 +231,18 @@ #define GPTIMER11_SYSCONFIG __REG32(OMAP24XX_GPT11 + 0x10) #define GPTIMER12_SYSCONFIG __REG32(OMAP24XX_GPT12 + 0x10) -#define GPIOX_BASE(X) (OMAP24XX_GPIO_BASE+(0x2000*((X)-1))) +/* FIXME: Move these to gpio code */ +#define OMAP24XX_GPIO_BASE 0x48018000 +#define GPIOX_BASE(X) (OMAP24XX_GPIO_BASE + (0x2000 * ((X) - 1))) -#define GPIO1_SYSCONFIG __REG32((GPIOX_BASE(1)+0x10)) -#define GPIO2_SYSCONFIG __REG32((GPIOX_BASE(2)+0x10)) -#define GPIO3_SYSCONFIG __REG32((GPIOX_BASE(3)+0x10)) -#define GPIO4_SYSCONFIG __REG32((GPIOX_BASE(4)+0x10)) +#define GPIO1_SYSCONFIG __REG32((GPIOX_BASE(1) + 0x10)) +#define GPIO2_SYSCONFIG __REG32((GPIOX_BASE(2) + 0x10)) +#define GPIO3_SYSCONFIG __REG32((GPIOX_BASE(3) + 0x10)) +#define GPIO4_SYSCONFIG __REG32((GPIOX_BASE(4) + 0x10)) + +#if defined(CONFIG_ARCH_OMAP243X) +#define GPIO5_SYSCONFIG __REG32((OMAP24XX_GPIO5_BASE + 0x10)) +#endif /* GP TIMER 1 */ #define GPTIMER1_TISTAT GPT1_REG32(0x014) @@ -243,15 +260,15 @@ #define GPTIMER1_TCAR2 GPT1_REG32(0x044) /* rkw -- base fix up please... */ -#define GPTIMER3_TISR __REG32(OMAP24XX_L4_IO_BASE+0x78018) +#define GPTIMER3_TISR __REG32(OMAP24XX_L4_IO_BASE + 0x78018) /* SDRC */ -#define SDRC_DLLA_CTRL __REG32(OMAP24XX_SDRC_BASE+0x060) -#define SDRC_DLLA_STATUS __REG32(OMAP24XX_SDRC_BASE+0x064) -#define SDRC_DLLB_CTRL __REG32(OMAP24XX_SDRC_BASE+0x068) -#define SDRC_DLLB_STATUS __REG32(OMAP24XX_SDRC_BASE+0x06C) -#define SDRC_POWER __REG32(OMAP24XX_SDRC_BASE+0x070) -#define SDRC_MR_0 __REG32(OMAP24XX_SDRC_BASE+0x084) +#define SDRC_DLLA_CTRL __REG32(OMAP24XX_SDRC_BASE + 0x060) +#define SDRC_DLLA_STATUS __REG32(OMAP24XX_SDRC_BASE + 0x064) +#define SDRC_DLLB_CTRL __REG32(OMAP24XX_SDRC_BASE + 0x068) +#define SDRC_DLLB_STATUS __REG32(OMAP24XX_SDRC_BASE + 0x06C) +#define SDRC_POWER __REG32(OMAP24XX_SDRC_BASE + 0x070) +#define SDRC_MR_0 __REG32(OMAP24XX_SDRC_BASE + 0x084) /* GPIO 1 */ #define GPIO1_BASE GPIOX_BASE(1) @@ -278,6 +295,8 @@ #define GPIO2_DATAIN GPIO2_REG32(0x038) #define GPIO2_OE GPIO2_REG32(0x034) #define GPIO2_DATAOUT GPIO2_REG32(0x03C) +#define GPIO2_DEBOUNCENABLE GPIO2_REG32(0x050) +#define GPIO2_DEBOUNCINGTIME GPIO2_REG32(0x054) /* GPIO 3 */ #define GPIO3_BASE GPIOX_BASE(3) @@ -294,6 +313,8 @@ #define GPIO3_DATAOUT GPIO3_REG32(0x03C) #define GPIO3_DEBOUNCENABLE GPIO3_REG32(0x050) #define GPIO3_DEBOUNCINGTIME GPIO3_REG32(0x054) +#define GPIO3_DEBOUNCENABLE GPIO3_REG32(0x050) +#define GPIO3_DEBOUNCINGTIME GPIO3_REG32(0x054) /* GPIO 4 */ #define GPIO4_BASE GPIOX_BASE(4) @@ -311,10 +332,26 @@ #define GPIO4_DEBOUNCENABLE GPIO4_REG32(0x050) #define GPIO4_DEBOUNCINGTIME GPIO4_REG32(0x054) +#if defined(CONFIG_ARCH_OMAP243X) +/* GPIO 5 */ +#define GPIO5_REG32(offset) __REG32((OMAP24XX_GPIO5_BASE + (offset))) +#define GPIO5_IRQENABLE1 GPIO5_REG32(0x01C) +#define GPIO5_IRQSTATUS1 GPIO5_REG32(0x018) +#define GPIO5_IRQENABLE2 GPIO5_REG32(0x02C) +#define GPIO5_IRQSTATUS2 GPIO5_REG32(0x028) +#define GPIO5_WAKEUPENABLE GPIO5_REG32(0x020) +#define GPIO5_RISINGDETECT GPIO5_REG32(0x048) +#define GPIO5_FALLINGDETECT GPIO5_REG32(0x04C) +#define GPIO5_DATAIN GPIO5_REG32(0x038) +#define GPIO5_OE GPIO5_REG32(0x034) +#define GPIO5_DATAOUT GPIO5_REG32(0x03C) +#define GPIO5_DEBOUNCENABLE GPIO5_REG32(0x050) +#define GPIO5_DEBOUNCINGTIME GPIO5_REG32(0x054) +#endif /* IO CONFIG */ -#define CONTROL_BASE (OMAP24XX_CTRL_BASE) -#define CONTROL_REG32(offset) __REG32(CONTROL_BASE + (offset)) +#define OMAP24XX_CTRL_BASE (L4_24XX_BASE) +#define CONTROL_REG32(offset) __REG32(OMAP24XX_CTRL_BASE + (offset)) #define CONTROL_PADCONF_SPI1_NCS2 CONTROL_REG32(0x104) #define CONTROL_PADCONF_SYS_XTALOUT CONTROL_REG32(0x134) @@ -322,15 +359,18 @@ #define CONTROL_PADCONF_MCBSP1_DX CONTROL_REG32(0x10C) #define CONTROL_PADCONF_GPMC_NCS4 CONTROL_REG32(0x090) #define CONTROL_PADCONF_DSS_D5 CONTROL_REG32(0x0B8) -#define CONTROL_PADCONF_DSS_D9 CONTROL_REG32(0x0BC) +#define CONTROL_PADCONF_DSS_D9 CONTROL_REG32(0x0BC) /* 2420 */ #define CONTROL_PADCONF_DSS_D13 CONTROL_REG32(0x0C0) #define CONTROL_PADCONF_DSS_VSYNC CONTROL_REG32(0x0CC) +#define CONTROL_PADCONF_SYS_NIRQW0 CONTROL_REG32(0x0BC) /* 2430 */ +#define CONTROL_PADCONF_SSI1_FLAG_TX CONTROL_REG32(0x108) /* 2430 */ /* CONTROL */ #define CONTROL_DEVCONF CONTROL_REG32(0x274) +#define CONTROL_DEVCONF1 CONTROL_REG32(0x2E8) /* INTERRUPT CONTROLLER */ -#define INTC_BASE (OMAP24XX_L4_IO_BASE+0xfe000) +#define INTC_BASE ((L4_24XX_BASE) + 0xfe000) #define INTC_REG32(offset) __REG32(INTC_BASE + (offset)) #define INTC1_U_BASE INTC_REG32(0x000) @@ -348,10 +388,12 @@ #define INTC_ISR_CLEAR2 INTC_REG32(0x0D4) #define INTC_SIR_IRQ INTC_REG32(0x040) #define INTC_CONTROL INTC_REG32(0x048) -#define INTC_ILR11 INTC_REG32(0x12C) +#define INTC_ILR11 INTC_REG32(0x12C) /* PRCM on MPU PIC */ +#define INTC_ILR30 INTC_REG32(0x178) +#define INTC_ILR31 INTC_REG32(0x17C) #define INTC_ILR32 INTC_REG32(0x180) -#define INTC_ILR37 INTC_REG32(0x194) -#define INTC_SYSCONFIG INTC_REG32(0x010) +#define INTC_ILR37 INTC_REG32(0x194) /* GPIO4 on MPU PIC */ +#define INTC_SYSCONFIG INTC_REG32(0x010) /* GPT1 on MPU PIC */ /* RAM FIREWALL */ #define RAMFW_BASE (0x68005000) @@ -373,6 +415,24 @@ #define GPMC_CONFIG6_0 GPMC_REG32(0x074) #define GPMC_CONFIG7_0 GPMC_REG32(0x078) +/* GPMC CS1 */ +#define GPMC_CONFIG1_1 GPMC_REG32(0x090) +#define GPMC_CONFIG2_1 GPMC_REG32(0x094) +#define GPMC_CONFIG3_1 GPMC_REG32(0x098) +#define GPMC_CONFIG4_1 GPMC_REG32(0x09C) +#define GPMC_CONFIG5_1 GPMC_REG32(0x0a0) +#define GPMC_CONFIG6_1 GPMC_REG32(0x0a4) +#define GPMC_CONFIG7_1 GPMC_REG32(0x0a8) + +/* GPMC CS3 */ +#define GPMC_CONFIG1_3 GPMC_REG32(0x0F0) +#define GPMC_CONFIG2_3 GPMC_REG32(0x0F4) +#define GPMC_CONFIG3_3 GPMC_REG32(0x0F8) +#define GPMC_CONFIG4_3 GPMC_REG32(0x0FC) +#define GPMC_CONFIG5_3 GPMC_REG32(0x100) +#define GPMC_CONFIG6_3 GPMC_REG32(0x104) +#define GPMC_CONFIG7_3 GPMC_REG32(0x108) + /* DSS */ #define DSS_CONTROL DISP_REG32(0x040) #define DISPC_CONTROL DISP_REG32(0x440) @@ -405,11 +465,15 @@ #define DISPC_DATA_CYCLE2 DISP_REG32(0x5D8) #define DISPC_DATA_CYCLE3 DISP_REG32(0x5DC) -/* Wake up define for board */ -#define GPIO97 (1 << 1) -#define GPIO88 (1 << 24) +/* HSUSB Suspend */ +#define HSUSB_CTRL __REG8(0x480AC001) +#define USBOTG_POWER __REG32(0x480AC000) -#endif /* __ASSEMBLER__ */ +/* HS MMC */ +#define MMCHS1_SYSCONFIG __REG32(0x4809C010) +#define MMCHS2_SYSCONFIG __REG32(0x480b4010) + +#endif /* __ASSEMBLER__ */ #endif diff --git a/arch/arm/mach-omap2/prcm.c b/arch/arm/mach-omap2/prcm.c new file mode 100644 index 000000000000..8893479dc7e0 --- /dev/null +++ b/arch/arm/mach-omap2/prcm.c @@ -0,0 +1,40 @@ +/* + * linux/arch/arm/mach-omap2/prcm.c + * + * OMAP 24xx Power Reset and Clock Management (PRCM) functions + * + * Copyright (C) 2005 Nokia Corporation + * + * Written by Tony Lindgren + * + * Some pieces of code Copyright (C) 2005 Texas Instruments, 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. + */ +#include +#include +#include +#include + +#include "prcm-regs.h" + +u32 omap_prcm_get_reset_sources(void) +{ + return RM_RSTST_WKUP & 0x7f; +} +EXPORT_SYMBOL(omap_prcm_get_reset_sources); + +/* Resets clock rates and reboots the system. Only called from system.h */ +void omap_prcm_arch_reset(char mode) +{ + u32 rate; + struct clk *vclk, *sclk; + + vclk = clk_get(NULL, "virt_prcm_set"); + sclk = clk_get(NULL, "sys_ck"); + rate = clk_get_rate(sclk); + clk_set_rate(vclk, rate); /* go to bypass for OMAP limitation */ + RM_RSTCTRL_WKUP |= 2; +} diff --git a/arch/arm/mach-omap2/sleep.S b/arch/arm/mach-omap2/sleep.S new file mode 100644 index 000000000000..00299cbeb911 --- /dev/null +++ b/arch/arm/mach-omap2/sleep.S @@ -0,0 +1,144 @@ +/* + * linux/arch/arm/mach-omap2/sleep.S + * + * (C) Copyright 2004 + * Texas Instruments, + * Richard Woodruff + * + * 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 + +#define A_32KSYNC_CR_V IO_ADDRESS(OMAP_TIMER32K_BASE+0x10) +#define A_PRCM_VOLTCTRL_V IO_ADDRESS(OMAP24XX_PRCM_BASE+0x50) +#define A_PRCM_CLKCFG_CTRL_V IO_ADDRESS(OMAP24XX_PRCM_BASE+0x80) +#define A_CM_CLKEN_PLL_V IO_ADDRESS(OMAP24XX_PRCM_BASE+0x500) +#define A_CM_IDLEST_CKGEN_V IO_ADDRESS(OMAP24XX_PRCM_BASE+0x520) +#define A_CM_CLKSEL1_PLL_V IO_ADDRESS(OMAP24XX_PRCM_BASE+0x540) +#define A_CM_CLKSEL2_PLL_V IO_ADDRESS(OMAP24XX_PRCM_BASE+0x544) + +#define A_SDRC_DLLA_CTRL_V IO_ADDRESS(OMAP24XX_SDRC_BASE+0x60) +#define A_SDRC_POWER_V IO_ADDRESS(OMAP24XX_SDRC_BASE+0x70) +#define A_SDRC_RFR_CTRL_V IO_ADDRESS(OMAP24XX_SDRC_BASE+0xA4) +#define A_SDRC0_V (0xC0000000) +#define A_SDRC_MANUAL_V IO_ADDRESS(OMAP24XX_SDRC_BASE+0xA8) + + .text + +/* + * Forces OMAP into idle state + * + * omap24xx_idle_loop_suspend() - This bit of code just executes the WFI + * for normal idles. + * + * Note: This code get's copied to internal SRAM at boot. When the OMAP + * wakes up it continues execution at the point it went to sleep. + */ +ENTRY(omap24xx_idle_loop_suspend) + stmfd sp!, {r0, lr} @ save registers on stack + mov r0, #0 @ clear for mcr setup + mcr p15, 0, r0, c7, c0, 4 @ wait for interrupt + ldmfd sp!, {r0, pc} @ restore regs and return + +ENTRY(omap24xx_idle_loop_suspend_sz) + .word . - omap24xx_idle_loop_suspend + +/* + * omap242x_cpu_suspend() - Forces OMAP into deep sleep state by completing + * SDRC shutdown then ARM shutdown. Upon wake MPU is back on so just restore + * SDRC. + * + * Input: + * R0 : DLL ctrl value pre-Sleep + * R1 : Processor+Revision + * 2420: 0x21 = 242xES1, 0x26 = 242xES2.2 + * 2430: 0x31 = 2430ES1, 0x32 = 2430ES2 + * + * The if the DPLL is going to AutoIdle. It seems like the DPLL may be back on + * when we get called, but the DLL probably isn't. We will wait a bit more in + * case the DPLL isn't quite there yet. The code will wait on DLL for DDR even + * if in unlocked mode. + * + * For less than 242x-ES2.2 upon wake from a sleep mode where the external + * oscillator was stopped, a timing bug exists where a non-stabilized 12MHz + * clock can pass into the PRCM can cause problems at DSP and IVA. + * To work around this the code will switch to the 32kHz source prior to sleep. + * Post sleep we will shift back to using the DPLL. Apparently, + * CM_IDLEST_CLKGEN does not reflect the full clock change so you need to wait + * 3x12MHz + 3x32kHz clocks for a full switch. + * + * The DLL load value is not kept in RETENTION or OFF. It needs to be restored + * at wake + */ +ENTRY(omap24xx_cpu_suspend) + stmfd sp!, {r0 - r12, lr} @ save registers on stack + mov r3, #0x0 @ clear for mrc call + mcr p15, 0, r3, c7, c10, 4 @ memory barrier, hope SDR/DDR finished + nop + nop + ldr r3, A_SDRC_POWER @ addr of sdrc power + ldr r4, [r3] @ value of sdrc power + orr r4, r4, #0x40 @ enable self refresh on idle req + mov r5, #0x2000 @ set delay (DPLL relock + DLL relock) + str r4, [r3] @ make it so + mov r2, #0 + nop + mcr p15, 0, r2, c7, c0, 4 @ wait for interrupt + nop +loop: + subs r5, r5, #0x1 @ awake, wait just a bit + bne loop + + /* The DPLL has on before we take the DDR out of self refresh */ + bic r4, r4, #0x40 @ now clear self refresh bit. + str r4, [r3] @ put vlaue back. + ldr r4, A_SDRC0 @ make a clock happen + ldr r4, [r4] + nop @ start auto refresh only after clk ok + movs r0, r0 @ see if DDR or SDR + ldrne r1, A_SDRC_DLLA_CTRL_S @ get addr of DLL ctrl + strne r0, [r1] @ rewrite DLLA to force DLL reload + addne r1, r1, #0x8 @ move to DLLB + strne r0, [r1] @ rewrite DLLB to force DLL reload + + mov r5, #0x1000 +loop2: + subs r5, r5, #0x1 + bne loop2 + /* resume*/ + ldmfd sp!, {r0 - r12, pc} @ restore regs and return + +A_SDRC_POWER: + .word A_SDRC_POWER_V +A_SDRC0: + .word A_SDRC0_V +A_CM_CLKSEL2_PLL_S: + .word A_CM_CLKSEL2_PLL_V +A_CM_CLKEN_PLL: + .word A_CM_CLKEN_PLL_V +A_SDRC_DLLA_CTRL_S: + .word A_SDRC_DLLA_CTRL_V +A_SDRC_MANUAL_S: + .word A_SDRC_MANUAL_V + +ENTRY(omap24xx_cpu_suspend_sz) + .word . - omap24xx_cpu_suspend + diff --git a/arch/arm/mach-omap2/sram-fn.S b/arch/arm/mach-omap2/sram-fn.S index 2a869e203342..d261e4ff4d9b 100644 --- a/arch/arm/mach-omap2/sram-fn.S +++ b/arch/arm/mach-omap2/sram-fn.S @@ -1,5 +1,5 @@ /* - * linux/arch/arm/mach-omap1/sram.S + * linux/arch/arm/mach-omap2/sram.S * * Omap2 specific functions that need to be run in internal SRAM * @@ -28,7 +28,7 @@ #include #include -#include +#include "prcm-regs.h" #define TIMER_32KSYNCT_CR_V IO_ADDRESS(OMAP24XX_32KSYNCT_BASE + 0x010) diff --git a/arch/arm/mach-pxa/corgi.c b/arch/arm/mach-pxa/corgi.c index 68923b1d2b62..d6d726036361 100644 --- a/arch/arm/mach-pxa/corgi.c +++ b/arch/arm/mach-pxa/corgi.c @@ -141,6 +141,8 @@ struct corgissp_machinfo corgi_ssp_machinfo = { */ static struct corgibl_machinfo corgi_bl_machinfo = { .max_intensity = 0x2f, + .default_intensity = 0x1f, + .limit_mask = 0x0b, .set_bl_intensity = corgi_bl_set_intensity, }; @@ -163,6 +165,14 @@ static struct platform_device corgikbd_device = { }; +/* + * Corgi LEDs + */ +static struct platform_device corgiled_device = { + .name = "corgi-led", + .id = -1, +}; + /* * Corgi Touch Screen Device */ @@ -297,6 +307,7 @@ static struct platform_device *devices[] __initdata = { &corgikbd_device, &corgibl_device, &corgits_device, + &corgiled_device, }; static void __init corgi_init(void) diff --git a/arch/arm/mach-pxa/poodle.c b/arch/arm/mach-pxa/poodle.c index b45560a8f6c4..a042473deedd 100644 --- a/arch/arm/mach-pxa/poodle.c +++ b/arch/arm/mach-pxa/poodle.c @@ -307,6 +307,10 @@ static void __init fixup_poodle(struct machine_desc *desc, struct tag *tags, char **cmdline, struct meminfo *mi) { sharpsl_save_param(); + mi->nr_banks=1; + mi->bank[0].start = 0xa0000000; + mi->bank[0].node = 0; + mi->bank[0].size = (32*1024*1024); } MACHINE_START(POODLE, "SHARP Poodle") diff --git a/arch/arm/mach-pxa/spitz.c b/arch/arm/mach-pxa/spitz.c index 0dbb079ecd25..19b372df544a 100644 --- a/arch/arm/mach-pxa/spitz.c +++ b/arch/arm/mach-pxa/spitz.c @@ -220,6 +220,8 @@ struct corgissp_machinfo spitz_ssp_machinfo = { * Spitz Backlight Device */ static struct corgibl_machinfo spitz_bl_machinfo = { + .default_intensity = 0x1f, + .limit_mask = 0x0b, .max_intensity = 0x2f, }; @@ -241,6 +243,14 @@ static struct platform_device spitzkbd_device = { }; +/* + * Spitz LEDs + */ +static struct platform_device spitzled_device = { + .name = "spitz-led", + .id = -1, +}; + /* * Spitz Touch Screen Device */ @@ -418,6 +428,7 @@ static struct platform_device *devices[] __initdata = { &spitzkbd_device, &spitzts_device, &spitzbl_device, + &spitzled_device, }; static void __init common_init(void) diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c index 66ec71756d0f..76c0e7f0a219 100644 --- a/arch/arm/mach-pxa/tosa.c +++ b/arch/arm/mach-pxa/tosa.c @@ -251,10 +251,19 @@ static struct platform_device tosakbd_device = { .id = -1, }; +/* + * Tosa LEDs + */ +static struct platform_device tosaled_device = { + .name = "tosa-led", + .id = -1, +}; + static struct platform_device *devices[] __initdata = { &tosascoop_device, &tosascoop_jc_device, &tosakbd_device, + &tosaled_device, }; static void __init tosa_init(void) diff --git a/arch/arm/mach-s3c2410/Kconfig b/arch/arm/mach-s3c2410/Kconfig index ed07c4149d82..ce7d81000695 100644 --- a/arch/arm/mach-s3c2410/Kconfig +++ b/arch/arm/mach-s3c2410/Kconfig @@ -50,9 +50,15 @@ config MACH_N30 . +config MACH_SMDK + bool + help + Common machine code for SMDK2410 and SMDK2440 + config ARCH_SMDK2410 bool "SMDK2410/A9M2410" select CPU_S3C2410 + select MACH_SMDK help Say Y here if you are using the SMDK2410 or the derived module A9M2410 @@ -60,6 +66,7 @@ config ARCH_SMDK2410 config ARCH_S3C2440 bool "SMDK2440" select CPU_S3C2440 + select MACH_SMDK help Say Y here if you are using the SMDK2440. diff --git a/arch/arm/mach-s3c2410/Makefile b/arch/arm/mach-s3c2410/Makefile index 1b3b476e5637..3e5712db6b52 100644 --- a/arch/arm/mach-s3c2410/Makefile +++ b/arch/arm/mach-s3c2410/Makefile @@ -48,3 +48,5 @@ obj-$(CONFIG_MACH_VR1000) += mach-vr1000.o usb-simtec.o obj-$(CONFIG_MACH_RX3715) += mach-rx3715.o obj-$(CONFIG_MACH_OTOM) += mach-otom.o obj-$(CONFIG_MACH_NEXCODER_2440) += mach-nexcoder.o + +obj-$(CONFIG_MACH_SMDK) += common-smdk.o \ No newline at end of file diff --git a/arch/arm/mach-s3c2410/clock.c b/arch/arm/mach-s3c2410/clock.c index fec02c92f95f..6de713ad319a 100644 --- a/arch/arm/mach-s3c2410/clock.c +++ b/arch/arm/mach-s3c2410/clock.c @@ -249,7 +249,7 @@ static int s3c24xx_upll_enable(struct clk *clk, int enable) /* if we started the UPLL, then allow to settle */ - if (enable && !(orig & S3C2410_CLKSLOW_UCLK_OFF)) + if (enable && (orig & S3C2410_CLKSLOW_UCLK_OFF)) udelay(200); return 0; @@ -367,6 +367,8 @@ static int s3c24xx_clkout_setparent(struct clk *clk, struct clk *parent) source = S3C2410_MISCCR_CLK0_UPLL; else if (parent == &clk_f) source = S3C2410_MISCCR_CLK0_FCLK; + else if (parent == &clk_h) + source = S3C2410_MISCCR_CLK0_HCLK; else if (parent == &clk_p) source = S3C2410_MISCCR_CLK0_PCLK; else if (clk == &s3c24xx_clkout0 && parent == &s3c24xx_dclk0) @@ -376,6 +378,8 @@ static int s3c24xx_clkout_setparent(struct clk *clk, struct clk *parent) else return -EINVAL; + clk->parent = parent; + if (clk == &s3c24xx_dclk0) mask = S3C2410_MISCCR_CLK0_MASK; else { diff --git a/arch/arm/mach-s3c2410/common-smdk.c b/arch/arm/mach-s3c2410/common-smdk.c new file mode 100644 index 000000000000..f372fbda124e --- /dev/null +++ b/arch/arm/mach-s3c2410/common-smdk.c @@ -0,0 +1,135 @@ +/* linux/arch/arm/mach-s3c2410/common-smdk.c + * + * Copyright (c) 2006 Simtec Electronics + * Ben Dooks + * + * Common code for SMDK2410 and SMDK2440 boards + * + * http://www.fluff.org/ben/smdk2440/ + * + * 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 +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include "common-smdk.h" +#include "devs.h" +#include "pm.h" + +/* NAND parititon from 2.4.18-swl5 */ + +static struct mtd_partition smdk_default_nand_part[] = { + [0] = { + .name = "Boot Agent", + .size = SZ_16K, + .offset = 0, + }, + [1] = { + .name = "S3C2410 flash parition 1", + .offset = 0, + .size = SZ_2M, + }, + [2] = { + .name = "S3C2410 flash partition 2", + .offset = SZ_4M, + .size = SZ_4M, + }, + [3] = { + .name = "S3C2410 flash partition 3", + .offset = SZ_8M, + .size = SZ_2M, + }, + [4] = { + .name = "S3C2410 flash partition 4", + .offset = SZ_1M * 10, + .size = SZ_4M, + }, + [5] = { + .name = "S3C2410 flash partition 5", + .offset = SZ_1M * 14, + .size = SZ_1M * 10, + }, + [6] = { + .name = "S3C2410 flash partition 6", + .offset = SZ_1M * 24, + .size = SZ_1M * 24, + }, + [7] = { + .name = "S3C2410 flash partition 7", + .offset = SZ_1M * 48, + .size = SZ_16M, + } +}; + +static struct s3c2410_nand_set smdk_nand_sets[] = { + [0] = { + .name = "NAND", + .nr_chips = 1, + .nr_partitions = ARRAY_SIZE(smdk_default_nand_part), + .partitions = smdk_default_nand_part, + }, +}; + +/* choose a set of timings which should suit most 512Mbit + * chips and beyond. +*/ + +static struct s3c2410_platform_nand smdk_nand_info = { + .tacls = 20, + .twrph0 = 60, + .twrph1 = 20, + .nr_sets = ARRAY_SIZE(smdk_nand_sets), + .sets = smdk_nand_sets, +}; + +/* devices we initialise */ + +static struct platform_device __initdata *smdk_devs[] = { + &s3c_device_nand, +}; + +void __init smdk_machine_init(void) +{ + /* Configure the LEDs (even if we have no LED support)*/ + + s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP); + s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP); + s3c2410_gpio_cfgpin(S3C2410_GPF6, S3C2410_GPF6_OUTP); + s3c2410_gpio_cfgpin(S3C2410_GPF7, S3C2410_GPF7_OUTP); + + s3c2410_gpio_setpin(S3C2410_GPF4, 1); + s3c2410_gpio_setpin(S3C2410_GPF5, 1); + s3c2410_gpio_setpin(S3C2410_GPF6, 1); + s3c2410_gpio_setpin(S3C2410_GPF7, 1); + + s3c_device_nand.dev.platform_data = &smdk_nand_info; + + platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs)); + + s3c2410_pm_init(); +} diff --git a/arch/arm/mach-s3c2410/common-smdk.h b/arch/arm/mach-s3c2410/common-smdk.h new file mode 100644 index 000000000000..0e3a3be330a3 --- /dev/null +++ b/arch/arm/mach-s3c2410/common-smdk.h @@ -0,0 +1,15 @@ +/* linux/arch/arm/mach-s3c2410/common-smdk.h + * + * Copyright (c) 2006 Simtec Electronics + * Ben Dooks + * + * Common code for SMDK2410 and SMDK2440 boards + * + * http://www.fluff.org/ben/smdk2440/ + * + * 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. +*/ + +extern void smdk_machine_init(void); diff --git a/arch/arm/mach-s3c2410/mach-anubis.c b/arch/arm/mach-s3c2410/mach-anubis.c index 3e327b8e46be..cc97fbf66291 100644 --- a/arch/arm/mach-s3c2410/mach-anubis.c +++ b/arch/arm/mach-s3c2410/mach-anubis.c @@ -232,8 +232,8 @@ static void anubis_nand_select(struct s3c2410_nand_set *set, int slot) static struct s3c2410_platform_nand anubis_nand_info = { .tacls = 25, - .twrph0 = 80, - .twrph1 = 80, + .twrph0 = 55, + .twrph1 = 40, .nr_sets = ARRAY_SIZE(anubis_nand_sets), .sets = anubis_nand_sets, .select_chip = anubis_nand_select, diff --git a/arch/arm/mach-s3c2410/mach-rx3715.c b/arch/arm/mach-s3c2410/mach-rx3715.c index 0260ed5ab946..306afc1d7cd3 100644 --- a/arch/arm/mach-s3c2410/mach-rx3715.c +++ b/arch/arm/mach-s3c2410/mach-rx3715.c @@ -32,6 +32,11 @@ #include #include +#include +#include +#include +#include + #include #include #include @@ -46,6 +51,7 @@ #include #include +#include #include #include "clock.h" @@ -170,12 +176,39 @@ static struct s3c2410fb_mach_info rx3715_lcdcfg __initdata = { }, }; +static struct mtd_partition rx3715_nand_part[] = { + [0] = { + .name = "Whole Flash", + .offset = 0, + .size = MTDPART_SIZ_FULL, + .mask_flags = MTD_WRITEABLE, + } +}; + +static struct s3c2410_nand_set rx3715_nand_sets[] = { + [0] = { + .name = "Internal", + .nr_chips = 1, + .nr_partitions = ARRAY_SIZE(rx3715_nand_part), + .partitions = rx3715_nand_part, + }, +}; + +static struct s3c2410_platform_nand rx3715_nand_info = { + .tacls = 25, + .twrph0 = 50, + .twrph1 = 15, + .nr_sets = ARRAY_SIZE(rx3715_nand_sets), + .sets = rx3715_nand_sets, +}; + static struct platform_device *rx3715_devices[] __initdata = { &s3c_device_usb, &s3c_device_lcd, &s3c_device_wdt, &s3c_device_i2c, &s3c_device_iis, + &s3c_device_nand, }; static struct s3c24xx_board rx3715_board __initdata = { @@ -185,6 +218,8 @@ static struct s3c24xx_board rx3715_board __initdata = { static void __init rx3715_map_io(void) { + s3c_device_nand.dev.platform_data = &rx3715_nand_info; + s3c24xx_init_io(rx3715_iodesc, ARRAY_SIZE(rx3715_iodesc)); s3c24xx_init_clocks(16934000); s3c24xx_init_uarts(rx3715_uartcfgs, ARRAY_SIZE(rx3715_uartcfgs)); diff --git a/arch/arm/mach-s3c2410/mach-smdk2410.c b/arch/arm/mach-s3c2410/mach-smdk2410.c index 1e76e1fdfcea..2db932d72c5a 100644 --- a/arch/arm/mach-s3c2410/mach-smdk2410.c +++ b/arch/arm/mach-s3c2410/mach-smdk2410.c @@ -28,7 +28,8 @@ * Ben Dooks * * 10-Mar-2005 LCVR Changed S3C2410_VA to S3C24XX_VA - * 20-Sep-2005 BJD Added static to non-exported items + * 20-Sep-2005 BJD Added static to non-exported items + * 01-Apr-2006 BJD Moved init code to common smdk * ***********************************************************************/ @@ -54,6 +55,8 @@ #include "devs.h" #include "cpu.h" +#include "common-smdk.h" + static struct map_desc smdk2410_iodesc[] __initdata = { /* nothing here yet */ }; @@ -107,11 +110,6 @@ static void __init smdk2410_map_io(void) s3c24xx_set_board(&smdk2410_board); } -static void __init smdk2410_init_irq(void) -{ - s3c24xx_init_irq(); -} - MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switch * to SMDK2410 */ /* Maintainer: Jonas Dietsche */ @@ -119,7 +117,8 @@ MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switc .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc, .boot_params = S3C2410_SDRAM_PA + 0x100, .map_io = smdk2410_map_io, - .init_irq = smdk2410_init_irq, + .init_irq = s3c24xx_init_irq, + .init_machine = smdk_machine_init, .timer = &s3c24xx_timer, MACHINE_END diff --git a/arch/arm/mach-s3c2410/mach-smdk2440.c b/arch/arm/mach-s3c2410/mach-smdk2440.c index f4315721c3b8..5fffd1d51047 100644 --- a/arch/arm/mach-s3c2410/mach-smdk2440.c +++ b/arch/arm/mach-s3c2410/mach-smdk2440.c @@ -53,7 +53,8 @@ #include "clock.h" #include "devs.h" #include "cpu.h" -#include "pm.h" + +#include "common-smdk.h" static struct map_desc smdk2440_iodesc[] __initdata = { /* ISA IO Space map (memory space selected by A24) */ @@ -197,21 +198,9 @@ static void __init smdk2440_map_io(void) static void __init smdk2440_machine_init(void) { - /* Configure the LEDs (even if we have no LED support)*/ - - s3c2410_gpio_cfgpin(S3C2410_GPF4, S3C2410_GPF4_OUTP); - s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_OUTP); - s3c2410_gpio_cfgpin(S3C2410_GPF6, S3C2410_GPF6_OUTP); - s3c2410_gpio_cfgpin(S3C2410_GPF7, S3C2410_GPF7_OUTP); - - s3c2410_gpio_setpin(S3C2410_GPF4, 0); - s3c2410_gpio_setpin(S3C2410_GPF5, 0); - s3c2410_gpio_setpin(S3C2410_GPF6, 0); - s3c2410_gpio_setpin(S3C2410_GPF7, 0); - s3c24xx_fb_set_platdata(&smdk2440_lcd_cfg); - s3c2410_pm_init(); + smdk_machine_init(); } MACHINE_START(S3C2440, "SMDK2440") diff --git a/arch/arm/mach-sa1100/collie.c b/arch/arm/mach-sa1100/collie.c index 102454082474..676b5c5b75bb 100644 --- a/arch/arm/mach-sa1100/collie.c +++ b/arch/arm/mach-sa1100/collie.c @@ -11,7 +11,8 @@ * published by the Free Software Foundation. * * ChangeLog: - * 03-06-2004 John Lenz + * 2006 Pavel Machek + * 03-06-2004 John Lenz * 06-04-2002 Chris Larson * 04-16-2001 Lineo Japan,Inc. ... */ @@ -87,12 +88,75 @@ static struct mcp_plat_data collie_mcp_data = { .sclk_rate = 11981000, }; +#ifdef CONFIG_SHARP_LOCOMO +/* + * low-level UART features. + */ +static struct locomo_dev *uart_dev = NULL; + +static void collie_uart_set_mctrl(struct uart_port *port, u_int mctrl) +{ + if (!uart_dev) return; + + if (mctrl & TIOCM_RTS) + locomo_gpio_write(uart_dev, LOCOMO_GPIO_RTS, 0); + else + locomo_gpio_write(uart_dev, LOCOMO_GPIO_RTS, 1); + + if (mctrl & TIOCM_DTR) + locomo_gpio_write(uart_dev, LOCOMO_GPIO_DTR, 0); + else + locomo_gpio_write(uart_dev, LOCOMO_GPIO_DTR, 1); +} + +static u_int collie_uart_get_mctrl(struct uart_port *port) +{ + int ret = TIOCM_CD; + unsigned int r; + if (!uart_dev) return ret; + + r = locomo_gpio_read_output(uart_dev, LOCOMO_GPIO_CTS & LOCOMO_GPIO_DSR); + if (r & LOCOMO_GPIO_CTS) + ret |= TIOCM_CTS; + if (r & LOCOMO_GPIO_DSR) + ret |= TIOCM_DSR; + + return ret; +} static struct sa1100_port_fns collie_port_fns __initdata = { .set_mctrl = collie_uart_set_mctrl, .get_mctrl = collie_uart_get_mctrl, }; +static int collie_uart_probe(struct locomo_dev *dev) +{ + uart_dev = dev; + return 0; +} + +static int collie_uart_remove(struct locomo_dev *dev) +{ + uart_dev = NULL; + return 0; +} + +static struct locomo_driver collie_uart_driver = { + .drv = { + .name = "collie_uart", + }, + .devid = LOCOMO_DEVID_UART, + .probe = collie_uart_probe, + .remove = collie_uart_remove, +}; + +static int __init collie_uart_init(void) { + return locomo_driver_register(&collie_uart_driver); +} +device_initcall(collie_uart_init); + +#endif + static struct resource locomo_resources[] = { [0] = { @@ -218,6 +282,12 @@ static void __init collie_map_io(void) { sa1100_map_io(); iotable_init(collie_io_desc, ARRAY_SIZE(collie_io_desc)); + +#ifdef CONFIG_SHARP_LOCOMO + sa1100_register_uart_fns(&collie_port_fns); +#endif + sa1100_register_uart(0, 3); + sa1100_register_uart(1, 1); } MACHINE_START(COLLIE, "Sharp-Collie") diff --git a/arch/arm/mm/cache-v4wb.S b/arch/arm/mm/cache-v4wb.S index 5c4055b62d97..54e3c5bb5186 100644 --- a/arch/arm/mm/cache-v4wb.S +++ b/arch/arm/mm/cache-v4wb.S @@ -10,7 +10,7 @@ #include #include #include -#include +#include #include #include "proc-macros.S" @@ -46,6 +46,11 @@ */ #define CACHE_DLIMIT (CACHE_DSIZE * 4) + .data +flush_base: + .long FLUSH_BASE + .text + /* * flush_user_cache_all() * @@ -63,11 +68,21 @@ ENTRY(v4wb_flush_kern_cache_all) mov ip, #0 mcr p15, 0, ip, c7, c5, 0 @ invalidate I cache __flush_whole_cache: - mov r0, #FLUSH_BASE - add r1, r0, #CACHE_DSIZE -1: ldr r2, [r0], #32 - cmp r0, r1 + ldr r3, =flush_base + ldr r1, [r3, #0] + eor r1, r1, #CACHE_DSIZE + str r1, [r3, #0] + add r2, r1, #CACHE_DSIZE +1: ldr r3, [r1], #32 + cmp r1, r2 blo 1b +#ifdef FLUSH_BASE_MINICACHE + add r2, r2, #FLUSH_BASE_MINICACHE - FLUSH_BASE + sub r1, r2, #512 @ only 512 bytes +1: ldr r3, [r1], #32 + cmp r1, r2 + blo 1b +#endif mcr p15, 0, ip, c7, c10, 4 @ drain write buffer mov pc, lr @@ -82,6 +97,7 @@ __flush_whole_cache: * - flags - vma_area_struct flags describing address space */ ENTRY(v4wb_flush_user_cache_range) + mov ip, #0 sub r3, r1, r0 @ calculate total size tst r2, #VM_EXEC @ executable region? mcrne p15, 0, ip, c7, c5, 0 @ invalidate I cache diff --git a/arch/arm/mm/consistent.c b/arch/arm/mm/consistent.c index 8a1bfcd50087..50e6b6bfb2e2 100644 --- a/arch/arm/mm/consistent.c +++ b/arch/arm/mm/consistent.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -272,6 +273,17 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp, void * dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp) { + if (arch_is_coherent()) { + void *virt; + + virt = kmalloc(size, gfp); + if (!virt) + return NULL; + *handle = virt_to_dma(dev, virt); + + return virt; + } + return __dma_alloc(dev, size, handle, gfp, pgprot_noncached(pgprot_kernel)); } @@ -350,6 +362,11 @@ void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr WARN_ON(irqs_disabled()); + if (arch_is_coherent()) { + kfree(cpu_addr); + return; + } + size = PAGE_ALIGN(size); spin_lock_irqsave(&consistent_lock, flags); diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 88279124317a..9ea1f87a7079 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -455,14 +456,14 @@ static void __init devicemaps_init(struct machine_desc *mdesc) #ifdef FLUSH_BASE map.pfn = __phys_to_pfn(FLUSH_BASE_PHYS); map.virtual = FLUSH_BASE; - map.length = PGDIR_SIZE; + map.length = SZ_1M; map.type = MT_CACHECLEAN; create_mapping(&map); #endif #ifdef FLUSH_BASE_MINICACHE - map.pfn = __phys_to_pfn(FLUSH_BASE_PHYS + PGDIR_SIZE); + map.pfn = __phys_to_pfn(FLUSH_BASE_PHYS + SZ_1M); map.virtual = FLUSH_BASE_MINICACHE; - map.length = PGDIR_SIZE; + map.length = SZ_1M; map.type = MT_MINICLEAN; create_mapping(&map); #endif diff --git a/arch/arm/mm/mm-armv.c b/arch/arm/mm/mm-armv.c index 5e5d05bcad50..f14b2d0f3690 100644 --- a/arch/arm/mm/mm-armv.c +++ b/arch/arm/mm/mm-armv.c @@ -388,6 +388,17 @@ void __init build_mem_type_table(void) cp = &cache_policies[cachepolicy]; kern_pgprot = user_pgprot = cp->pte; + /* + * Enable CPU-specific coherency if supported. + * (Only available on XSC3 at the moment.) + */ + if (arch_is_coherent()) { + if (cpu_is_xsc3()) { + mem_types[MT_MEMORY].prot_sect |= PMD_SECT_S; + mem_types[MT_MEMORY].prot_pte |= L_PTE_COHERENT; + } + } + /* * ARMv6 and above have extended page tables. */ diff --git a/arch/arm/mm/proc-sa110.S b/arch/arm/mm/proc-sa110.S index c916a6cae404..a2dd5ae1077d 100644 --- a/arch/arm/mm/proc-sa110.S +++ b/arch/arm/mm/proc-sa110.S @@ -26,22 +26,7 @@ * the cache line size of the I and D cache */ #define DCACHELINESIZE 32 -#define FLUSH_OFFSET 32768 - .macro flush_110_dcache rd, ra, re - ldr \rd, =flush_base - ldr \ra, [\rd] - eor \ra, \ra, #FLUSH_OFFSET - str \ra, [\rd] - add \re, \ra, #16384 @ only necessary for 16k -1001: ldr \rd, [\ra], #DCACHELINESIZE - teq \re, \ra - bne 1001b - .endm - - .data -flush_base: - .long FLUSH_BASE .text /* @@ -145,13 +130,11 @@ ENTRY(cpu_sa110_dcache_clean_area) */ .align 5 ENTRY(cpu_sa110_switch_mm) - flush_110_dcache r3, ip, r1 - mov r1, #0 - mcr p15, 0, r1, c7, c5, 0 @ invalidate I cache - mcr p15, 0, r1, c7, c10, 4 @ drain WB + str lr, [sp, #-4]! + bl v4wb_flush_kern_cache_all @ clears IP mcr p15, 0, r0, c2, c0, 0 @ load page table pointer - mcr p15, 0, r1, c8, c7, 0 @ invalidate I & D TLBs - mov pc, lr + mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs + ldr pc, [sp], #4 /* * cpu_sa110_set_pte(ptep, pte) diff --git a/arch/arm/mm/proc-sa1100.S b/arch/arm/mm/proc-sa1100.S index 41f21f2dd8ff..777ad99c1439 100644 --- a/arch/arm/mm/proc-sa1100.S +++ b/arch/arm/mm/proc-sa1100.S @@ -30,30 +30,6 @@ * the cache line size of the I and D cache */ #define DCACHELINESIZE 32 -#define FLUSH_OFFSET 32768 - - .macro flush_1100_dcache rd, ra, re - ldr \rd, =flush_base - ldr \ra, [\rd] - eor \ra, \ra, #FLUSH_OFFSET - str \ra, [\rd] - add \re, \ra, #8192 @ only necessary for 8k -1001: ldr \rd, [\ra], #DCACHELINESIZE - teq \re, \ra - bne 1001b -#ifdef FLUSH_BASE_MINICACHE - add \ra, \ra, #FLUSH_BASE_MINICACHE - FLUSH_BASE - add \re, \ra, #512 @ only 512 bytes -1002: ldr \rd, [\ra], #DCACHELINESIZE - teq \re, \ra - bne 1002b -#endif - .endm - - .data -flush_base: - .long FLUSH_BASE - .text __INIT @@ -79,9 +55,8 @@ ENTRY(cpu_sa1100_proc_fin) stmfd sp!, {lr} mov ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE msr cpsr_c, ip - flush_1100_dcache r0, r1, r2 @ clean caches - mov r0, #0 - mcr p15, 0, r0, c15, c2, 2 @ Disable clock switching + bl v4wb_flush_kern_cache_all + mcr p15, 0, ip, c15, c2, 2 @ Disable clock switching mrc p15, 0, r0, c1, c0, 0 @ ctrl register bic r0, r0, #0x1000 @ ...i............ bic r0, r0, #0x000e @ ............wca. @@ -167,14 +142,12 @@ ENTRY(cpu_sa1100_dcache_clean_area) */ .align 5 ENTRY(cpu_sa1100_switch_mm) - flush_1100_dcache r3, ip, r1 - mov ip, #0 - mcr p15, 0, ip, c7, c5, 0 @ invalidate I cache + str lr, [sp, #-4]! + bl v4wb_flush_kern_cache_all @ clears IP mcr p15, 0, ip, c9, c0, 0 @ invalidate RB - mcr p15, 0, ip, c7, c10, 4 @ drain WB mcr p15, 0, r0, c2, c0, 0 @ load page table pointer mcr p15, 0, ip, c8, c7, 0 @ invalidate I & D TLBs - mov pc, lr + ldr pc, [sp], #4 /* * cpu_sa1100_set_pte(ptep, pte) diff --git a/arch/arm/mm/proc-xsc3.S b/arch/arm/mm/proc-xsc3.S index f90513e9af0c..80873b36c3f7 100644 --- a/arch/arm/mm/proc-xsc3.S +++ b/arch/arm/mm/proc-xsc3.S @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include "proc-macros.S" @@ -370,7 +371,7 @@ ENTRY(cpu_xsc3_switch_mm) ENTRY(cpu_xsc3_set_pte) str r1, [r0], #-2048 @ linux version - bic r2, r1, #0xff0 + bic r2, r1, #0xdf0 @ Keep C, B, coherency bits orr r2, r2, #PTE_TYPE_EXT @ extended page eor r3, r1, #L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_WRITE | L_PTE_DIRTY diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig index 0887bb2a2551..ec49495e651e 100644 --- a/arch/arm/plat-omap/Kconfig +++ b/arch/arm/plat-omap/Kconfig @@ -70,13 +70,13 @@ config OMAP_MPU_TIMER config OMAP_32K_TIMER bool "Use 32KHz timer" - depends on ARCH_OMAP16XX + depends on ARCH_OMAP16XX || ARCH_OMAP24XX help Select this option if you want to enable the OMAP 32KHz timer. This timer saves power compared to the OMAP_MPU_TIMER, and has support for no tick during idle. The 32KHz timer provides less intra-tick resolution than OMAP_MPU_TIMER. The 32KHz timer is - currently only available for OMAP-16xx. + currently only available for OMAP16XX and 24XX. endchoice diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile index 9ccf1943fc94..2896b4546411 100644 --- a/arch/arm/plat-omap/Makefile +++ b/arch/arm/plat-omap/Makefile @@ -3,16 +3,16 @@ # # Common support -obj-y := common.o sram.o sram-fn.o clock.o devices.o dma.o mux.o gpio.o mcbsp.o usb.o +obj-y := common.o sram.o sram-fn.o clock.o devices.o dma.o mux.o gpio.o mcbsp.o usb.o fb.o obj-m := obj-n := obj- := +obj-$(CONFIG_OMAP_32K_TIMER) += timer32k.o + # OCPI interconnect support for 1710, 1610 and 5912 obj-$(CONFIG_ARCH_OMAP16XX) += ocpi.o -# Power Management -obj-$(CONFIG_PM) += pm.o sleep.o obj-$(CONFIG_CPU_FREQ) += cpu-omap.o obj-$(CONFIG_OMAP_DM_TIMER) += dmtimer.o diff --git a/arch/arm/plat-omap/clock.c b/arch/arm/plat-omap/clock.c index 3c2bfc0efdaf..32ec04c58bcd 100644 --- a/arch/arm/plat-omap/clock.c +++ b/arch/arm/plat-omap/clock.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -37,17 +38,38 @@ static struct clk_functions *arch_clock; * Standard clock functions defined in include/linux/clk.h *-------------------------------------------------------------------------*/ +/* + * Returns a clock. Note that we first try to use device id on the bus + * and clock name. If this fails, we try to use clock name only. + */ struct clk * clk_get(struct device *dev, const char *id) { struct clk *p, *clk = ERR_PTR(-ENOENT); + int idno; + + if (dev == NULL || dev->bus != &platform_bus_type) + idno = -1; + else + idno = to_platform_device(dev)->id; mutex_lock(&clocks_mutex); + + list_for_each_entry(p, &clocks, node) { + if (p->id == idno && + strcmp(id, p->name) == 0 && try_module_get(p->owner)) { + clk = p; + goto found; + } + } + list_for_each_entry(p, &clocks, node) { if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) { clk = p; break; } } + +found: mutex_unlock(&clocks_mutex); return clk; @@ -59,6 +81,9 @@ int clk_enable(struct clk *clk) unsigned long flags; int ret = 0; + if (clk == NULL || IS_ERR(clk)) + return -EINVAL; + spin_lock_irqsave(&clockfw_lock, flags); if (arch_clock->clk_enable) ret = arch_clock->clk_enable(clk); @@ -72,6 +97,9 @@ void clk_disable(struct clk *clk) { unsigned long flags; + if (clk == NULL || IS_ERR(clk)) + return; + spin_lock_irqsave(&clockfw_lock, flags); if (arch_clock->clk_disable) arch_clock->clk_disable(clk); @@ -84,6 +112,9 @@ int clk_get_usecount(struct clk *clk) unsigned long flags; int ret = 0; + if (clk == NULL || IS_ERR(clk)) + return 0; + spin_lock_irqsave(&clockfw_lock, flags); ret = clk->usecount; spin_unlock_irqrestore(&clockfw_lock, flags); @@ -97,6 +128,9 @@ unsigned long clk_get_rate(struct clk *clk) unsigned long flags; unsigned long ret = 0; + if (clk == NULL || IS_ERR(clk)) + return 0; + spin_lock_irqsave(&clockfw_lock, flags); ret = clk->rate; spin_unlock_irqrestore(&clockfw_lock, flags); @@ -121,6 +155,9 @@ long clk_round_rate(struct clk *clk, unsigned long rate) unsigned long flags; long ret = 0; + if (clk == NULL || IS_ERR(clk)) + return ret; + spin_lock_irqsave(&clockfw_lock, flags); if (arch_clock->clk_round_rate) ret = arch_clock->clk_round_rate(clk, rate); @@ -133,7 +170,10 @@ EXPORT_SYMBOL(clk_round_rate); int clk_set_rate(struct clk *clk, unsigned long rate) { unsigned long flags; - int ret = 0; + int ret = -EINVAL; + + if (clk == NULL || IS_ERR(clk)) + return ret; spin_lock_irqsave(&clockfw_lock, flags); if (arch_clock->clk_set_rate) @@ -147,7 +187,10 @@ EXPORT_SYMBOL(clk_set_rate); int clk_set_parent(struct clk *clk, struct clk *parent) { unsigned long flags; - int ret = 0; + int ret = -EINVAL; + + if (clk == NULL || IS_ERR(clk) || parent == NULL || IS_ERR(parent)) + return ret; spin_lock_irqsave(&clockfw_lock, flags); if (arch_clock->clk_set_parent) @@ -163,6 +206,9 @@ struct clk *clk_get_parent(struct clk *clk) unsigned long flags; struct clk * ret = NULL; + if (clk == NULL || IS_ERR(clk)) + return ret; + spin_lock_irqsave(&clockfw_lock, flags); if (arch_clock->clk_get_parent) ret = arch_clock->clk_get_parent(clk); @@ -199,6 +245,9 @@ __setup("mpurate=", omap_clk_setup); /* Used for clocks that always have same value as the parent clock */ void followparent_recalc(struct clk *clk) { + if (clk == NULL || IS_ERR(clk)) + return; + clk->rate = clk->parent->rate; } @@ -207,6 +256,9 @@ void propagate_rate(struct clk * tclk) { struct clk *clkp; + if (tclk == NULL || IS_ERR(tclk)) + return; + list_for_each_entry(clkp, &clocks, node) { if (likely(clkp->parent != tclk)) continue; @@ -217,6 +269,9 @@ void propagate_rate(struct clk * tclk) int clk_register(struct clk *clk) { + if (clk == NULL || IS_ERR(clk)) + return -EINVAL; + mutex_lock(&clocks_mutex); list_add(&clk->node, &clocks); if (clk->init) @@ -229,6 +284,9 @@ EXPORT_SYMBOL(clk_register); void clk_unregister(struct clk *clk) { + if (clk == NULL || IS_ERR(clk)) + return; + mutex_lock(&clocks_mutex); list_del(&clk->node); mutex_unlock(&clocks_mutex); @@ -239,6 +297,9 @@ void clk_deny_idle(struct clk *clk) { unsigned long flags; + if (clk == NULL || IS_ERR(clk)) + return; + spin_lock_irqsave(&clockfw_lock, flags); if (arch_clock->clk_deny_idle) arch_clock->clk_deny_idle(clk); @@ -250,6 +311,9 @@ void clk_allow_idle(struct clk *clk) { unsigned long flags; + if (clk == NULL || IS_ERR(clk)) + return; + spin_lock_irqsave(&clockfw_lock, flags); if (arch_clock->clk_allow_idle) arch_clock->clk_allow_idle(clk); diff --git a/arch/arm/plat-omap/devices.c b/arch/arm/plat-omap/devices.c index 9dcce904b608..5d5d6eb222dd 100644 --- a/arch/arm/plat-omap/devices.c +++ b/arch/arm/plat-omap/devices.c @@ -24,14 +24,7 @@ #include #include #include - - -void omap_nop_release(struct device *dev) -{ - /* Nothing */ -} - -/*-------------------------------------------------------------------------*/ +#include #if defined(CONFIG_I2C_OMAP) || defined(CONFIG_I2C_OMAP_MODULE) @@ -58,9 +51,6 @@ static struct resource i2c_resources1[] = { static struct platform_device omap_i2c_device1 = { .name = "i2c_omap", .id = 1, - .dev = { - .release = omap_nop_release, - }, .num_resources = ARRAY_SIZE(i2c_resources1), .resource = i2c_resources1, }; @@ -97,6 +87,62 @@ static void omap_init_i2c(void) static inline void omap_init_i2c(void) {} #endif +/*-------------------------------------------------------------------------*/ +#if defined(CONFIG_KEYBOARD_OMAP) || defined(CONFIG_KEYBOARD_OMAP_MODULE) + +static void omap_init_kp(void) +{ + if (machine_is_omap_h2() || machine_is_omap_h3()) { + omap_cfg_reg(F18_1610_KBC0); + omap_cfg_reg(D20_1610_KBC1); + omap_cfg_reg(D19_1610_KBC2); + omap_cfg_reg(E18_1610_KBC3); + omap_cfg_reg(C21_1610_KBC4); + + omap_cfg_reg(G18_1610_KBR0); + omap_cfg_reg(F19_1610_KBR1); + omap_cfg_reg(H14_1610_KBR2); + omap_cfg_reg(E20_1610_KBR3); + omap_cfg_reg(E19_1610_KBR4); + omap_cfg_reg(N19_1610_KBR5); + } else if (machine_is_omap_perseus2()) { + omap_cfg_reg(E2_730_KBR0); + omap_cfg_reg(J7_730_KBR1); + omap_cfg_reg(E1_730_KBR2); + omap_cfg_reg(F3_730_KBR3); + omap_cfg_reg(D2_730_KBR4); + + omap_cfg_reg(C2_730_KBC0); + omap_cfg_reg(D3_730_KBC1); + omap_cfg_reg(E4_730_KBC2); + omap_cfg_reg(F4_730_KBC3); + omap_cfg_reg(E3_730_KBC4); + } else if (machine_is_omap_h4()) { + omap_cfg_reg(T19_24XX_KBR0); + omap_cfg_reg(R19_24XX_KBR1); + omap_cfg_reg(V18_24XX_KBR2); + omap_cfg_reg(M21_24XX_KBR3); + omap_cfg_reg(E5__24XX_KBR4); + if (omap_has_menelaus()) { + omap_cfg_reg(B3__24XX_KBR5); + omap_cfg_reg(AA4_24XX_KBC2); + omap_cfg_reg(B13_24XX_KBC6); + } else { + omap_cfg_reg(M18_24XX_KBR5); + omap_cfg_reg(H19_24XX_KBC2); + omap_cfg_reg(N19_24XX_KBC6); + } + omap_cfg_reg(R20_24XX_KBC0); + omap_cfg_reg(M14_24XX_KBC1); + omap_cfg_reg(V17_24XX_KBC3); + omap_cfg_reg(P21_24XX_KBC4); + omap_cfg_reg(L14_24XX_KBC5); + } +} +#else +static inline void omap_init_kp(void) {} +#endif + /*-------------------------------------------------------------------------*/ #if defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE) @@ -130,7 +176,6 @@ static struct platform_device mmc_omap_device1 = { .name = "mmci-omap", .id = 1, .dev = { - .release = omap_nop_release, .dma_mask = &mmc1_dmamask, .platform_data = &mmc1_conf, }, @@ -160,7 +205,6 @@ static struct platform_device mmc_omap_device2 = { .name = "mmci-omap", .id = 2, .dev = { - .release = omap_nop_release, .dma_mask = &mmc2_dmamask, .platform_data = &mmc2_conf, }, @@ -240,6 +284,52 @@ static void __init omap_init_mmc(void) static inline void omap_init_mmc(void) {} #endif +/*-------------------------------------------------------------------------*/ + +/* Numbering for the SPI-capable controllers when used for SPI: + * spi = 1 + * uwire = 2 + * mmc1..2 = 3..4 + * mcbsp1..3 = 5..7 + */ + +#if defined(CONFIG_SPI_OMAP_UWIRE) || defined(CONFIG_SPI_OMAP_UWIRE_MODULE) + +#define OMAP_UWIRE_BASE 0xfffb3000 + +static struct resource uwire_resources[] = { + { + .start = OMAP_UWIRE_BASE, + .end = OMAP_UWIRE_BASE + 0x20, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device omap_uwire_device = { + .name = "omap_uwire", + .id = -1, + .num_resources = ARRAY_SIZE(uwire_resources), + .resource = uwire_resources, +}; + +static void omap_init_uwire(void) +{ + /* FIXME define and use a boot tag; not all boards will be hooking + * up devices to the microwire controller, and multi-board configs + * mean that CONFIG_SPI_OMAP_UWIRE may be configured anyway... + */ + + /* board-specific code must configure chipselects (only a few + * are normally used) and SCLK/SDI/SDO (each has two choices). + */ + (void) platform_device_register(&omap_uwire_device); +} +#else +static inline void omap_init_uwire(void) {} +#endif + +/*-------------------------------------------------------------------------*/ + #if defined(CONFIG_OMAP_WATCHDOG) || defined(CONFIG_OMAP_WATCHDOG_MODULE) #ifdef CONFIG_ARCH_OMAP24XX @@ -259,9 +349,6 @@ static struct resource wdt_resources[] = { static struct platform_device omap_wdt_device = { .name = "omap_wdt", .id = -1, - .dev = { - .release = omap_nop_release, - }, .num_resources = ARRAY_SIZE(wdt_resources), .resource = wdt_resources, }; @@ -295,9 +382,6 @@ static struct resource rng_resources[] = { static struct platform_device omap_rng_device = { .name = "omap_rng", .id = -1, - .dev = { - .release = omap_nop_release, - }, .num_resources = ARRAY_SIZE(rng_resources), .resource = rng_resources, }; @@ -310,40 +394,6 @@ static void omap_init_rng(void) static inline void omap_init_rng(void) {} #endif -#if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE) - -static struct omap_lcd_config omap_fb_conf; - -static u64 omap_fb_dma_mask = ~(u32)0; - -static struct platform_device omap_fb_device = { - .name = "omapfb", - .id = -1, - .dev = { - .release = omap_nop_release, - .dma_mask = &omap_fb_dma_mask, - .coherent_dma_mask = ~(u32)0, - .platform_data = &omap_fb_conf, - }, - .num_resources = 0, -}; - -static inline void omap_init_fb(void) -{ - const struct omap_lcd_config *conf; - - conf = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config); - if (conf != NULL) - omap_fb_conf = *conf; - platform_device_register(&omap_fb_device); -} - -#else - -static inline void omap_init_fb(void) {} - -#endif - /* * This gets called after board-specific INIT_MACHINE, and initializes most * on-chip peripherals accessible on this board (except for few like USB): @@ -369,9 +419,10 @@ static int __init omap_init_devices(void) /* please keep these calls, and their implementations above, * in alphabetical order so they're easier to sort through. */ - omap_init_fb(); omap_init_i2c(); + omap_init_kp(); omap_init_mmc(); + omap_init_uwire(); omap_init_wdt(); omap_init_rng(); diff --git a/arch/arm/plat-omap/dma.c b/arch/arm/plat-omap/dma.c index a4e5ac77f6df..5dac4230360d 100644 --- a/arch/arm/plat-omap/dma.c +++ b/arch/arm/plat-omap/dma.c @@ -1258,6 +1258,11 @@ void omap_stop_lcd_dma(void) omap_writew(w, OMAP1610_DMA_LCD_CTRL); } +int omap_lcd_dma_ext_running(void) +{ + return lcd_dma.ext_ctrl && lcd_dma.active; +} + /*----------------------------------------------------------------------------*/ static int __init omap_init_dma(void) @@ -1389,6 +1394,7 @@ EXPORT_SYMBOL(omap_free_lcd_dma); EXPORT_SYMBOL(omap_enable_lcd_dma); EXPORT_SYMBOL(omap_setup_lcd_dma); EXPORT_SYMBOL(omap_stop_lcd_dma); +EXPORT_SYMBOL(omap_lcd_dma_ext_running); EXPORT_SYMBOL(omap_set_lcd_dma_b1); EXPORT_SYMBOL(omap_set_lcd_dma_single_transfer); EXPORT_SYMBOL(omap_set_lcd_dma_ext_controller); diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c index 38d7ebf87920..eba3cb52ad87 100644 --- a/arch/arm/plat-omap/dmtimer.c +++ b/arch/arm/plat-omap/dmtimer.c @@ -97,6 +97,32 @@ int omap_dm_timers_active(void) } +/** + * omap_dm_timer_modify_idlect_mask - Check if any running timers use ARMXOR + * @inputmask: current value of idlect mask + */ +__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask) +{ + int n; + + /* If ARMXOR cannot be idled this function call is unnecessary */ + if (!(inputmask & (1 << 1))) + return inputmask; + + /* If any active timer is using ARMXOR return modified mask */ + for (n = 0; dm_timers[n].base; ++n) + if (omap_dm_timer_read_reg(&dm_timers[n], OMAP_TIMER_CTRL_REG)& + OMAP_TIMER_CTRL_ST) { + if (((omap_readl(MOD_CONF_CTRL_1)>>(n*2)) & 0x03) == 0) + inputmask &= ~(1 << 1); + else + inputmask &= ~(1 << 2); + } + + return inputmask; +} + + void omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) { int n = (timer - dm_timers) << 1; diff --git a/arch/arm/plat-omap/fb.c b/arch/arm/plat-omap/fb.c new file mode 100644 index 000000000000..305e9b990b71 --- /dev/null +++ b/arch/arm/plat-omap/fb.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE) + +static struct omapfb_platform_data omapfb_config; + +static u64 omap_fb_dma_mask = ~(u32)0; + +static struct platform_device omap_fb_device = { + .name = "omapfb", + .id = -1, + .dev = { + .dma_mask = &omap_fb_dma_mask, + .coherent_dma_mask = ~(u32)0, + .platform_data = &omapfb_config, + }, + .num_resources = 0, +}; + +/* called from map_io */ +void omapfb_reserve_mem(void) +{ + const struct omap_fbmem_config *fbmem_conf; + + omapfb_config.fbmem.fb_sram_start = omap_fb_sram_start; + omapfb_config.fbmem.fb_sram_size = omap_fb_sram_size; + + fbmem_conf = omap_get_config(OMAP_TAG_FBMEM, struct omap_fbmem_config); + + if (fbmem_conf != NULL) { + /* indicate that the bootloader already initialized the + * fb device, so we'll skip that part in the fb driver + */ + omapfb_config.fbmem.fb_sdram_start = fbmem_conf->fb_sdram_start; + omapfb_config.fbmem.fb_sdram_size = fbmem_conf->fb_sdram_size; + if (fbmem_conf->fb_sdram_size) { + pr_info("Reserving %u bytes SDRAM for frame buffer\n", + fbmem_conf->fb_sdram_size); + reserve_bootmem(fbmem_conf->fb_sdram_start, + fbmem_conf->fb_sdram_size); + } + } +} + +static inline int omap_init_fb(void) +{ + const struct omap_lcd_config *conf; + + conf = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config); + if (conf == NULL) + return 0; + + omapfb_config.lcd = *conf; + + return platform_device_register(&omap_fb_device); +} + +arch_initcall(omap_init_fb); + +#else + +void omapfb_reserve_mem(void) {} + +#endif + + diff --git a/arch/arm/plat-omap/gpio.c b/arch/arm/plat-omap/gpio.c index b4d5b9e4bfce..d3c8ea7eecfd 100644 --- a/arch/arm/plat-omap/gpio.c +++ b/arch/arm/plat-omap/gpio.c @@ -174,7 +174,7 @@ static int gpio_bank_count; static inline struct gpio_bank *get_gpio_bank(int gpio) { #ifdef CONFIG_ARCH_OMAP15XX - if (cpu_is_omap1510()) { + if (cpu_is_omap15xx()) { if (OMAP_GPIO_IS_MPUIO(gpio)) return &gpio_bank[0]; return &gpio_bank[1]; @@ -223,7 +223,7 @@ static inline int gpio_valid(int gpio) return 0; } #ifdef CONFIG_ARCH_OMAP15XX - if (cpu_is_omap1510() && gpio < 16) + if (cpu_is_omap15xx() && gpio < 16) return 0; #endif #if defined(CONFIG_ARCH_OMAP16XX) @@ -402,13 +402,13 @@ static inline void set_24xx_gpio_triggering(void __iomem *base, int gpio, int tr u32 gpio_bit = 1 << gpio; MOD_REG_BIT(OMAP24XX_GPIO_LEVELDETECT0, gpio_bit, - trigger & IRQT_LOW); + trigger & __IRQT_LOWLVL); MOD_REG_BIT(OMAP24XX_GPIO_LEVELDETECT1, gpio_bit, - trigger & IRQT_HIGH); + trigger & __IRQT_HIGHLVL); MOD_REG_BIT(OMAP24XX_GPIO_RISINGDETECT, gpio_bit, - trigger & IRQT_RISING); + trigger & __IRQT_RISEDGE); MOD_REG_BIT(OMAP24XX_GPIO_FALLINGDETECT, gpio_bit, - trigger & IRQT_FALLING); + trigger & __IRQT_FALEDGE); /* FIXME: Possibly do 'set_irq_handler(j, do_level_IRQ)' if only level * triggering requested. */ } @@ -422,9 +422,9 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger) case METHOD_MPUIO: reg += OMAP_MPUIO_GPIO_INT_EDGE; l = __raw_readl(reg); - if (trigger == IRQT_RISING) + if (trigger & __IRQT_RISEDGE) l |= 1 << gpio; - else if (trigger == IRQT_FALLING) + else if (trigger & __IRQT_FALEDGE) l &= ~(1 << gpio); else goto bad; @@ -432,9 +432,9 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger) case METHOD_GPIO_1510: reg += OMAP1510_GPIO_INT_CONTROL; l = __raw_readl(reg); - if (trigger == IRQT_RISING) + if (trigger & __IRQT_RISEDGE) l |= 1 << gpio; - else if (trigger == IRQT_FALLING) + else if (trigger & __IRQT_FALEDGE) l &= ~(1 << gpio); else goto bad; @@ -446,20 +446,21 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger) reg += OMAP1610_GPIO_EDGE_CTRL1; gpio &= 0x07; /* We allow only edge triggering, i.e. two lowest bits */ - if (trigger & ~IRQT_BOTHEDGE) + if (trigger & (__IRQT_LOWLVL | __IRQT_HIGHLVL)) BUG(); - /* NOTE: knows __IRQT_{FAL,RIS}EDGE match OMAP hardware */ - trigger &= 0x03; l = __raw_readl(reg); l &= ~(3 << (gpio << 1)); - l |= trigger << (gpio << 1); + if (trigger & __IRQT_RISEDGE) + l |= 2 << (gpio << 1); + if (trigger & __IRQT_FALEDGE) + l |= 1 << (gpio << 1); break; case METHOD_GPIO_730: reg += OMAP730_GPIO_INT_CONTROL; l = __raw_readl(reg); - if (trigger == IRQT_RISING) + if (trigger & __IRQT_RISEDGE) l |= 1 << gpio; - else if (trigger == IRQT_FALLING) + else if (trigger & __IRQT_FALEDGE) l &= ~(1 << gpio); else goto bad; @@ -491,7 +492,9 @@ static int gpio_irq_type(unsigned irq, unsigned type) if (check_gpio(gpio) < 0) return -EINVAL; - if (type & (__IRQT_LOWLVL|__IRQT_HIGHLVL|IRQT_PROBE)) + if (type & IRQT_PROBE) + return -EINVAL; + if (!cpu_is_omap24xx() && (type & (__IRQT_LOWLVL|__IRQT_HIGHLVL))) return -EINVAL; bank = get_gpio_bank(gpio); @@ -755,13 +758,32 @@ static void gpio_irq_handler(unsigned int irq, struct irqdesc *desc, if (bank->method == METHOD_GPIO_24XX) isr_reg = bank->base + OMAP24XX_GPIO_IRQSTATUS1; #endif - while(1) { - isr = __raw_readl(isr_reg); - _enable_gpio_irqbank(bank, isr, 0); - _clear_gpio_irqbank(bank, isr); - _enable_gpio_irqbank(bank, isr, 1); - desc->chip->unmask(irq); + u32 isr_saved, level_mask = 0; + + isr_saved = isr = __raw_readl(isr_reg); + + if (cpu_is_omap15xx() && (bank->method == METHOD_MPUIO)) + isr &= 0x0000ffff; + + if (cpu_is_omap24xx()) + level_mask = + __raw_readl(bank->base + + OMAP24XX_GPIO_LEVELDETECT0) | + __raw_readl(bank->base + + OMAP24XX_GPIO_LEVELDETECT1); + + /* clear edge sensitive interrupts before handler(s) are + called so that we don't miss any interrupt occurred while + executing them */ + _enable_gpio_irqbank(bank, isr_saved & ~level_mask, 0); + _clear_gpio_irqbank(bank, isr_saved & ~level_mask); + _enable_gpio_irqbank(bank, isr_saved & ~level_mask, 1); + + /* if there is only edge sensitive GPIO pin interrupts + configured, we could unmask GPIO bank interrupt immediately */ + if (!level_mask) + desc->chip->unmask(irq); if (!isr) break; @@ -774,6 +796,20 @@ static void gpio_irq_handler(unsigned int irq, struct irqdesc *desc, d = irq_desc + gpio_irq; desc_handle_irq(gpio_irq, d, regs); } + + if (cpu_is_omap24xx()) { + /* clear level sensitive interrupts after handler(s) */ + _enable_gpio_irqbank(bank, isr_saved & level_mask, 0); + _clear_gpio_irqbank(bank, isr_saved & level_mask); + _enable_gpio_irqbank(bank, isr_saved & level_mask, 1); + } + + /* if bank has any level sensitive GPIO pin interrupt + configured, we must unmask the bank interrupt only after + handler(s) are executed in order to avoid spurious bank + interrupt */ + if (level_mask) + desc->chip->unmask(irq); } } @@ -848,7 +884,7 @@ static int __init _omap_gpio_init(void) initialized = 1; - if (cpu_is_omap1510()) { + if (cpu_is_omap15xx()) { gpio_ick = clk_get(NULL, "arm_gpio_ck"); if (IS_ERR(gpio_ick)) printk("Could not get arm_gpio_ck\n"); @@ -869,7 +905,7 @@ static int __init _omap_gpio_init(void) } #ifdef CONFIG_ARCH_OMAP15XX - if (cpu_is_omap1510()) { + if (cpu_is_omap15xx()) { printk(KERN_INFO "OMAP1510 GPIO hardware\n"); gpio_bank_count = 2; gpio_bank = gpio_bank_1510; diff --git a/arch/arm/plat-omap/mcbsp.c b/arch/arm/plat-omap/mcbsp.c index 1cd2cace7e1b..196aac3ac329 100644 --- a/arch/arm/plat-omap/mcbsp.c +++ b/arch/arm/plat-omap/mcbsp.c @@ -34,7 +34,7 @@ #ifdef CONFIG_MCBSP_DEBUG #define DBG(x...) printk(x) #else -#define DBG(x...) do { } while (0) +#define DBG(x...) do { } while (0) #endif struct omap_mcbsp { @@ -44,6 +44,7 @@ struct omap_mcbsp { omap_mcbsp_word_length rx_word_length; omap_mcbsp_word_length tx_word_length; + omap_mcbsp_io_type_t io_type; /* IRQ or poll */ /* IRQ based TX/RX */ int rx_irq; int tx_irq; @@ -64,10 +65,19 @@ struct omap_mcbsp { }; static struct omap_mcbsp mcbsp[OMAP_MAX_MCBSP_COUNT]; +#ifdef CONFIG_ARCH_OMAP1 static struct clk *mcbsp_dsp_ck = 0; static struct clk *mcbsp_api_ck = 0; static struct clk *mcbsp_dspxor_ck = 0; - +#endif +#ifdef CONFIG_ARCH_OMAP2 +static struct clk *mcbsp1_ick = 0; +static struct clk *mcbsp1_fck = 0; +static struct clk *mcbsp2_ick = 0; +static struct clk *mcbsp2_fck = 0; +static struct clk *sys_ck = 0; +static struct clk *sys_clkout = 0; +#endif static void omap_mcbsp_dump_reg(u8 id) { @@ -88,7 +98,6 @@ static void omap_mcbsp_dump_reg(u8 id) DBG("***********************\n"); } - static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id, struct pt_regs *regs) { struct omap_mcbsp * mcbsp_tx = (struct omap_mcbsp *)(dev_id); @@ -109,7 +118,6 @@ static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *dev_id, struct pt_re return IRQ_HANDLED; } - static void omap_mcbsp_tx_dma_callback(int lch, u16 ch_status, void *data) { struct omap_mcbsp * mcbsp_dma_tx = (struct omap_mcbsp *)(data); @@ -176,7 +184,7 @@ static int omap_mcbsp_check(unsigned int id) return 0; } - if (cpu_is_omap1510() || cpu_is_omap16xx()) { + if (cpu_is_omap15xx() || cpu_is_omap16xx() || cpu_is_omap24xx()) { if (id > OMAP_MAX_MCBSP_COUNT) { printk(KERN_ERR "OMAP-McBSP: McBSP%d doesn't exist\n", id + 1); return -1; @@ -187,9 +195,10 @@ static int omap_mcbsp_check(unsigned int id) return -1; } +#ifdef CONFIG_ARCH_OMAP1 static void omap_mcbsp_dsp_request(void) { - if (cpu_is_omap1510() || cpu_is_omap16xx()) { + if (cpu_is_omap15xx() || cpu_is_omap16xx()) { clk_enable(mcbsp_dsp_ck); clk_enable(mcbsp_api_ck); @@ -207,12 +216,49 @@ static void omap_mcbsp_dsp_request(void) static void omap_mcbsp_dsp_free(void) { - if (cpu_is_omap1510() || cpu_is_omap16xx()) { + if (cpu_is_omap15xx() || cpu_is_omap16xx()) { clk_disable(mcbsp_dspxor_ck); clk_disable(mcbsp_dsp_ck); clk_disable(mcbsp_api_ck); } } +#endif + +#ifdef CONFIG_ARCH_OMAP2 +static void omap2_mcbsp2_mux_setup(void) +{ + omap_cfg_reg(Y15_24XX_MCBSP2_CLKX); + omap_cfg_reg(R14_24XX_MCBSP2_FSX); + omap_cfg_reg(W15_24XX_MCBSP2_DR); + omap_cfg_reg(V15_24XX_MCBSP2_DX); + omap_cfg_reg(V14_24XX_GPIO117); + omap_cfg_reg(W14_24XX_SYS_CLKOUT); +} +#endif + +/* + * We can choose between IRQ based or polled IO. + * This needs to be called before omap_mcbsp_request(). + */ +int omap_mcbsp_set_io_type(unsigned int id, omap_mcbsp_io_type_t io_type) +{ + if (omap_mcbsp_check(id) < 0) + return -EINVAL; + + spin_lock(&mcbsp[id].lock); + + if (!mcbsp[id].free) { + printk (KERN_ERR "OMAP-McBSP: McBSP%d is currently in use\n", id + 1); + spin_unlock(&mcbsp[id].lock); + return -EINVAL; + } + + mcbsp[id].io_type = io_type; + + spin_unlock(&mcbsp[id].lock); + + return 0; +} int omap_mcbsp_request(unsigned int id) { @@ -221,12 +267,26 @@ int omap_mcbsp_request(unsigned int id) if (omap_mcbsp_check(id) < 0) return -EINVAL; +#ifdef CONFIG_ARCH_OMAP1 /* * On 1510, 1610 and 1710, McBSP1 and McBSP3 * are DSP public peripherals. */ if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3) omap_mcbsp_dsp_request(); +#endif + +#ifdef CONFIG_ARCH_OMAP2 + if (cpu_is_omap24xx()) { + if (id == OMAP_MCBSP1) { + clk_enable(mcbsp1_ick); + clk_enable(mcbsp1_fck); + } else { + clk_enable(mcbsp2_ick); + clk_enable(mcbsp2_fck); + } + } +#endif spin_lock(&mcbsp[id].lock); if (!mcbsp[id].free) { @@ -238,30 +298,33 @@ int omap_mcbsp_request(unsigned int id) mcbsp[id].free = 0; spin_unlock(&mcbsp[id].lock); - /* We need to get IRQs here */ - err = request_irq(mcbsp[id].tx_irq, omap_mcbsp_tx_irq_handler, 0, - "McBSP", - (void *) (&mcbsp[id])); - if (err != 0) { - printk(KERN_ERR "OMAP-McBSP: Unable to request TX IRQ %d for McBSP%d\n", - mcbsp[id].tx_irq, mcbsp[id].id); - return err; + if (mcbsp[id].io_type == OMAP_MCBSP_IRQ_IO) { + /* We need to get IRQs here */ + err = request_irq(mcbsp[id].tx_irq, omap_mcbsp_tx_irq_handler, 0, + "McBSP", + (void *) (&mcbsp[id])); + if (err != 0) { + printk(KERN_ERR "OMAP-McBSP: Unable to request TX IRQ %d for McBSP%d\n", + mcbsp[id].tx_irq, mcbsp[id].id); + return err; + } + + init_completion(&(mcbsp[id].tx_irq_completion)); + + + err = request_irq(mcbsp[id].rx_irq, omap_mcbsp_rx_irq_handler, 0, + "McBSP", + (void *) (&mcbsp[id])); + if (err != 0) { + printk(KERN_ERR "OMAP-McBSP: Unable to request RX IRQ %d for McBSP%d\n", + mcbsp[id].rx_irq, mcbsp[id].id); + free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id])); + return err; + } + + init_completion(&(mcbsp[id].rx_irq_completion)); } - init_completion(&(mcbsp[id].tx_irq_completion)); - - - err = request_irq(mcbsp[id].rx_irq, omap_mcbsp_rx_irq_handler, 0, - "McBSP", - (void *) (&mcbsp[id])); - if (err != 0) { - printk(KERN_ERR "OMAP-McBSP: Unable to request RX IRQ %d for McBSP%d\n", - mcbsp[id].rx_irq, mcbsp[id].id); - free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id])); - return err; - } - - init_completion(&(mcbsp[id].rx_irq_completion)); return 0; } @@ -271,8 +334,24 @@ void omap_mcbsp_free(unsigned int id) if (omap_mcbsp_check(id) < 0) return; - if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3) - omap_mcbsp_dsp_free(); +#ifdef CONFIG_ARCH_OMAP1 + if (cpu_class_is_omap1()) { + if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3) + omap_mcbsp_dsp_free(); + } +#endif + +#ifdef CONFIG_ARCH_OMAP2 + if (cpu_is_omap24xx()) { + if (id == OMAP_MCBSP1) { + clk_disable(mcbsp1_ick); + clk_disable(mcbsp1_fck); + } else { + clk_disable(mcbsp2_ick); + clk_disable(mcbsp2_fck); + } + } +#endif spin_lock(&mcbsp[id].lock); if (mcbsp[id].free) { @@ -284,9 +363,11 @@ void omap_mcbsp_free(unsigned int id) mcbsp[id].free = 1; spin_unlock(&mcbsp[id].lock); - /* Free IRQs */ - free_irq(mcbsp[id].rx_irq, (void *) (&mcbsp[id])); - free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id])); + if (mcbsp[id].io_type == OMAP_MCBSP_IRQ_IO) { + /* Free IRQs */ + free_irq(mcbsp[id].rx_irq, (void *) (&mcbsp[id])); + free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id])); + } } /* @@ -461,6 +542,115 @@ u32 omap_mcbsp_recv_word(unsigned int id) } +int omap_mcbsp_spi_master_xmit_word_poll(unsigned int id, u32 word) +{ + u32 io_base = mcbsp[id].io_base; + omap_mcbsp_word_length tx_word_length = mcbsp[id].tx_word_length; + omap_mcbsp_word_length rx_word_length = mcbsp[id].rx_word_length; + u16 spcr2, spcr1, attempts = 0, word_lsb, word_msb = 0; + + if (tx_word_length != rx_word_length) + return -EINVAL; + + /* First we wait for the transmitter to be ready */ + spcr2 = OMAP_MCBSP_READ(io_base, SPCR2); + while (!(spcr2 & XRDY)) { + spcr2 = OMAP_MCBSP_READ(io_base, SPCR2); + if (attempts++ > 1000) { + /* We must reset the transmitter */ + OMAP_MCBSP_WRITE(io_base, SPCR2, spcr2 & (~XRST)); + udelay(10); + OMAP_MCBSP_WRITE(io_base, SPCR2, spcr2 | XRST); + udelay(10); + printk("McBSP transmitter not ready\n"); + return -EAGAIN; + } + } + + /* Now we can push the data */ + if (tx_word_length > OMAP_MCBSP_WORD_16) + OMAP_MCBSP_WRITE(io_base, DXR2, word >> 16); + OMAP_MCBSP_WRITE(io_base, DXR1, word & 0xffff); + + /* We wait for the receiver to be ready */ + spcr1 = OMAP_MCBSP_READ(io_base, SPCR1); + while (!(spcr1 & RRDY)) { + spcr1 = OMAP_MCBSP_READ(io_base, SPCR1); + if (attempts++ > 1000) { + /* We must reset the receiver */ + OMAP_MCBSP_WRITE(io_base, SPCR1, spcr1 & (~RRST)); + udelay(10); + OMAP_MCBSP_WRITE(io_base, SPCR1, spcr1 | RRST); + udelay(10); + printk("McBSP receiver not ready\n"); + return -EAGAIN; + } + } + + /* Receiver is ready, let's read the dummy data */ + if (rx_word_length > OMAP_MCBSP_WORD_16) + word_msb = OMAP_MCBSP_READ(io_base, DRR2); + word_lsb = OMAP_MCBSP_READ(io_base, DRR1); + + return 0; +} + +int omap_mcbsp_spi_master_recv_word_poll(unsigned int id, u32 * word) +{ + u32 io_base = mcbsp[id].io_base, clock_word = 0; + omap_mcbsp_word_length tx_word_length = mcbsp[id].tx_word_length; + omap_mcbsp_word_length rx_word_length = mcbsp[id].rx_word_length; + u16 spcr2, spcr1, attempts = 0, word_lsb, word_msb = 0; + + if (tx_word_length != rx_word_length) + return -EINVAL; + + /* First we wait for the transmitter to be ready */ + spcr2 = OMAP_MCBSP_READ(io_base, SPCR2); + while (!(spcr2 & XRDY)) { + spcr2 = OMAP_MCBSP_READ(io_base, SPCR2); + if (attempts++ > 1000) { + /* We must reset the transmitter */ + OMAP_MCBSP_WRITE(io_base, SPCR2, spcr2 & (~XRST)); + udelay(10); + OMAP_MCBSP_WRITE(io_base, SPCR2, spcr2 | XRST); + udelay(10); + printk("McBSP transmitter not ready\n"); + return -EAGAIN; + } + } + + /* We first need to enable the bus clock */ + if (tx_word_length > OMAP_MCBSP_WORD_16) + OMAP_MCBSP_WRITE(io_base, DXR2, clock_word >> 16); + OMAP_MCBSP_WRITE(io_base, DXR1, clock_word & 0xffff); + + /* We wait for the receiver to be ready */ + spcr1 = OMAP_MCBSP_READ(io_base, SPCR1); + while (!(spcr1 & RRDY)) { + spcr1 = OMAP_MCBSP_READ(io_base, SPCR1); + if (attempts++ > 1000) { + /* We must reset the receiver */ + OMAP_MCBSP_WRITE(io_base, SPCR1, spcr1 & (~RRST)); + udelay(10); + OMAP_MCBSP_WRITE(io_base, SPCR1, spcr1 | RRST); + udelay(10); + printk("McBSP receiver not ready\n"); + return -EAGAIN; + } + } + + /* Receiver is ready, there is something for us */ + if (rx_word_length > OMAP_MCBSP_WORD_16) + word_msb = OMAP_MCBSP_READ(io_base, DRR2); + word_lsb = OMAP_MCBSP_READ(io_base, DRR1); + + word[0] = (word_lsb | (word_msb << 16)); + + return 0; +} + + /* * Simple DMA based buffer rx/tx routines. * Nothing fancy, just a single buffer tx/rx through DMA. @@ -471,6 +661,9 @@ u32 omap_mcbsp_recv_word(unsigned int id) int omap_mcbsp_xmit_buffer(unsigned int id, dma_addr_t buffer, unsigned int length) { int dma_tx_ch; + int src_port = 0; + int dest_port = 0; + int sync_dev = 0; if (omap_mcbsp_check(id) < 0) return -EINVAL; @@ -487,20 +680,27 @@ int omap_mcbsp_xmit_buffer(unsigned int id, dma_addr_t buffer, unsigned int leng init_completion(&(mcbsp[id].tx_dma_completion)); + if (cpu_class_is_omap1()) { + src_port = OMAP_DMA_PORT_TIPB; + dest_port = OMAP_DMA_PORT_EMIFF; + } + if (cpu_is_omap24xx()) + sync_dev = mcbsp[id].dma_tx_sync; + omap_set_dma_transfer_params(mcbsp[id].dma_tx_lch, OMAP_DMA_DATA_TYPE_S16, length >> 1, 1, OMAP_DMA_SYNC_ELEMENT, - 0, 0); + sync_dev, 0); omap_set_dma_dest_params(mcbsp[id].dma_tx_lch, - OMAP_DMA_PORT_TIPB, + src_port, OMAP_DMA_AMODE_CONSTANT, mcbsp[id].io_base + OMAP_MCBSP_REG_DXR1, 0, 0); omap_set_dma_src_params(mcbsp[id].dma_tx_lch, - OMAP_DMA_PORT_EMIFF, + dest_port, OMAP_DMA_AMODE_POST_INC, buffer, 0, 0); @@ -514,6 +714,9 @@ int omap_mcbsp_xmit_buffer(unsigned int id, dma_addr_t buffer, unsigned int leng int omap_mcbsp_recv_buffer(unsigned int id, dma_addr_t buffer, unsigned int length) { int dma_rx_ch; + int src_port = 0; + int dest_port = 0; + int sync_dev = 0; if (omap_mcbsp_check(id) < 0) return -EINVAL; @@ -530,20 +733,27 @@ int omap_mcbsp_recv_buffer(unsigned int id, dma_addr_t buffer, unsigned int leng init_completion(&(mcbsp[id].rx_dma_completion)); + if (cpu_class_is_omap1()) { + src_port = OMAP_DMA_PORT_TIPB; + dest_port = OMAP_DMA_PORT_EMIFF; + } + if (cpu_is_omap24xx()) + sync_dev = mcbsp[id].dma_rx_sync; + omap_set_dma_transfer_params(mcbsp[id].dma_rx_lch, OMAP_DMA_DATA_TYPE_S16, length >> 1, 1, OMAP_DMA_SYNC_ELEMENT, - 0, 0); + sync_dev, 0); omap_set_dma_src_params(mcbsp[id].dma_rx_lch, - OMAP_DMA_PORT_TIPB, + src_port, OMAP_DMA_AMODE_CONSTANT, mcbsp[id].io_base + OMAP_MCBSP_REG_DRR1, 0, 0); omap_set_dma_dest_params(mcbsp[id].dma_rx_lch, - OMAP_DMA_PORT_EMIFF, + dest_port, OMAP_DMA_AMODE_POST_INC, buffer, 0, 0); @@ -688,6 +898,23 @@ static const struct omap_mcbsp_info mcbsp_1610[] = { }; #endif +#if defined(CONFIG_ARCH_OMAP24XX) +static const struct omap_mcbsp_info mcbsp_24xx[] = { + [0] = { .virt_base = IO_ADDRESS(OMAP24XX_MCBSP1_BASE), + .dma_rx_sync = OMAP24XX_DMA_MCBSP1_RX, + .dma_tx_sync = OMAP24XX_DMA_MCBSP1_TX, + .rx_irq = INT_24XX_MCBSP1_IRQ_RX, + .tx_irq = INT_24XX_MCBSP1_IRQ_TX, + }, + [1] = { .virt_base = IO_ADDRESS(OMAP24XX_MCBSP2_BASE), + .dma_rx_sync = OMAP24XX_DMA_MCBSP2_RX, + .dma_tx_sync = OMAP24XX_DMA_MCBSP2_TX, + .rx_irq = INT_24XX_MCBSP2_IRQ_RX, + .tx_irq = INT_24XX_MCBSP2_IRQ_TX, + }, +}; +#endif + static int __init omap_mcbsp_init(void) { int mcbsp_count = 0, i; @@ -695,6 +922,7 @@ static int __init omap_mcbsp_init(void) printk("Initializing OMAP McBSP system\n"); +#ifdef CONFIG_ARCH_OMAP1 mcbsp_dsp_ck = clk_get(0, "dsp_ck"); if (IS_ERR(mcbsp_dsp_ck)) { printk(KERN_ERR "mcbsp: could not acquire dsp_ck handle.\n"); @@ -710,6 +938,29 @@ static int __init omap_mcbsp_init(void) printk(KERN_ERR "mcbsp: could not acquire dspxor_ck handle.\n"); return PTR_ERR(mcbsp_dspxor_ck); } +#endif +#ifdef CONFIG_ARCH_OMAP2 + mcbsp1_ick = clk_get(0, "mcbsp1_ick"); + if (IS_ERR(mcbsp1_ick)) { + printk(KERN_ERR "mcbsp: could not acquire mcbsp1_ick handle.\n"); + return PTR_ERR(mcbsp1_ick); + } + mcbsp1_fck = clk_get(0, "mcbsp1_fck"); + if (IS_ERR(mcbsp1_fck)) { + printk(KERN_ERR "mcbsp: could not acquire mcbsp1_fck handle.\n"); + return PTR_ERR(mcbsp1_fck); + } + mcbsp2_ick = clk_get(0, "mcbsp2_ick"); + if (IS_ERR(mcbsp2_ick)) { + printk(KERN_ERR "mcbsp: could not acquire mcbsp2_ick handle.\n"); + return PTR_ERR(mcbsp2_ick); + } + mcbsp2_fck = clk_get(0, "mcbsp2_fck"); + if (IS_ERR(mcbsp2_fck)) { + printk(KERN_ERR "mcbsp: could not acquire mcbsp2_fck handle.\n"); + return PTR_ERR(mcbsp2_fck); + } +#endif #ifdef CONFIG_ARCH_OMAP730 if (cpu_is_omap730()) { @@ -718,7 +969,7 @@ static int __init omap_mcbsp_init(void) } #endif #ifdef CONFIG_ARCH_OMAP15XX - if (cpu_is_omap1510()) { + if (cpu_is_omap15xx()) { mcbsp_info = mcbsp_1510; mcbsp_count = ARRAY_SIZE(mcbsp_1510); } @@ -728,6 +979,19 @@ static int __init omap_mcbsp_init(void) mcbsp_info = mcbsp_1610; mcbsp_count = ARRAY_SIZE(mcbsp_1610); } +#endif +#if defined(CONFIG_ARCH_OMAP24XX) + if (cpu_is_omap24xx()) { + mcbsp_info = mcbsp_24xx; + mcbsp_count = ARRAY_SIZE(mcbsp_24xx); + + /* REVISIT: where's the right place? */ + omap2_mcbsp2_mux_setup(); + sys_ck = clk_get(0, "sys_ck"); + sys_clkout = clk_get(0, "sys_clkout"); + clk_set_parent(sys_clkout, sys_ck); + clk_enable(sys_clkout); + } #endif for (i = 0; i < OMAP_MAX_MCBSP_COUNT ; i++) { if (i >= mcbsp_count) { @@ -741,6 +1005,7 @@ static int __init omap_mcbsp_init(void) mcbsp[i].dma_rx_lch = -1; mcbsp[i].io_base = mcbsp_info[i].virt_base; + mcbsp[i].io_type = OMAP_MCBSP_IRQ_IO; /* Default I/O is IRQ based */ mcbsp[i].tx_irq = mcbsp_info[i].tx_irq; mcbsp[i].rx_irq = mcbsp_info[i].rx_irq; mcbsp[i].dma_rx_sync = mcbsp_info[i].dma_rx_sync; @@ -751,11 +1016,11 @@ static int __init omap_mcbsp_init(void) return 0; } - arch_initcall(omap_mcbsp_init); EXPORT_SYMBOL(omap_mcbsp_config); EXPORT_SYMBOL(omap_mcbsp_request); +EXPORT_SYMBOL(omap_mcbsp_set_io_type); EXPORT_SYMBOL(omap_mcbsp_free); EXPORT_SYMBOL(omap_mcbsp_start); EXPORT_SYMBOL(omap_mcbsp_stop); @@ -763,4 +1028,6 @@ EXPORT_SYMBOL(omap_mcbsp_xmit_word); EXPORT_SYMBOL(omap_mcbsp_recv_word); EXPORT_SYMBOL(omap_mcbsp_xmit_buffer); EXPORT_SYMBOL(omap_mcbsp_recv_buffer); +EXPORT_SYMBOL(omap_mcbsp_spi_master_xmit_word_poll); +EXPORT_SYMBOL(omap_mcbsp_spi_master_recv_word_poll); EXPORT_SYMBOL(omap_mcbsp_set_spi_mode); diff --git a/arch/arm/plat-omap/ocpi.c b/arch/arm/plat-omap/ocpi.c index 5cc6775c789c..37792d43738b 100644 --- a/arch/arm/plat-omap/ocpi.c +++ b/arch/arm/plat-omap/ocpi.c @@ -62,9 +62,6 @@ int ocpi_enable(void) if (!cpu_is_omap16xx()) return -ENODEV; - /* Make sure there's clock for OCPI */ - clk_enable(ocpi_ck); - /* Enable access for OHCI in OCPI */ val = omap_readl(OCPI_PROT); val &= ~0xff; diff --git a/arch/arm/plat-omap/pm.c b/arch/arm/plat-omap/pm.c index 093efd786f21..1a24e2c10714 100644 --- a/arch/arm/plat-omap/pm.c +++ b/arch/arm/plat-omap/pm.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include diff --git a/arch/arm/plat-omap/sram.c b/arch/arm/plat-omap/sram.c index ee82763b02b8..b7bf09b1b412 100644 --- a/arch/arm/plat-omap/sram.c +++ b/arch/arm/plat-omap/sram.c @@ -16,24 +16,94 @@ #include #include -#include #include #include #include +#include + #include +#include #define OMAP1_SRAM_PA 0x20000000 #define OMAP1_SRAM_VA 0xd0000000 #define OMAP2_SRAM_PA 0x40200000 +#define OMAP2_SRAM_PUB_PA 0x4020f800 #define OMAP2_SRAM_VA 0xd0000000 +#define OMAP2_SRAM_PUB_VA 0xd0000800 +#if defined(CONFIG_ARCH_OMAP24XX) +#define SRAM_BOOTLOADER_SZ 0x00 +#else #define SRAM_BOOTLOADER_SZ 0x80 +#endif + +#define VA_REQINFOPERM0 IO_ADDRESS(0x68005048) +#define VA_READPERM0 IO_ADDRESS(0x68005050) +#define VA_WRITEPERM0 IO_ADDRESS(0x68005058) +#define VA_CONTROL_STAT IO_ADDRESS(0x480002F8) +#define GP_DEVICE 0x300 +#define TYPE_MASK 0x700 + +#define ROUND_DOWN(value,boundary) ((value) & (~((boundary)-1))) static unsigned long omap_sram_base; static unsigned long omap_sram_size; static unsigned long omap_sram_ceil; +unsigned long omap_fb_sram_start; +unsigned long omap_fb_sram_size; + +/* Depending on the target RAMFS firewall setup, the public usable amount of + * SRAM varies. The default accessable size for all device types is 2k. A GP + * device allows ARM11 but not other initators for full size. This + * functionality seems ok until some nice security API happens. + */ +static int is_sram_locked(void) +{ + int type = 0; + + if (cpu_is_omap242x()) + type = __raw_readl(VA_CONTROL_STAT) & TYPE_MASK; + + if (type == GP_DEVICE) { + /* RAMFW: R/W access to all initators for all qualifier sets */ + if (cpu_is_omap242x()) { + __raw_writel(0xFF, VA_REQINFOPERM0); /* all q-vects */ + __raw_writel(0xCFDE, VA_READPERM0); /* all i-read */ + __raw_writel(0xCFDE, VA_WRITEPERM0); /* all i-write */ + } + return 0; + } else + return 1; /* assume locked with no PPA or security driver */ +} + +void get_fb_sram_conf(unsigned long start_avail, unsigned size_avail, + unsigned long *start, unsigned long *size) +{ + const struct omap_fbmem_config *fbmem_conf; + + fbmem_conf = omap_get_config(OMAP_TAG_FBMEM, struct omap_fbmem_config); + if (fbmem_conf != NULL) { + *start = fbmem_conf->fb_sram_start; + *size = fbmem_conf->fb_sram_size; + } else { + *size = 0; + *start = 0; + } + + if (*size && ( + *start < start_avail || + *start + *size > start_avail + size_avail)) { + printk(KERN_ERR "invalid FB SRAM configuration\n"); + *start = start_avail; + *size = size_avail; + } + + if (*size) + pr_info("Reserving %lu bytes SRAM for frame buffer\n", *size); +} + /* * The amount of SRAM depends on the core type. * Note that we cannot try to test for SRAM here because writes @@ -42,26 +112,45 @@ static unsigned long omap_sram_ceil; */ void __init omap_detect_sram(void) { - if (!cpu_is_omap24xx()) + unsigned long sram_start; + + if (cpu_is_omap24xx()) { + if (is_sram_locked()) { + omap_sram_base = OMAP2_SRAM_PUB_VA; + sram_start = OMAP2_SRAM_PUB_PA; + omap_sram_size = 0x800; /* 2K */ + } else { + omap_sram_base = OMAP2_SRAM_VA; + sram_start = OMAP2_SRAM_PA; + if (cpu_is_omap242x()) + omap_sram_size = 0xa0000; /* 640K */ + else if (cpu_is_omap243x()) + omap_sram_size = 0x10000; /* 64K */ + } + } else { omap_sram_base = OMAP1_SRAM_VA; - else - omap_sram_base = OMAP2_SRAM_VA; + sram_start = OMAP1_SRAM_PA; - if (cpu_is_omap730()) - omap_sram_size = 0x32000; /* 200K */ - else if (cpu_is_omap15xx()) - omap_sram_size = 0x30000; /* 192K */ - else if (cpu_is_omap1610() || cpu_is_omap1621() || cpu_is_omap1710()) - omap_sram_size = 0x4000; /* 16K */ - else if (cpu_is_omap1611()) - omap_sram_size = 0x3e800; /* 250K */ - else if (cpu_is_omap2420()) - omap_sram_size = 0xa0014; /* 640K */ - else { - printk(KERN_ERR "Could not detect SRAM size\n"); - omap_sram_size = 0x4000; + if (cpu_is_omap730()) + omap_sram_size = 0x32000; /* 200K */ + else if (cpu_is_omap15xx()) + omap_sram_size = 0x30000; /* 192K */ + else if (cpu_is_omap1610() || cpu_is_omap1621() || + cpu_is_omap1710()) + omap_sram_size = 0x4000; /* 16K */ + else if (cpu_is_omap1611()) + omap_sram_size = 0x3e800; /* 250K */ + else { + printk(KERN_ERR "Could not detect SRAM size\n"); + omap_sram_size = 0x4000; + } } - + get_fb_sram_conf(sram_start + SRAM_BOOTLOADER_SZ, + omap_sram_size - SRAM_BOOTLOADER_SZ, + &omap_fb_sram_start, &omap_fb_sram_size); + if (omap_fb_sram_size) + omap_sram_size -= sram_start + omap_sram_size - + omap_fb_sram_start; omap_sram_ceil = omap_sram_base + omap_sram_size; } @@ -80,12 +169,20 @@ static struct map_desc omap_sram_io_desc[] __initdata = { */ void __init omap_map_sram(void) { + unsigned long base; + if (omap_sram_size == 0) return; if (cpu_is_omap24xx()) { omap_sram_io_desc[0].virtual = OMAP2_SRAM_VA; - omap_sram_io_desc[0].pfn = __phys_to_pfn(OMAP2_SRAM_PA); + + if (is_sram_locked()) + base = OMAP2_SRAM_PUB_PA; + else + base = OMAP2_SRAM_PA; + base = ROUND_DOWN(base, PAGE_SIZE); + omap_sram_io_desc[0].pfn = __phys_to_pfn(base); } omap_sram_io_desc[0].length = (omap_sram_size + PAGE_SIZE-1)/PAGE_SIZE; @@ -93,7 +190,8 @@ void __init omap_map_sram(void) iotable_init(omap_sram_io_desc, ARRAY_SIZE(omap_sram_io_desc)); printk(KERN_INFO "SRAM: Mapped pa 0x%08lx to va 0x%08lx size: 0x%lx\n", - omap_sram_io_desc[0].pfn, omap_sram_io_desc[0].virtual, + __pfn_to_phys(omap_sram_io_desc[0].pfn), + omap_sram_io_desc[0].virtual, omap_sram_io_desc[0].length); /* @@ -118,8 +216,9 @@ void * omap_sram_push(void * start, unsigned long size) printk(KERN_ERR "Not enough space in SRAM\n"); return NULL; } + omap_sram_ceil -= size; - omap_sram_ceil &= ~0x3; + omap_sram_ceil = ROUND_DOWN(omap_sram_ceil, sizeof(void *)); memcpy((void *)omap_sram_ceil, start, size); return (void *)omap_sram_ceil; diff --git a/arch/arm/plat-omap/timer32k.c b/arch/arm/plat-omap/timer32k.c new file mode 100644 index 000000000000..b2a943bf11ef --- /dev/null +++ b/arch/arm/plat-omap/timer32k.c @@ -0,0 +1,325 @@ +/* + * linux/arch/arm/plat-omap/timer32k.c + * + * OMAP 32K Timer + * + * Copyright (C) 2004 - 2005 Nokia Corporation + * Partial timer rewrite and additional dynamic tick timer support by + * Tony Lindgen and + * Tuukka Tikkanen + * + * MPU timer code based on the older MPU timer code for OMAP + * Copyright (C) 2000 RidgeRun, Inc. + * Author: Greg Lonnon + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +struct sys_timer omap_timer; + +/* + * --------------------------------------------------------------------------- + * 32KHz OS timer + * + * This currently works only on 16xx, as 1510 does not have the continuous + * 32KHz synchronous timer. The 32KHz synchronous timer is used to keep track + * of time in addition to the 32KHz OS timer. Using only the 32KHz OS timer + * on 1510 would be possible, but the timer would not be as accurate as + * with the 32KHz synchronized timer. + * --------------------------------------------------------------------------- + */ + +#if defined(CONFIG_ARCH_OMAP16XX) +#define TIMER_32K_SYNCHRONIZED 0xfffbc410 +#elif defined(CONFIG_ARCH_OMAP24XX) +#define TIMER_32K_SYNCHRONIZED 0x48004010 +#else +#error OMAP 32KHz timer does not currently work on 15XX! +#endif + +/* 16xx specific defines */ +#define OMAP1_32K_TIMER_BASE 0xfffb9000 +#define OMAP1_32K_TIMER_CR 0x08 +#define OMAP1_32K_TIMER_TVR 0x00 +#define OMAP1_32K_TIMER_TCR 0x04 + +/* 24xx specific defines */ +#define OMAP2_GP_TIMER_BASE 0x48028000 +#define CM_CLKSEL_WKUP 0x48008440 +#define GP_TIMER_TIDR 0x00 +#define GP_TIMER_TISR 0x18 +#define GP_TIMER_TIER 0x1c +#define GP_TIMER_TCLR 0x24 +#define GP_TIMER_TCRR 0x28 +#define GP_TIMER_TLDR 0x2c +#define GP_TIMER_TTGR 0x30 +#define GP_TIMER_TSICR 0x40 + +#define OMAP_32K_TICKS_PER_HZ (32768 / HZ) + +/* + * TRM says 1 / HZ = ( TVR + 1) / 32768, so TRV = (32768 / HZ) - 1 + * so with HZ = 128, TVR = 255. + */ +#define OMAP_32K_TIMER_TICK_PERIOD ((32768 / HZ) - 1) + +#define JIFFIES_TO_HW_TICKS(nr_jiffies, clock_rate) \ + (((nr_jiffies) * (clock_rate)) / HZ) + +static inline void omap_32k_timer_write(int val, int reg) +{ + if (cpu_class_is_omap1()) + omap_writew(val, OMAP1_32K_TIMER_BASE + reg); + + if (cpu_is_omap24xx()) + omap_writel(val, OMAP2_GP_TIMER_BASE + reg); +} + +static inline unsigned long omap_32k_timer_read(int reg) +{ + if (cpu_class_is_omap1()) + return omap_readl(OMAP1_32K_TIMER_BASE + reg) & 0xffffff; + + if (cpu_is_omap24xx()) + return omap_readl(OMAP2_GP_TIMER_BASE + reg); +} + +/* + * The 32KHz synchronized timer is an additional timer on 16xx. + * It is always running. + */ +static inline unsigned long omap_32k_sync_timer_read(void) +{ + return omap_readl(TIMER_32K_SYNCHRONIZED); +} + +static inline void omap_32k_timer_start(unsigned long load_val) +{ + if (cpu_class_is_omap1()) { + omap_32k_timer_write(load_val, OMAP1_32K_TIMER_TVR); + omap_32k_timer_write(0x0f, OMAP1_32K_TIMER_CR); + } + + if (cpu_is_omap24xx()) { + omap_32k_timer_write(0xffffffff - load_val, GP_TIMER_TCRR); + omap_32k_timer_write((1 << 1), GP_TIMER_TIER); + omap_32k_timer_write((1 << 1) | 1, GP_TIMER_TCLR); + } +} + +static inline void omap_32k_timer_stop(void) +{ + if (cpu_class_is_omap1()) + omap_32k_timer_write(0x0, OMAP1_32K_TIMER_CR); + + if (cpu_is_omap24xx()) + omap_32k_timer_write(0x0, GP_TIMER_TCLR); +} + +/* + * Rounds down to nearest usec. Note that this will overflow for larger values. + */ +static inline unsigned long omap_32k_ticks_to_usecs(unsigned long ticks_32k) +{ + return (ticks_32k * 5*5*5*5*5*5) >> 9; +} + +/* + * Rounds down to nearest nsec. + */ +static inline unsigned long long +omap_32k_ticks_to_nsecs(unsigned long ticks_32k) +{ + return (unsigned long long) ticks_32k * 1000 * 5*5*5*5*5*5 >> 9; +} + +static unsigned long omap_32k_last_tick = 0; + +/* + * Returns elapsed usecs since last 32k timer interrupt + */ +static unsigned long omap_32k_timer_gettimeoffset(void) +{ + unsigned long now = omap_32k_sync_timer_read(); + return omap_32k_ticks_to_usecs(now - omap_32k_last_tick); +} + +/* + * Returns current time from boot in nsecs. It's OK for this to wrap + * around for now, as it's just a relative time stamp. + */ +unsigned long long sched_clock(void) +{ + return omap_32k_ticks_to_nsecs(omap_32k_sync_timer_read()); +} + +/* + * Timer interrupt for 32KHz timer. When dynamic tick is enabled, this + * function is also called from other interrupts to remove latency + * issues with dynamic tick. In the dynamic tick case, we need to lock + * with irqsave. + */ +static irqreturn_t omap_32k_timer_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + unsigned long flags; + unsigned long now; + + write_seqlock_irqsave(&xtime_lock, flags); + + if (cpu_is_omap24xx()) { + u32 status = omap_32k_timer_read(GP_TIMER_TISR); + omap_32k_timer_write(status, GP_TIMER_TISR); + } + + now = omap_32k_sync_timer_read(); + + while (now - omap_32k_last_tick >= OMAP_32K_TICKS_PER_HZ) { + omap_32k_last_tick += OMAP_32K_TICKS_PER_HZ; + timer_tick(regs); + } + + /* Restart timer so we don't drift off due to modulo or dynamic tick. + * By default we program the next timer to be continuous to avoid + * latencies during high system load. During dynamic tick operation the + * continuous timer can be overridden from pm_idle to be longer. + */ + omap_32k_timer_start(omap_32k_last_tick + OMAP_32K_TICKS_PER_HZ - now); + write_sequnlock_irqrestore(&xtime_lock, flags); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_NO_IDLE_HZ +/* + * Programs the next timer interrupt needed. Called when dynamic tick is + * enabled, and to reprogram the ticks to skip from pm_idle. Note that + * we can keep the timer continuous, and don't need to set it to run in + * one-shot mode. This is because the timer will get reprogrammed again + * after next interrupt. + */ +void omap_32k_timer_reprogram(unsigned long next_tick) +{ + omap_32k_timer_start(JIFFIES_TO_HW_TICKS(next_tick, 32768) + 1); +} + +static struct irqaction omap_32k_timer_irq; +extern struct timer_update_handler timer_update; + +static int omap_32k_timer_enable_dyn_tick(void) +{ + /* No need to reprogram timer, just use the next interrupt */ + return 0; +} + +static int omap_32k_timer_disable_dyn_tick(void) +{ + omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD); + return 0; +} + +static struct dyn_tick_timer omap_dyn_tick_timer = { + .enable = omap_32k_timer_enable_dyn_tick, + .disable = omap_32k_timer_disable_dyn_tick, + .reprogram = omap_32k_timer_reprogram, + .handler = omap_32k_timer_interrupt, +}; +#endif /* CONFIG_NO_IDLE_HZ */ + +static struct irqaction omap_32k_timer_irq = { + .name = "32KHz timer", + .flags = SA_INTERRUPT | SA_TIMER, + .handler = omap_32k_timer_interrupt, +}; + +static struct clk * gpt1_ick; +static struct clk * gpt1_fck; + +static __init void omap_init_32k_timer(void) +{ +#ifdef CONFIG_NO_IDLE_HZ + omap_timer.dyn_tick = &omap_dyn_tick_timer; +#endif + + if (cpu_class_is_omap1()) + setup_irq(INT_OS_TIMER, &omap_32k_timer_irq); + if (cpu_is_omap24xx()) + setup_irq(37, &omap_32k_timer_irq); + omap_timer.offset = omap_32k_timer_gettimeoffset; + omap_32k_last_tick = omap_32k_sync_timer_read(); + + /* REVISIT: Check 24xx TIOCP_CFG settings after idle works */ + if (cpu_is_omap24xx()) { + omap_32k_timer_write(0, GP_TIMER_TCLR); + omap_writel(0, CM_CLKSEL_WKUP); /* 32KHz clock source */ + + gpt1_ick = clk_get(NULL, "gpt1_ick"); + if (IS_ERR(gpt1_ick)) + printk(KERN_ERR "Could not get gpt1_ick\n"); + else + clk_enable(gpt1_ick); + + gpt1_fck = clk_get(NULL, "gpt1_fck"); + if (IS_ERR(gpt1_fck)) + printk(KERN_ERR "Could not get gpt1_fck\n"); + else + clk_enable(gpt1_fck); + + mdelay(100); /* Wait for clocks to stabilize */ + + omap_32k_timer_write(0x7, GP_TIMER_TISR); + } + + omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD); +} + +/* + * --------------------------------------------------------------------------- + * Timer initialization + * --------------------------------------------------------------------------- + */ +static void __init omap_timer_init(void) +{ + omap_init_32k_timer(); +} + +struct sys_timer omap_timer = { + .init = omap_timer_init, + .offset = NULL, /* Initialized later */ +}; diff --git a/arch/arm/vfp/vfpdouble.c b/arch/arm/vfp/vfpdouble.c index 9b367a65cb4d..febd115dba28 100644 --- a/arch/arm/vfp/vfpdouble.c +++ b/arch/arm/vfp/vfpdouble.c @@ -588,6 +588,7 @@ static u32 vfp_double_ftosi(int sd, int unused, int dm, u32 fpscr) struct vfp_double vdm; u32 d, exceptions = 0; int rmode = fpscr & FPSCR_RMODE_MASK; + int tm; vfp_double_unpack(&vdm, vfp_get_double(dm)); vfp_double_dump("VDM", &vdm); @@ -595,10 +596,14 @@ static u32 vfp_double_ftosi(int sd, int unused, int dm, u32 fpscr) /* * Do we have denormalised number? */ - if (vfp_double_type(&vdm) & VFP_DENORMAL) + tm = vfp_double_type(&vdm); + if (tm & VFP_DENORMAL) exceptions |= FPSCR_IDC; - if (vdm.exponent >= 1023 + 32) { + if (tm & VFP_NAN) { + d = 0; + exceptions |= FPSCR_IOC; + } else if (vdm.exponent >= 1023 + 32) { d = 0x7fffffff; if (vdm.sign) d = ~d; @@ -1122,9 +1127,9 @@ u32 vfp_double_cpdo(u32 inst, u32 fpscr) { u32 op = inst & FOP_MASK; u32 exceptions = 0; - unsigned int dd = vfp_get_sd(inst); - unsigned int dn = vfp_get_sn(inst); - unsigned int dm = vfp_get_sm(inst); + unsigned int dd = vfp_get_dd(inst); + unsigned int dn = vfp_get_dn(inst); + unsigned int dm = vfp_get_dm(inst); unsigned int vecitr, veclen, vecstride; u32 (*fop)(int, int, s32, u32); @@ -1141,7 +1146,7 @@ u32 vfp_double_cpdo(u32 inst, u32 fpscr) pr_debug("VFP: vecstride=%u veclen=%u\n", vecstride, (veclen >> FPSCR_LENGTH_BIT) + 1); - fop = (op == FOP_EXT) ? fop_extfns[dn] : fop_fns[FOP_TO_IDX(op)]; + fop = (op == FOP_EXT) ? fop_extfns[FEXT_TO_IDX(inst)] : fop_fns[FOP_TO_IDX(op)]; if (!fop) goto invalid; @@ -1149,17 +1154,13 @@ u32 vfp_double_cpdo(u32 inst, u32 fpscr) u32 except; if (op == FOP_EXT) - pr_debug("VFP: itr%d (d%u.%u) = op[%u] (d%u.%u)\n", + pr_debug("VFP: itr%d (d%u) = op[%u] (d%u)\n", vecitr >> FPSCR_LENGTH_BIT, - dd >> 1, dd & 1, dn, - dm >> 1, dm & 1); + dd, dn, dm); else - pr_debug("VFP: itr%d (d%u.%u) = (d%u.%u) op[%u] (d%u.%u)\n", + pr_debug("VFP: itr%d (d%u) = (d%u) op[%u] (d%u)\n", vecitr >> FPSCR_LENGTH_BIT, - dd >> 1, dd & 1, - dn >> 1, dn & 1, - FOP_TO_IDX(op), - dm >> 1, dm & 1); + dd, dn, FOP_TO_IDX(op), dm); except = fop(dd, dn, dm, fpscr); pr_debug("VFP: itr%d: exceptions=%08x\n", diff --git a/arch/arm/vfp/vfphw.S b/arch/arm/vfp/vfphw.S index b7ed57e00cd4..a3f65b47aea9 100644 --- a/arch/arm/vfp/vfphw.S +++ b/arch/arm/vfp/vfphw.S @@ -189,11 +189,10 @@ vfp_put_float: .globl vfp_get_double vfp_get_double: - mov r0, r0, lsr #1 add pc, pc, r0, lsl #3 mov r0, r0 .irp dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 - mrrc p10, 1, r0, r1, c\dr @ fmrrd r0, r1, d\dr + mrrc p11, 1, r0, r1, c\dr @ fmrrd r0, r1, d\dr mov pc, lr .endr @@ -204,10 +203,9 @@ vfp_get_double: .globl vfp_put_double vfp_put_double: - mov r0, r0, lsr #1 add pc, pc, r0, lsl #3 mov r0, r0 .irp dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 - mcrr p10, 1, r1, r2, c\dr @ fmrrd r1, r2, d\dr + mcrr p11, 1, r1, r2, c\dr @ fmdrr r1, r2, d\dr mov pc, lr .endr diff --git a/arch/arm/vfp/vfpsingle.c b/arch/arm/vfp/vfpsingle.c index 14dd696ddeb1..4ac27f193934 100644 --- a/arch/arm/vfp/vfpsingle.c +++ b/arch/arm/vfp/vfpsingle.c @@ -632,6 +632,7 @@ static u32 vfp_single_ftosi(int sd, int unused, s32 m, u32 fpscr) struct vfp_single vsm; u32 d, exceptions = 0; int rmode = fpscr & FPSCR_RMODE_MASK; + int tm; vfp_single_unpack(&vsm, m); vfp_single_dump("VSM", &vsm); @@ -639,10 +640,14 @@ static u32 vfp_single_ftosi(int sd, int unused, s32 m, u32 fpscr) /* * Do we have a denormalised number? */ + tm = vfp_single_type(&vsm); if (vfp_single_type(&vsm) & VFP_DENORMAL) exceptions |= FPSCR_IDC; - if (vsm.exponent >= 127 + 32) { + if (tm & VFP_NAN) { + d = 0; + exceptions |= FPSCR_IOC; + } else if (vsm.exponent >= 127 + 32) { /* * m >= 2^31-2^7: invalid */ @@ -1188,7 +1193,7 @@ u32 vfp_single_cpdo(u32 inst, u32 fpscr) pr_debug("VFP: vecstride=%u veclen=%u\n", vecstride, (veclen >> FPSCR_LENGTH_BIT) + 1); - fop = (op == FOP_EXT) ? fop_extfns[sn] : fop_fns[FOP_TO_IDX(op)]; + fop = (op == FOP_EXT) ? fop_extfns[FEXT_TO_IDX(inst)] : fop_fns[FOP_TO_IDX(op)]; if (!fop) goto invalid; diff --git a/arch/arm26/kernel/armksyms.c b/arch/arm26/kernel/armksyms.c index 811a6376c624..9d66c27f2724 100644 --- a/arch/arm26/kernel/armksyms.c +++ b/arch/arm26/kernel/armksyms.c @@ -152,7 +152,6 @@ EXPORT_SYMBOL(strncmp); EXPORT_SYMBOL(strchr); EXPORT_SYMBOL(strlen); EXPORT_SYMBOL(strnlen); -EXPORT_SYMBOL(strpbrk); EXPORT_SYMBOL(strrchr); EXPORT_SYMBOL(strstr); EXPORT_SYMBOL(memset); @@ -212,8 +211,6 @@ EXPORT_SYMBOL(sys_open); EXPORT_SYMBOL(sys_exit); EXPORT_SYMBOL(sys_wait4); -EXPORT_SYMBOL(get_wchan); - #ifdef CONFIG_PREEMPT EXPORT_SYMBOL(kernel_flag); #endif diff --git a/arch/cris/kernel/crisksyms.c b/arch/cris/kernel/crisksyms.c index de39725da920..d57859053ce7 100644 --- a/arch/cris/kernel/crisksyms.c +++ b/arch/cris/kernel/crisksyms.c @@ -39,7 +39,6 @@ EXPORT_SYMBOL(loops_per_usec); /* String functions */ EXPORT_SYMBOL(memcmp); EXPORT_SYMBOL(memmove); -EXPORT_SYMBOL(strpbrk); EXPORT_SYMBOL(strstr); EXPORT_SYMBOL(strcpy); EXPORT_SYMBOL(strchr); diff --git a/arch/frv/kernel/entry.S b/arch/frv/kernel/entry.S index 1d21c8d34d8a..a9b59527a741 100644 --- a/arch/frv/kernel/entry.S +++ b/arch/frv/kernel/entry.S @@ -1170,12 +1170,6 @@ __syscall_badsys: # syscall vector table # ############################################################################### -#ifdef CONFIG_MMU -#define __MMU(X) X -#else -#define __MMU(X) sys_ni_syscall -#endif - .section .rodata ALIGN .globl sys_call_table @@ -1305,7 +1299,7 @@ sys_call_table: .long sys_newuname .long sys_ni_syscall /* old "cacheflush" */ .long sys_adjtimex - .long __MMU(sys_mprotect) /* 125 */ + .long sys_mprotect /* 125 */ .long sys_sigprocmask .long sys_ni_syscall /* old "create_module" */ .long sys_init_module @@ -1324,16 +1318,16 @@ sys_call_table: .long sys_getdents .long sys_select .long sys_flock - .long __MMU(sys_msync) + .long sys_msync .long sys_readv /* 145 */ .long sys_writev .long sys_getsid .long sys_fdatasync .long sys_sysctl - .long __MMU(sys_mlock) /* 150 */ - .long __MMU(sys_munlock) - .long __MMU(sys_mlockall) - .long __MMU(sys_munlockall) + .long sys_mlock /* 150 */ + .long sys_munlock + .long sys_mlockall + .long sys_munlockall .long sys_sched_setparam .long sys_sched_getparam /* 155 */ .long sys_sched_setscheduler @@ -1343,7 +1337,7 @@ sys_call_table: .long sys_sched_get_priority_min /* 160 */ .long sys_sched_rr_get_interval .long sys_nanosleep - .long __MMU(sys_mremap) + .long sys_mremap .long sys_setresuid16 .long sys_getresuid16 /* 165 */ .long sys_ni_syscall /* for vm86 */ @@ -1398,8 +1392,8 @@ sys_call_table: .long sys_setfsuid /* 215 */ .long sys_setfsgid .long sys_pivot_root - .long __MMU(sys_mincore) - .long __MMU(sys_madvise) + .long sys_mincore + .long sys_madvise .long sys_getdents64 /* 220 */ .long sys_fcntl64 .long sys_ni_syscall /* reserved for TUX */ @@ -1437,7 +1431,7 @@ sys_call_table: .long sys_epoll_create .long sys_epoll_ctl /* 255 */ .long sys_epoll_wait - .long __MMU(sys_remap_file_pages) + .long sys_remap_file_pages .long sys_set_tid_address .long sys_timer_create .long sys_timer_settime /* 260 */ diff --git a/arch/frv/kernel/frv_ksyms.c b/arch/frv/kernel/frv_ksyms.c index aa6b7d0a2109..0f273a7aca5a 100644 --- a/arch/frv/kernel/frv_ksyms.c +++ b/arch/frv/kernel/frv_ksyms.c @@ -27,7 +27,6 @@ EXPORT_SYMBOL(__ioremap); EXPORT_SYMBOL(iounmap); EXPORT_SYMBOL(strnlen); -EXPORT_SYMBOL(strpbrk); EXPORT_SYMBOL(strrchr); EXPORT_SYMBOL(strstr); EXPORT_SYMBOL(strchr); @@ -79,8 +78,6 @@ EXPORT_SYMBOL(memmove); EXPORT_SYMBOL(__outsl_ns); EXPORT_SYMBOL(__insl_ns); -EXPORT_SYMBOL(get_wchan); - #ifdef CONFIG_FRV_OUTOFLINE_ATOMIC_OPS EXPORT_SYMBOL(atomic_test_and_ANDNOT_mask); EXPORT_SYMBOL(atomic_test_and_OR_mask); diff --git a/arch/h8300/kernel/h8300_ksyms.c b/arch/h8300/kernel/h8300_ksyms.c index 69d6ad32d56c..f8d6dee84781 100644 --- a/arch/h8300/kernel/h8300_ksyms.c +++ b/arch/h8300/kernel/h8300_ksyms.c @@ -25,7 +25,6 @@ extern char h8300_debug_device[]; /* platform dependent support */ EXPORT_SYMBOL(strnlen); -EXPORT_SYMBOL(strpbrk); EXPORT_SYMBOL(strrchr); EXPORT_SYMBOL(strstr); EXPORT_SYMBOL(strchr); @@ -55,8 +54,6 @@ EXPORT_SYMBOL(memcmp); EXPORT_SYMBOL(memscan); EXPORT_SYMBOL(memmove); -EXPORT_SYMBOL(get_wchan); - /* * libgcc functions - functions that are used internally by the * compiler... (prototypes are not correct though, but that diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig index f17bd1d2707e..18ec9fe6deb6 100644 --- a/arch/i386/Kconfig +++ b/arch/i386/Kconfig @@ -53,6 +53,35 @@ source "init/Kconfig" menu "Processor type and features" +config SMP + bool "Symmetric multi-processing support" + ---help--- + This enables support for systems with more than one CPU. If you have + a system with only one CPU, like most personal computers, say N. If + you have a system with more than one CPU, say Y. + + If you say N here, the kernel will run on single and multiprocessor + machines, but will use only one CPU of a multiprocessor machine. If + you say Y here, the kernel will run on many, but not all, + singleprocessor machines. On a singleprocessor machine, the kernel + will run faster if you say N here. + + Note that if you say Y here and choose architecture "586" or + "Pentium" under "Processor family", the kernel will not work on 486 + architectures. Similarly, multiprocessor kernels for the "PPro" + architecture may not work on all Pentium based boards. + + People using multiprocessor machines who say Y here should also say + Y to "Enhanced Real Time Clock Support", below. The "Advanced Power + Management" code will be disabled if you say Y here. + + See also the , + , + and the SMP-HOWTO available at + . + + If you don't know what to do here, say N. + choice prompt "Subarchitecture Type" default X86_PC @@ -178,35 +207,6 @@ config HPET_EMULATE_RTC depends on HPET_TIMER && RTC=y default y -config SMP - bool "Symmetric multi-processing support" - ---help--- - This enables support for systems with more than one CPU. If you have - a system with only one CPU, like most personal computers, say N. If - you have a system with more than one CPU, say Y. - - If you say N here, the kernel will run on single and multiprocessor - machines, but will use only one CPU of a multiprocessor machine. If - you say Y here, the kernel will run on many, but not all, - singleprocessor machines. On a singleprocessor machine, the kernel - will run faster if you say N here. - - Note that if you say Y here and choose architecture "586" or - "Pentium" under "Processor family", the kernel will not work on 486 - architectures. Similarly, multiprocessor kernels for the "PPro" - architecture may not work on all Pentium based boards. - - People using multiprocessor machines who say Y here should also say - Y to "Enhanced Real Time Clock Support", below. The "Advanced Power - Management" code will be disabled if you say Y here. - - See also the , - , - and the SMP-HOWTO available at - . - - If you don't know what to do here, say N. - config NR_CPUS int "Maximum number of CPUs (2-255)" range 2 255 @@ -522,6 +522,12 @@ config NUMA comment "NUMA (Summit) requires SMP, 64GB highmem support, ACPI" depends on X86_SUMMIT && (!HIGHMEM64G || !ACPI) +config NODES_SHIFT + int + default "4" if X86_NUMAQ + default "3" + depends on NEED_MULTIPLE_NODES + config HAVE_ARCH_BOOTMEM_NODE bool depends on NUMA @@ -757,15 +763,6 @@ config HOTPLUG_CPU Say N. -config DOUBLEFAULT - default y - bool "Enable doublefault exception handler" if EMBEDDED - help - This option allows trapping of rare doublefault exceptions that - would otherwise cause a system to silently reboot. Disabling this - option saves about 4k and might cause you much additional grey - hair. - endmenu diff --git a/arch/i386/Kconfig.cpu b/arch/i386/Kconfig.cpu index 79603b3471f9..eb130482ba18 100644 --- a/arch/i386/Kconfig.cpu +++ b/arch/i386/Kconfig.cpu @@ -311,5 +311,5 @@ config X86_OOSTORE config X86_TSC bool - depends on (MWINCHIP3D || MWINCHIP2 || MCRUSOE || MEFFICEON || MCYRIXIII || MK7 || MK6 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || M586MMX || M586TSC || MK8 || MVIAC3_2 || MGEODEGX1) && !X86_NUMAQ + depends on (MWINCHIP3D || MWINCHIP2 || MCRUSOE || MEFFICEON || MCYRIXIII || MK7 || MK6 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || M586MMX || M586TSC || MK8 || MVIAC3_2 || MGEODEGX1 || MGEODE_LX) && !X86_NUMAQ default y diff --git a/arch/i386/boot/video.S b/arch/i386/boot/video.S index 0000a2674537..c9343c3a8082 100644 --- a/arch/i386/boot/video.S +++ b/arch/i386/boot/video.S @@ -97,6 +97,7 @@ #define PARAM_VESAPM_OFF 0x30 #define PARAM_LFB_PAGES 0x32 #define PARAM_VESA_ATTRIB 0x34 +#define PARAM_CAPABILITIES 0x36 /* Define DO_STORE according to CONFIG_VIDEO_RETAIN */ #ifdef CONFIG_VIDEO_RETAIN @@ -233,6 +234,10 @@ mopar_gr: movw 18(%di), %ax movl %eax, %fs:(PARAM_LFB_SIZE) +# store mode capabilities + movl 10(%di), %eax + movl %eax, %fs:(PARAM_CAPABILITIES) + # switching the DAC to 8-bit is for <= 8 bpp only movw %fs:(PARAM_LFB_DEPTH), %ax cmpw $8, %ax diff --git a/arch/i386/kernel/acpi/boot.c b/arch/i386/kernel/acpi/boot.c index 033066176b3e..030a0007a4e0 100644 --- a/arch/i386/kernel/acpi/boot.c +++ b/arch/i386/kernel/acpi/boot.c @@ -168,7 +168,7 @@ int __init acpi_parse_mcfg(unsigned long phys_addr, unsigned long size) unsigned long i; int config_size; - if (!phys_addr || !size) + if (!phys_addr || !size || !cpu_has_apic) return -EINVAL; mcfg = (struct acpi_table_mcfg *)__acpi_map_table(phys_addr, size); @@ -215,7 +215,7 @@ static int __init acpi_parse_madt(unsigned long phys_addr, unsigned long size) { struct acpi_table_madt *madt = NULL; - if (!phys_addr || !size) + if (!phys_addr || !size || !cpu_has_apic) return -EINVAL; madt = (struct acpi_table_madt *)__acpi_map_table(phys_addr, size); @@ -693,6 +693,9 @@ static int __init acpi_parse_madt_lapic_entries(void) { int count; + if (!cpu_has_apic) + return -ENODEV; + /* * Note that the LAPIC address is obtained from the MADT (32-bit value) * and (optionally) overriden by a LAPIC_ADDR_OVR entry (64-bit value). @@ -751,6 +754,9 @@ static int __init acpi_parse_madt_ioapic_entries(void) return -ENODEV; } + if (!cpu_has_apic) + return -ENODEV; + /* * if "noapic" boot option, don't look for IO-APICs */ diff --git a/arch/i386/kernel/apic.c b/arch/i386/kernel/apic.c index eb5279d23b7f..254cee9f0b7b 100644 --- a/arch/i386/kernel/apic.c +++ b/arch/i386/kernel/apic.c @@ -62,6 +62,18 @@ int apic_verbosity; static void apic_pm_activate(void); +int modern_apic(void) +{ + unsigned int lvr, version; + /* AMD systems use old APIC versions, so check the CPU */ + if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD && + boot_cpu_data.x86 >= 0xf) + return 1; + lvr = apic_read(APIC_LVR); + version = GET_APIC_VERSION(lvr); + return version >= 0x14; +} + /* * 'what should we do if we get a hw irq event on an illegal vector'. * each architecture has to answer this themselves. @@ -119,10 +131,7 @@ void enable_NMI_through_LVT0 (void * dummy) int get_physical_broadcast(void) { - unsigned int lvr, version; - lvr = apic_read(APIC_LVR); - version = GET_APIC_VERSION(lvr); - if (!APIC_INTEGRATED(version) || version >= 0x14) + if (modern_apic()) return 0xff; else return 0xf; @@ -349,9 +358,9 @@ int __init verify_local_APIC(void) void __init sync_Arb_IDs(void) { - /* Unsupported on P4 - see Intel Dev. Manual Vol. 3, Ch. 8.6.1 */ - unsigned int ver = GET_APIC_VERSION(apic_read(APIC_LVR)); - if (ver >= 0x14) /* P4 or higher */ + /* Unsupported on P4 - see Intel Dev. Manual Vol. 3, Ch. 8.6.1 + And not needed on AMD */ + if (modern_apic()) return; /* * Wait for idle. @@ -415,6 +424,7 @@ void __init init_bsp_APIC(void) void __devinit setup_local_APIC(void) { unsigned long oldvalue, value, ver, maxlvt; + int i, j; /* Pound the ESR really hard over the head with a big hammer - mbligh */ if (esr_disable) { @@ -451,6 +461,25 @@ void __devinit setup_local_APIC(void) value &= ~APIC_TPRI_MASK; apic_write_around(APIC_TASKPRI, value); + /* + * After a crash, we no longer service the interrupts and a pending + * interrupt from previous kernel might still have ISR bit set. + * + * Most probably by now CPU has serviced that pending interrupt and + * it might not have done the ack_APIC_irq() because it thought, + * interrupt came from i8259 as ExtInt. LAPIC did not get EOI so it + * does not clear the ISR bit and cpu thinks it has already serivced + * the interrupt. Hence a vector might get locked. It was noticed + * for timer irq (vector 0x31). Issue an extra EOI to clear ISR. + */ + for (i = APIC_ISR_NR - 1; i >= 0; i--) { + value = apic_read(APIC_ISR + i*0x10); + for (j = 31; j >= 0; j--) { + if (value & (1<cpus_allowed; set_cpus_allowed(current, cpumask_of_cpu(pol->cpu)); @@ -969,6 +972,9 @@ static int powernowk8_verify(struct cpufreq_policy *pol) { struct powernow_k8_data *data = powernow_data[pol->cpu]; + if (!data) + return -EINVAL; + return cpufreq_frequency_table_verify(pol, data->powernow_table); } @@ -977,7 +983,7 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol) { struct powernow_k8_data *data; cpumask_t oldmask = CPU_MASK_ALL; - int rc, i; + int rc; if (!cpu_online(pol->cpu)) return -ENODEV; @@ -1063,8 +1069,7 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol) printk("cpu_init done, current fid 0x%x, vid 0x%x\n", data->currfid, data->currvid); - for_each_cpu_mask(i, cpu_core_map[pol->cpu]) - powernow_data[i] = data; + powernow_data[pol->cpu] = data; return 0; @@ -1101,6 +1106,9 @@ static unsigned int powernowk8_get (unsigned int cpu) data = powernow_data[first_cpu(cpu_core_map[cpu])]; + if (!data) + return -EINVAL; + if (!data) return -EINVAL; diff --git a/arch/i386/kernel/cpu/mcheck/mce.c b/arch/i386/kernel/cpu/mcheck/mce.c index 6170af3c271a..afa0888f9a1e 100644 --- a/arch/i386/kernel/cpu/mcheck/mce.c +++ b/arch/i386/kernel/cpu/mcheck/mce.c @@ -64,13 +64,13 @@ void mcheck_init(struct cpuinfo_x86 *c) static int __init mcheck_disable(char *str) { mce_disabled = 1; - return 0; + return 1; } static int __init mcheck_enable(char *str) { mce_disabled = -1; - return 0; + return 1; } __setup("nomce", mcheck_disable); diff --git a/arch/i386/kernel/crash.c b/arch/i386/kernel/crash.c index e3c5fca0aa8a..2b0cfce24a61 100644 --- a/arch/i386/kernel/crash.c +++ b/arch/i386/kernel/crash.c @@ -69,7 +69,7 @@ static void crash_save_this_cpu(struct pt_regs *regs, int cpu) * for the data I pass, and I need tags * on the data to indicate what information I have * squirrelled away. ELF notes happen to provide - * all of that that no need to invent something new. + * all of that, so there is no need to invent something new. */ buf = (u32*)per_cpu_ptr(crash_notes, cpu); if (!buf) diff --git a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c index 055325056a74..036a9857936f 100644 --- a/arch/i386/kernel/i386_ksyms.c +++ b/arch/i386/kernel/i386_ksyms.c @@ -19,7 +19,6 @@ EXPORT_SYMBOL(__put_user_2); EXPORT_SYMBOL(__put_user_4); EXPORT_SYMBOL(__put_user_8); -EXPORT_SYMBOL(strpbrk); EXPORT_SYMBOL(strstr); #ifdef CONFIG_SMP diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c index 3b329af4afc5..f8f132aa5472 100644 --- a/arch/i386/kernel/io_apic.c +++ b/arch/i386/kernel/io_apic.c @@ -644,7 +644,7 @@ failed: int __init irqbalance_disable(char *str) { irqbalance_disabled = 1; - return 0; + return 1; } __setup("noirqbalance", irqbalance_disable); diff --git a/arch/i386/kernel/mpparse.c b/arch/i386/kernel/mpparse.c index 8d8aa9d1796d..34d21e21e012 100644 --- a/arch/i386/kernel/mpparse.c +++ b/arch/i386/kernel/mpparse.c @@ -38,12 +38,6 @@ int smp_found_config; unsigned int __initdata maxcpus = NR_CPUS; -#ifdef CONFIG_HOTPLUG_CPU -#define CPU_HOTPLUG_ENABLED (1) -#else -#define CPU_HOTPLUG_ENABLED (0) -#endif - /* * Various Linux-internal data structures created from the * MP-table. @@ -110,21 +104,6 @@ static int __init mpf_checksum(unsigned char *mp, int len) static int mpc_record; static struct mpc_config_translation *translation_table[MAX_MPC_ENTRY] __initdata; -#ifdef CONFIG_X86_NUMAQ -static int MP_valid_apicid(int apicid, int version) -{ - return hweight_long(apicid & 0xf) == 1 && (apicid >> 4) != 0xf; -} -#else -static int MP_valid_apicid(int apicid, int version) -{ - if (version >= 0x14) - return apicid < 0xff; - else - return apicid < 0xf; -} -#endif - static void __devinit MP_processor_info (struct mpc_config_processor *m) { int ver, apicid; @@ -190,12 +169,6 @@ static void __devinit MP_processor_info (struct mpc_config_processor *m) ver = m->mpc_apicver; - if (!MP_valid_apicid(apicid, ver)) { - printk(KERN_WARNING "Processor #%d INVALID. (Max ID: %d).\n", - m->mpc_apicid, MAX_APICS); - return; - } - /* * Validate version */ @@ -225,7 +198,14 @@ static void __devinit MP_processor_info (struct mpc_config_processor *m) cpu_set(num_processors, cpu_possible_map); num_processors++; - if (CPU_HOTPLUG_ENABLED || (num_processors > 8)) { + /* + * Would be preferable to switch to bigsmp when CONFIG_HOTPLUG_CPU=y + * but we need to work other dependencies like SMP_SUSPEND etc + * before this can be done without some confusion. + * if (CPU_HOTPLUG_ENABLED || num_processors > 8) + * - Ashok Raj + */ + if (num_processors > 8) { switch (boot_cpu_data.x86_vendor) { case X86_VENDOR_INTEL: if (!APIC_XAPIC(ver)) { @@ -249,6 +229,13 @@ static void __init MP_bus_info (struct mpc_config_bus *m) mpc_oem_bus_info(m, str, translation_table[mpc_record]); + if (m->mpc_busid >= MAX_MP_BUSSES) { + printk(KERN_WARNING "MP table busid value (%d) for bustype %s " + " is too large, max. supported is %d\n", + m->mpc_busid, str, MAX_MP_BUSSES - 1); + return; + } + if (strncmp(str, BUSTYPE_ISA, sizeof(BUSTYPE_ISA)-1) == 0) { mp_bus_id_to_type[m->mpc_busid] = MP_BUS_ISA; } else if (strncmp(str, BUSTYPE_EISA, sizeof(BUSTYPE_EISA)-1) == 0) { diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index 24b3e745478b..6259afea46d1 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -781,7 +781,6 @@ unsigned long get_wchan(struct task_struct *p) } while (count++ < 16); return 0; } -EXPORT_SYMBOL(get_wchan); /* * sys_alloc_thread_area: get a yet unused TLS descriptor index. diff --git a/arch/i386/kernel/reboot_fixups.c b/arch/i386/kernel/reboot_fixups.c index 10e21a4773dd..99aab41a05b0 100644 --- a/arch/i386/kernel/reboot_fixups.c +++ b/arch/i386/kernel/reboot_fixups.c @@ -51,7 +51,5 @@ void mach_reboot_fixups(void) cur->reboot_fixup(dev); } - - printk(KERN_WARNING "No reboot fixup found for your hardware\n"); } diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 8c08660b4e5d..80cb3b2d0997 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -962,6 +963,36 @@ efi_memory_present_wrapper(unsigned long start, unsigned long end, void *arg) return 0; } + /* + * This function checks if the entire range is mapped with type. + * + * Note: this function only works correct if the e820 table is sorted and + * not-overlapping, which is the case + */ +int __init +e820_all_mapped(unsigned long start, unsigned long end, unsigned type) +{ + int i; + for (i = 0; i < e820.nr_map; i++) { + struct e820entry *ei = &e820.map[i]; + if (type && ei->type != type) + continue; + /* is the region (part) in overlap with the current region ?*/ + if (ei->addr >= end || ei->addr + ei->size <= start) + continue; + /* if the region is at the beginning of we move + * start to the end of the region since it's ok until there + */ + if (ei->addr <= start) + start = ei->addr + ei->size; + /* if start is now at or beyond end, we're done, full + * coverage */ + if (start >= end) + return 1; /* we're done */ + } + return 0; +} + /* * Find the highest page frame number we have available */ @@ -1316,8 +1347,8 @@ legacy_init_iomem_resources(struct resource *code_resource, struct resource *dat /* * Request address space for all standard resources * - * This is called just before pcibios_assign_resources(), which is also - * an fs_initcall, but is linked in later (in arch/i386/pci/i386.c). + * This is called just before pcibios_init(), which is also a + * subsys_initcall, but is linked in later (in arch/i386/pci/common.c). */ static int __init request_standard_resources(void) { @@ -1338,7 +1369,7 @@ static int __init request_standard_resources(void) return 0; } -fs_initcall(request_standard_resources); +subsys_initcall(request_standard_resources); static void __init register_memory(void) { @@ -1547,6 +1578,23 @@ void __init setup_arch(char **cmdline_p) #endif } +static __init int add_pcspkr(void) +{ + struct platform_device *pd; + int ret; + + pd = platform_device_alloc("pcspkr", -1); + if (!pd) + return -ENOMEM; + + ret = platform_device_add(pd); + if (ret) + platform_device_put(pd); + + return ret; +} +device_initcall(add_pcspkr); + #include "setup_arch_post.h" /* * Local Variables: diff --git a/arch/i386/kernel/syscall_table.S b/arch/i386/kernel/syscall_table.S index 326595f3fa4d..4f58b9c0efe3 100644 --- a/arch/i386/kernel/syscall_table.S +++ b/arch/i386/kernel/syscall_table.S @@ -312,3 +312,5 @@ ENTRY(sys_call_table) .long sys_unshare /* 310 */ .long sys_set_robust_list .long sys_get_robust_list + .long sys_splice + .long sys_sync_file_range diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 6b63a5aa1e46..2d22f5761b1d 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -365,6 +365,9 @@ void die(const char * str, struct pt_regs * regs, long err) if (++die.lock_owner_depth < 3) { int nl = 0; + unsigned long esp; + unsigned short ss; + handle_BUG(regs); printk(KERN_EMERG "%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter); #ifdef CONFIG_PREEMPT @@ -387,8 +390,19 @@ void die(const char * str, struct pt_regs * regs, long err) printk("\n"); if (notify_die(DIE_OOPS, str, regs, err, current->thread.trap_no, SIGSEGV) != - NOTIFY_STOP) + NOTIFY_STOP) { show_registers(regs); + /* Executive summary in case the oops scrolled away */ + esp = (unsigned long) (®s->esp); + savesegment(ss, ss); + if (user_mode(regs)) { + esp = regs->esp; + ss = regs->xss & 0xffff; + } + printk(KERN_EMERG "EIP: [<%08lx>] ", regs->eip); + print_symbol("%s", regs->eip); + printk(" SS:ESP %04x:%08lx\n", ss, esp); + } else regs = NULL; } else @@ -1193,6 +1207,6 @@ void __init trap_init(void) static int __init kstack_setup(char *s) { kstack_depth_to_print = simple_strtoul(s, NULL, 0); - return 0; + return 1; } __setup("kstack=", kstack_setup); diff --git a/arch/i386/kernel/vsyscall-sigreturn.S b/arch/i386/kernel/vsyscall-sigreturn.S index fadb5bc3c374..a92262f41659 100644 --- a/arch/i386/kernel/vsyscall-sigreturn.S +++ b/arch/i386/kernel/vsyscall-sigreturn.S @@ -44,7 +44,7 @@ __kernel_rt_sigreturn: .LSTARTCIEDLSI1: .long 0 /* CIE ID */ .byte 1 /* Version number */ - .string "zR" /* NUL-terminated augmentation string */ + .string "zRS" /* NUL-terminated augmentation string */ .uleb128 1 /* Code alignment factor */ .sleb128 -4 /* Data alignment factor */ .byte 8 /* Return address register column */ diff --git a/arch/i386/mach-voyager/voyager_cat.c b/arch/i386/mach-voyager/voyager_cat.c index 23967fe658d3..3039539de51e 100644 --- a/arch/i386/mach-voyager/voyager_cat.c +++ b/arch/i386/mach-voyager/voyager_cat.c @@ -106,9 +106,15 @@ voyager_module_t *voyager_cat_list; /* the I/O port assignments for the VIC and QIC */ static struct resource vic_res = { - "Voyager Interrupt Controller", 0xFC00, 0xFC6F }; + .name = "Voyager Interrupt Controller", + .start = 0xFC00, + .end = 0xFC6F +}; static struct resource qic_res = { - "Quad Interrupt Controller", 0xFC70, 0xFCFF }; + .name = "Quad Interrupt Controller", + .start = 0xFC70, + .end = 0xFCFF +}; /* This function is used to pack a data bit stream inside a message. * It writes num_bits of the data buffer in msg starting at start_bit. diff --git a/arch/i386/mm/init.c b/arch/i386/mm/init.c index 9f66ac582a8b..ae6534ad8161 100644 --- a/arch/i386/mm/init.c +++ b/arch/i386/mm/init.c @@ -651,6 +651,7 @@ void __init mem_init(void) * Specifically, in the case of x86, we will always add * memory to the highmem for now. */ +#ifdef CONFIG_HOTPLUG_MEMORY #ifndef CONFIG_NEED_MULTIPLE_NODES int add_memory(u64 start, u64 size) { @@ -667,6 +668,7 @@ int remove_memory(u64 start, u64 size) return -EINVAL; } #endif +#endif kmem_cache_t *pgd_cache; kmem_cache_t *pmd_cache; diff --git a/arch/i386/pci/direct.c b/arch/i386/pci/direct.c index 99012b93bd12..5d81fb510375 100644 --- a/arch/i386/pci/direct.c +++ b/arch/i386/pci/direct.c @@ -4,6 +4,7 @@ #include #include +#include #include "pci.h" /* @@ -18,8 +19,10 @@ int pci_conf1_read(unsigned int seg, unsigned int bus, { unsigned long flags; - if (!value || (bus > 255) || (devfn > 255) || (reg > 255)) + if ((bus > 255) || (devfn > 255) || (reg > 255)) { + *value = -1; return -EINVAL; + } spin_lock_irqsave(&pci_config_lock, flags); @@ -91,8 +94,10 @@ static int pci_conf2_read(unsigned int seg, unsigned int bus, unsigned long flags; int dev, fn; - if (!value || (bus > 255) || (devfn > 255) || (reg > 255)) + if ((bus > 255) || (devfn > 255) || (reg > 255)) { + *value = -1; return -EINVAL; + } dev = PCI_SLOT(devfn); fn = PCI_FUNC(devfn); @@ -188,6 +193,10 @@ static int __init pci_sanity_check(struct pci_raw_ops *o) if (pci_probe & PCI_NO_CHECKS) return 1; + /* Assume Type 1 works for newer systems. + This handles machines that don't have anything on PCI Bus 0. */ + if (dmi_get_year(DMI_BIOS_DATE) >= 2001) + return 1; for (devfn = 0; devfn < 0x100; devfn++) { if (o->read(0, 0, devfn, PCI_CLASS_DEVICE, 2, &x)) diff --git a/arch/i386/pci/mmconfig.c b/arch/i386/pci/mmconfig.c index 613789071f30..6b1ea0c9a570 100644 --- a/arch/i386/pci/mmconfig.c +++ b/arch/i386/pci/mmconfig.c @@ -12,14 +12,20 @@ #include #include #include +#include #include "pci.h" +#define MMCONFIG_APER_SIZE (256*1024*1024) + +/* Assume systems with more busses have correct MCFG */ +#define MAX_CHECK_BUS 16 + #define mmcfg_virt_addr ((void __iomem *) fix_to_virt(FIX_PCIE_MCFG)) /* The base address of the last MMCONFIG device accessed */ static u32 mmcfg_last_accessed_device; -static DECLARE_BITMAP(fallback_slots, 32); +static DECLARE_BITMAP(fallback_slots, MAX_CHECK_BUS*32); /* * Functions for accessing PCI configuration space with MMCONFIG accesses @@ -29,8 +35,8 @@ static u32 get_base_addr(unsigned int seg, int bus, unsigned devfn) int cfg_num = -1; struct acpi_table_mcfg_config *cfg; - if (seg == 0 && bus == 0 && - test_bit(PCI_SLOT(devfn), fallback_slots)) + if (seg == 0 && bus < MAX_CHECK_BUS && + test_bit(PCI_SLOT(devfn) + 32*bus, fallback_slots)) return 0; while (1) { @@ -74,8 +80,10 @@ static int pci_mmcfg_read(unsigned int seg, unsigned int bus, unsigned long flags; u32 base; - if (!value || (bus > 255) || (devfn > 255) || (reg > 4095)) + if ((bus > 255) || (devfn > 255) || (reg > 4095)) { + *value = -1; return -EINVAL; + } base = get_base_addr(seg, bus, devfn); if (!base) @@ -146,29 +154,34 @@ static struct pci_raw_ops pci_mmcfg = { Normally this can be expressed in the MCFG by not listing them and assigning suitable _SEGs, but this isn't implemented in some BIOS. Instead try to discover all devices on bus 0 that are unreachable using MM - and fallback for them. - We only do this for bus 0/seg 0 */ + and fallback for them. */ static __init void unreachable_devices(void) { - int i; + int i, k; unsigned long flags; - for (i = 0; i < 32; i++) { - u32 val1; - u32 addr; + for (k = 0; k < MAX_CHECK_BUS; k++) { + for (i = 0; i < 32; i++) { + u32 val1; + u32 addr; - pci_conf1_read(0, 0, PCI_DEVFN(i, 0), 0, 4, &val1); - if (val1 == 0xffffffff) - continue; + pci_conf1_read(0, k, PCI_DEVFN(i, 0), 0, 4, &val1); + if (val1 == 0xffffffff) + continue; - /* Locking probably not needed, but safer */ - spin_lock_irqsave(&pci_config_lock, flags); - addr = get_base_addr(0, 0, PCI_DEVFN(i, 0)); - if (addr != 0) - pci_exp_set_dev_base(addr, 0, PCI_DEVFN(i, 0)); - if (addr == 0 || readl((u32 __iomem *)mmcfg_virt_addr) != val1) - set_bit(i, fallback_slots); - spin_unlock_irqrestore(&pci_config_lock, flags); + /* Locking probably not needed, but safer */ + spin_lock_irqsave(&pci_config_lock, flags); + addr = get_base_addr(0, k, PCI_DEVFN(i, 0)); + if (addr != 0) + pci_exp_set_dev_base(addr, k, PCI_DEVFN(i, 0)); + if (addr == 0 || + readl((u32 __iomem *)mmcfg_virt_addr) != val1) { + set_bit(i, fallback_slots); + printk(KERN_NOTICE + "PCI: No mmconfig possible on %x:%x\n", k, i); + } + spin_unlock_irqrestore(&pci_config_lock, flags); + } } } @@ -183,6 +196,14 @@ void __init pci_mmcfg_init(void) (pci_mmcfg_config[0].base_address == 0)) return; + if (!e820_all_mapped(pci_mmcfg_config[0].base_address, + pci_mmcfg_config[0].base_address + MMCONFIG_APER_SIZE, + E820_RESERVED)) { + printk(KERN_ERR "PCI: BIOS Bug: MCFG area is not E820-reserved\n"); + printk(KERN_ERR "PCI: Not using MMCONFIG.\n"); + return; + } + printk(KERN_INFO "PCI: Using MMCONFIG\n"); raw_pci_ops = &pci_mmcfg; pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF; diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig index edffe25a477a..9f40eeff0b5c 100644 --- a/arch/ia64/Kconfig +++ b/arch/ia64/Kconfig @@ -260,15 +260,6 @@ config NR_CPUS than 64 will cause the use of a CPU mask array, causing a small performance hit. -config IA64_NR_NODES - int "Maximum number of NODEs (256-1024)" if (IA64_SGI_SN2 || IA64_GENERIC) - range 256 1024 - depends on IA64_SGI_SN2 || IA64_GENERIC - default "256" - help - This option specifies the maximum number of nodes in your SSI system. - If in doubt, use the default. - config HOTPLUG_CPU bool "Support for hot-pluggable CPUs (EXPERIMENTAL)" depends on SMP && EXPERIMENTAL @@ -352,6 +343,16 @@ config NUMA Access). This option is for configuring high-end multiprocessor server systems. If in doubt, say N. +config NODES_SHIFT + int "Max num nodes shift(3-10)" + range 3 10 + default "8" + depends on NEED_MULTIPLE_NODES + help + This option specifies the maximum number of nodes in your SSI system. + MAX_NUMNODES will be 2^(This value). + If in doubt, use the default. + # VIRTUAL_MEM_MAP and FLAT_NODE_MEM_MAP are functionally equivalent. # VIRTUAL_MEM_MAP has been retained for historical reasons. config VIRTUAL_MEM_MAP diff --git a/arch/ia64/kernel/acpi-ext.c b/arch/ia64/kernel/acpi-ext.c index 4a5574ff007b..fff82929d225 100644 --- a/arch/ia64/kernel/acpi-ext.c +++ b/arch/ia64/kernel/acpi-ext.c @@ -1,105 +1,104 @@ /* - * arch/ia64/kernel/acpi-ext.c + * (c) Copyright 2003, 2006 Hewlett-Packard Development Company, L.P. + * Alex Williamson + * Bjorn Helgaas * - * Copyright (C) 2003 Hewlett-Packard - * Copyright (C) Alex Williamson - * Copyright (C) Bjorn Helgaas - * - * Vendor specific extensions to ACPI. + * 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 -#include #include -struct acpi_vendor_descriptor { - u8 guid_id; - efi_guid_t guid; +/* + * Device CSRs that do not appear in PCI config space should be described + * via ACPI. This would normally be done with Address Space Descriptors + * marked as "consumer-only," but old versions of Windows and Linux ignore + * the producer/consumer flag, so HP invented a vendor-defined resource to + * describe the location and size of CSR space. + */ + +struct acpi_vendor_uuid hp_ccsr_uuid = { + .subtype = 2, + .data = { 0xf9, 0xad, 0xe9, 0x69, 0x4f, 0x92, 0x5f, 0xab, 0xf6, 0x4a, + 0x24, 0xd2, 0x01, 0x37, 0x0e, 0xad }, }; -struct acpi_vendor_info { - struct acpi_vendor_descriptor *descriptor; - u8 *data; - u32 length; -}; - -acpi_status -acpi_vendor_resource_match(struct acpi_resource *resource, void *context) -{ - struct acpi_vendor_info *info = (struct acpi_vendor_info *)context; - struct acpi_resource_vendor *vendor; - struct acpi_vendor_descriptor *descriptor; - u32 byte_length; - - if (resource->type != ACPI_RESOURCE_TYPE_VENDOR) - return AE_OK; - - vendor = (struct acpi_resource_vendor *)&resource->data; - descriptor = (struct acpi_vendor_descriptor *)vendor->byte_data; - if (vendor->byte_length <= sizeof(*info->descriptor) || - descriptor->guid_id != info->descriptor->guid_id || - efi_guidcmp(descriptor->guid, info->descriptor->guid)) - return AE_OK; - - byte_length = vendor->byte_length - sizeof(struct acpi_vendor_descriptor); - info->data = acpi_os_allocate(byte_length); - if (!info->data) - return AE_NO_MEMORY; - - memcpy(info->data, - vendor->byte_data + sizeof(struct acpi_vendor_descriptor), - byte_length); - info->length = byte_length; - return AE_CTRL_TERMINATE; -} - -acpi_status -acpi_find_vendor_resource(acpi_handle obj, struct acpi_vendor_descriptor * id, - u8 ** data, u32 * byte_length) -{ - struct acpi_vendor_info info; - - info.descriptor = id; - info.data = NULL; - - acpi_walk_resources(obj, METHOD_NAME__CRS, acpi_vendor_resource_match, - &info); - if (!info.data) - return AE_NOT_FOUND; - - *data = info.data; - *byte_length = info.length; - return AE_OK; -} - -struct acpi_vendor_descriptor hp_ccsr_descriptor = { - .guid_id = 2, - .guid = - EFI_GUID(0x69e9adf9, 0x924f, 0xab5f, 0xf6, 0x4a, 0x24, 0xd2, 0x01, - 0x37, 0x0e, 0xad) -}; - -acpi_status hp_acpi_csr_space(acpi_handle obj, u64 * csr_base, u64 * csr_length) +static acpi_status hp_ccsr_locate(acpi_handle obj, u64 *base, u64 *length) { acpi_status status; - u8 *data; - u32 length; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_resource *resource; + struct acpi_resource_vendor_typed *vendor; - status = - acpi_find_vendor_resource(obj, &hp_ccsr_descriptor, &data, &length); + status = acpi_get_vendor_resource(obj, METHOD_NAME__CRS, &hp_ccsr_uuid, + &buffer); - if (ACPI_FAILURE(status) || length != 16) + resource = buffer.pointer; + vendor = &resource->data.vendor_typed; + + if (ACPI_FAILURE(status) || vendor->byte_length < 16) { + status = AE_NOT_FOUND; + goto exit; + } + + memcpy(base, vendor->byte_data, sizeof(*base)); + memcpy(length, vendor->byte_data + 8, sizeof(*length)); + + exit: + acpi_os_free(buffer.pointer); + return status; +} + +struct csr_space { + u64 base; + u64 length; +}; + +static acpi_status find_csr_space(struct acpi_resource *resource, void *data) +{ + struct csr_space *space = data; + struct acpi_resource_address64 addr; + acpi_status status; + + status = acpi_resource_to_address64(resource, &addr); + if (ACPI_SUCCESS(status) && + addr.resource_type == ACPI_MEMORY_RANGE && + addr.address_length && + addr.producer_consumer == ACPI_CONSUMER) { + space->base = addr.minimum; + space->length = addr.address_length; + return AE_CTRL_TERMINATE; + } + return AE_OK; /* keep looking */ +} + +static acpi_status hp_crs_locate(acpi_handle obj, u64 *base, u64 *length) +{ + struct csr_space space = { 0, 0 }; + + acpi_walk_resources(obj, METHOD_NAME__CRS, find_csr_space, &space); + if (!space.length) return AE_NOT_FOUND; - memcpy(csr_base, data, sizeof(*csr_base)); - memcpy(csr_length, data + 8, sizeof(*csr_length)); - acpi_os_free(data); - + *base = space.base; + *length = space.length; return AE_OK; } +acpi_status hp_acpi_csr_space(acpi_handle obj, u64 *csr_base, u64 *csr_length) +{ + acpi_status status; + + status = hp_ccsr_locate(obj, csr_base, csr_length); + if (ACPI_SUCCESS(status)) + return status; + + return hp_crs_locate(obj, csr_base, csr_length); +} EXPORT_SYMBOL(hp_acpi_csr_space); diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S index 0e3eda99e549..6e16f6b35bd3 100644 --- a/arch/ia64/kernel/entry.S +++ b/arch/ia64/kernel/entry.S @@ -1605,5 +1605,9 @@ sys_call_table: data8 sys_ni_syscall // reserved for pselect data8 sys_ni_syscall // 1295 reserved for ppoll data8 sys_unshare + data8 sys_splice + data8 sys_set_robust_list + data8 sys_get_robust_list + data8 sys_sync_file_range // 1300 .org sys_call_table + 8*NR_syscalls // guard against failures to increase NR_syscalls diff --git a/arch/ia64/kernel/gate.lds.S b/arch/ia64/kernel/gate.lds.S index e1e4aba9ecd0..7c99e6ec3daf 100644 --- a/arch/ia64/kernel/gate.lds.S +++ b/arch/ia64/kernel/gate.lds.S @@ -59,6 +59,7 @@ SECTIONS *(.dynbss) *(.bss .bss.* .gnu.linkonce.b.*) *(__ex_table) + *(__mca_table) } } diff --git a/arch/ia64/kernel/iosapic.c b/arch/ia64/kernel/iosapic.c index 8832c553230a..7956eb9058fc 100644 --- a/arch/ia64/kernel/iosapic.c +++ b/arch/ia64/kernel/iosapic.c @@ -9,54 +9,65 @@ * Copyright (C) 1999 VA Linux Systems * Copyright (C) 1999,2000 Walt Drummond * - * 00/04/19 D. Mosberger Rewritten to mirror more closely the x86 I/O APIC code. - * In particular, we now have separate handlers for edge - * and level triggered interrupts. - * 00/10/27 Asit Mallick, Goutham Rao IRQ vector allocation - * PCI to vector mapping, shared PCI interrupts. - * 00/10/27 D. Mosberger Document things a bit more to make them more understandable. - * Clean up much of the old IOSAPIC cruft. - * 01/07/27 J.I. Lee PCI irq routing, Platform/Legacy interrupts and fixes for - * ACPI S5(SoftOff) support. + * 00/04/19 D. Mosberger Rewritten to mirror more closely the x86 I/O + * APIC code. In particular, we now have separate + * handlers for edge and level triggered + * interrupts. + * 00/10/27 Asit Mallick, Goutham Rao IRQ vector + * allocation PCI to vector mapping, shared PCI + * interrupts. + * 00/10/27 D. Mosberger Document things a bit more to make them more + * understandable. Clean up much of the old + * IOSAPIC cruft. + * 01/07/27 J.I. Lee PCI irq routing, Platform/Legacy interrupts + * and fixes for ACPI S5(SoftOff) support. * 02/01/23 J.I. Lee iosapic pgm fixes for PCI irq routing from _PRT - * 02/01/07 E. Focht Redirectable interrupt vectors in - * iosapic_set_affinity(), initializations for - * /proc/irq/#/smp_affinity + * 02/01/07 E. Focht Redirectable interrupt + * vectors in iosapic_set_affinity(), + * initializations for /proc/irq/#/smp_affinity * 02/04/02 P. Diefenbaugh Cleaned up ACPI PCI IRQ routing. * 02/04/18 J.I. Lee bug fix in iosapic_init_pci_irq - * 02/04/30 J.I. Lee bug fix in find_iosapic to fix ACPI PCI IRQ to IOSAPIC mapping - * error + * 02/04/30 J.I. Lee bug fix in find_iosapic to fix ACPI PCI IRQ to + * IOSAPIC mapping error * 02/07/29 T. Kochi Allocate interrupt vectors dynamically - * 02/08/04 T. Kochi Cleaned up terminology (irq, global system interrupt, vector, etc.) - * 02/09/20 D. Mosberger Simplified by taking advantage of ACPI's pci_irq code. + * 02/08/04 T. Kochi Cleaned up terminology (irq, global system + * interrupt, vector, etc.) + * 02/09/20 D. Mosberger Simplified by taking advantage of ACPI's + * pci_irq code. * 03/02/19 B. Helgaas Make pcat_compat system-wide, not per-IOSAPIC. - * Remove iosapic_address & gsi_base from external interfaces. - * Rationalize __init/__devinit attributes. + * Remove iosapic_address & gsi_base from + * external interfaces. Rationalize + * __init/__devinit attributes. * 04/12/04 Ashok Raj Intel Corporation 2004 - * Updated to work with irq migration necessary for CPU Hotplug + * Updated to work with irq migration necessary + * for CPU Hotplug */ /* - * Here is what the interrupt logic between a PCI device and the kernel looks like: + * Here is what the interrupt logic between a PCI device and the kernel looks + * like: * - * (1) A PCI device raises one of the four interrupt pins (INTA, INTB, INTC, INTD). The - * device is uniquely identified by its bus--, and slot-number (the function - * number does not matter here because all functions share the same interrupt - * lines). + * (1) A PCI device raises one of the four interrupt pins (INTA, INTB, INTC, + * INTD). The device is uniquely identified by its bus-, and slot-number + * (the function number does not matter here because all functions share + * the same interrupt lines). * - * (2) The motherboard routes the interrupt line to a pin on a IOSAPIC controller. - * Multiple interrupt lines may have to share the same IOSAPIC pin (if they're level - * triggered and use the same polarity). Each interrupt line has a unique Global - * System Interrupt (GSI) number which can be calculated as the sum of the controller's - * base GSI number and the IOSAPIC pin number to which the line connects. + * (2) The motherboard routes the interrupt line to a pin on a IOSAPIC + * controller. Multiple interrupt lines may have to share the same + * IOSAPIC pin (if they're level triggered and use the same polarity). + * Each interrupt line has a unique Global System Interrupt (GSI) number + * which can be calculated as the sum of the controller's base GSI number + * and the IOSAPIC pin number to which the line connects. * - * (3) The IOSAPIC uses an internal routing table entries (RTEs) to map the IOSAPIC pin - * into the IA-64 interrupt vector. This interrupt vector is then sent to the CPU. + * (3) The IOSAPIC uses an internal routing table entries (RTEs) to map the + * IOSAPIC pin into the IA-64 interrupt vector. This interrupt vector is then + * sent to the CPU. * - * (4) The kernel recognizes an interrupt as an IRQ. The IRQ interface is used as - * architecture-independent interrupt handling mechanism in Linux. As an - * IRQ is a number, we have to have IA-64 interrupt vector number <-> IRQ number - * mapping. On smaller systems, we use one-to-one mapping between IA-64 vector and - * IRQ. A platform can implement platform_irq_to_vector(irq) and + * (4) The kernel recognizes an interrupt as an IRQ. The IRQ interface is + * used as architecture-independent interrupt handling mechanism in Linux. + * As an IRQ is a number, we have to have + * IA-64 interrupt vector number <-> IRQ number mapping. On smaller + * systems, we use one-to-one mapping between IA-64 vector and IRQ. A + * platform can implement platform_irq_to_vector(irq) and * platform_local_vector_to_irq(vector) APIs to differentiate the mapping. * Please see also include/asm-ia64/hw_irq.h for those APIs. * @@ -64,9 +75,9 @@ * * PCI pin -> global system interrupt (GSI) -> IA-64 vector <-> IRQ * - * Note: The term "IRQ" is loosely used everywhere in Linux kernel to describe interrupts. - * Now we use "IRQ" only for Linux IRQ's. ISA IRQ (isa_irq) is the only exception in this - * source code. + * Note: The term "IRQ" is loosely used everywhere in Linux kernel to + * describeinterrupts. Now we use "IRQ" only for Linux IRQ's. ISA IRQ + * (isa_irq) is the only exception in this source code. */ #include @@ -90,7 +101,6 @@ #include #include - #undef DEBUG_INTERRUPT_ROUTING #ifdef DEBUG_INTERRUPT_ROUTING @@ -99,36 +109,46 @@ #define DBG(fmt...) #endif -#define NR_PREALLOCATE_RTE_ENTRIES (PAGE_SIZE / sizeof(struct iosapic_rte_info)) +#define NR_PREALLOCATE_RTE_ENTRIES \ + (PAGE_SIZE / sizeof(struct iosapic_rte_info)) #define RTE_PREALLOCATED (1) static DEFINE_SPINLOCK(iosapic_lock); -/* These tables map IA-64 vectors to the IOSAPIC pin that generates this vector. */ +/* + * These tables map IA-64 vectors to the IOSAPIC pin that generates this + * vector. + */ struct iosapic_rte_info { - struct list_head rte_list; /* node in list of RTEs sharing the same vector */ + struct list_head rte_list; /* node in list of RTEs sharing the + * same vector */ char __iomem *addr; /* base address of IOSAPIC */ - unsigned int gsi_base; /* first GSI assigned to this IOSAPIC */ + unsigned int gsi_base; /* first GSI assigned to this + * IOSAPIC */ char rte_index; /* IOSAPIC RTE index */ int refcnt; /* reference counter */ unsigned int flags; /* flags */ } ____cacheline_aligned; static struct iosapic_intr_info { - struct list_head rtes; /* RTEs using this vector (empty => not an IOSAPIC interrupt) */ + struct list_head rtes; /* RTEs using this vector (empty => + * not an IOSAPIC interrupt) */ int count; /* # of RTEs that shares this vector */ - u32 low32; /* current value of low word of Redirection table entry */ + u32 low32; /* current value of low word of + * Redirection table entry */ unsigned int dest; /* destination CPU physical ID */ unsigned char dmode : 3; /* delivery mode (see iosapic.h) */ - unsigned char polarity: 1; /* interrupt polarity (see iosapic.h) */ + unsigned char polarity: 1; /* interrupt polarity + * (see iosapic.h) */ unsigned char trigger : 1; /* trigger mode (see iosapic.h) */ } iosapic_intr_info[IA64_NUM_VECTORS]; static struct iosapic { char __iomem *addr; /* base address of IOSAPIC */ - unsigned int gsi_base; /* first GSI assigned to this IOSAPIC */ - unsigned short num_rte; /* number of RTE in this IOSAPIC */ + unsigned int gsi_base; /* first GSI assigned to this + * IOSAPIC */ + unsigned short num_rte; /* # of RTEs on this IOSAPIC */ int rtes_inuse; /* # of RTEs in use on this IOSAPIC */ #ifdef CONFIG_NUMA unsigned short node; /* numa node association via pxm */ @@ -149,7 +169,8 @@ find_iosapic (unsigned int gsi) int i; for (i = 0; i < NR_IOSAPICS; i++) { - if ((unsigned) (gsi - iosapic_lists[i].gsi_base) < iosapic_lists[i].num_rte) + if ((unsigned) (gsi - iosapic_lists[i].gsi_base) < + iosapic_lists[i].num_rte) return i; } @@ -162,7 +183,8 @@ _gsi_to_vector (unsigned int gsi) struct iosapic_intr_info *info; struct iosapic_rte_info *rte; - for (info = iosapic_intr_info; info < iosapic_intr_info + IA64_NUM_VECTORS; ++info) + for (info = iosapic_intr_info; info < + iosapic_intr_info + IA64_NUM_VECTORS; ++info) list_for_each_entry(rte, &info->rtes, rte_list) if (rte->gsi_base + rte->rte_index == gsi) return info - iosapic_intr_info; @@ -185,8 +207,8 @@ gsi_to_irq (unsigned int gsi) unsigned long flags; int irq; /* - * XXX fix me: this assumes an identity mapping vetween IA-64 vector and Linux irq - * numbers... + * XXX fix me: this assumes an identity mapping between IA-64 vector + * and Linux irq numbers... */ spin_lock_irqsave(&iosapic_lock, flags); { @@ -197,7 +219,8 @@ gsi_to_irq (unsigned int gsi) return irq; } -static struct iosapic_rte_info *gsi_vector_to_rte(unsigned int gsi, unsigned int vec) +static struct iosapic_rte_info *gsi_vector_to_rte(unsigned int gsi, + unsigned int vec) { struct iosapic_rte_info *rte; @@ -237,7 +260,9 @@ set_rte (unsigned int gsi, unsigned int vector, unsigned int dest, int mask) for (irq = 0; irq < NR_IRQS; ++irq) if (irq_to_vector(irq) == vector) { - set_irq_affinity_info(irq, (int)(dest & 0xffff), redir); + set_irq_affinity_info(irq, + (int)(dest & 0xffff), + redir); break; } } @@ -259,7 +284,7 @@ set_rte (unsigned int gsi, unsigned int vector, unsigned int dest, int mask) } static void -nop (unsigned int vector) +nop (unsigned int irq) { /* do nothing... */ } @@ -281,7 +306,8 @@ mask_irq (unsigned int irq) { /* set only the mask bit */ low32 = iosapic_intr_info[vec].low32 |= IOSAPIC_MASK; - list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list) { + list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, + rte_list) { addr = rte->addr; rte_index = rte->rte_index; iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32); @@ -306,7 +332,8 @@ unmask_irq (unsigned int irq) spin_lock_irqsave(&iosapic_lock, flags); { low32 = iosapic_intr_info[vec].low32 &= ~IOSAPIC_MASK; - list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list) { + list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, + rte_list) { addr = rte->addr; rte_index = rte->rte_index; iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32); @@ -346,21 +373,25 @@ iosapic_set_affinity (unsigned int irq, cpumask_t mask) spin_lock_irqsave(&iosapic_lock, flags); { - low32 = iosapic_intr_info[vec].low32 & ~(7 << IOSAPIC_DELIVERY_SHIFT); + low32 = iosapic_intr_info[vec].low32 & + ~(7 << IOSAPIC_DELIVERY_SHIFT); if (redir) /* change delivery mode to lowest priority */ - low32 |= (IOSAPIC_LOWEST_PRIORITY << IOSAPIC_DELIVERY_SHIFT); + low32 |= (IOSAPIC_LOWEST_PRIORITY << + IOSAPIC_DELIVERY_SHIFT); else /* change delivery mode to fixed */ low32 |= (IOSAPIC_FIXED << IOSAPIC_DELIVERY_SHIFT); iosapic_intr_info[vec].low32 = low32; iosapic_intr_info[vec].dest = dest; - list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, rte_list) { + list_for_each_entry(rte, &iosapic_intr_info[vec].rtes, + rte_list) { addr = rte->addr; rte_index = rte->rte_index; - iosapic_write(addr, IOSAPIC_RTE_HIGH(rte_index), high32); + iosapic_write(addr, IOSAPIC_RTE_HIGH(rte_index), + high32); iosapic_write(addr, IOSAPIC_RTE_LOW(rte_index), low32); } } @@ -433,7 +464,8 @@ iosapic_ack_edge_irq (unsigned int irq) * interrupt for real. This prevents IRQ storms from unhandled * devices. */ - if ((idesc->status & (IRQ_PENDING|IRQ_DISABLED)) == (IRQ_PENDING|IRQ_DISABLED)) + if ((idesc->status & (IRQ_PENDING|IRQ_DISABLED)) == + (IRQ_PENDING|IRQ_DISABLED)) mask_irq(irq); } @@ -467,7 +499,8 @@ iosapic_version (char __iomem *addr) return iosapic_read(addr, IOSAPIC_VERSION); } -static int iosapic_find_sharable_vector (unsigned long trigger, unsigned long pol) +static int iosapic_find_sharable_vector (unsigned long trigger, + unsigned long pol) { int i, vector = -1, min_count = -1; struct iosapic_intr_info *info; @@ -482,7 +515,8 @@ static int iosapic_find_sharable_vector (unsigned long trigger, unsigned long po for (i = IA64_FIRST_DEVICE_VECTOR; i <= IA64_LAST_DEVICE_VECTOR; i++) { info = &iosapic_intr_info[i]; if (info->trigger == trigger && info->polarity == pol && - (info->dmode == IOSAPIC_FIXED || info->dmode == IOSAPIC_LOWEST_PRIORITY)) { + (info->dmode == IOSAPIC_FIXED || info->dmode == + IOSAPIC_LOWEST_PRIORITY)) { if (min_count == -1 || info->count < min_count) { vector = i; min_count = info->count; @@ -506,12 +540,15 @@ iosapic_reassign_vector (int vector) new_vector = assign_irq_vector(AUTO_ASSIGN); if (new_vector < 0) panic("%s: out of interrupt vectors!\n", __FUNCTION__); - printk(KERN_INFO "Reassigning vector %d to %d\n", vector, new_vector); + printk(KERN_INFO "Reassigning vector %d to %d\n", + vector, new_vector); memcpy(&iosapic_intr_info[new_vector], &iosapic_intr_info[vector], sizeof(struct iosapic_intr_info)); INIT_LIST_HEAD(&iosapic_intr_info[new_vector].rtes); - list_move(iosapic_intr_info[vector].rtes.next, &iosapic_intr_info[new_vector].rtes); - memset(&iosapic_intr_info[vector], 0, sizeof(struct iosapic_intr_info)); + list_move(iosapic_intr_info[vector].rtes.next, + &iosapic_intr_info[new_vector].rtes); + memset(&iosapic_intr_info[vector], 0, + sizeof(struct iosapic_intr_info)); iosapic_intr_info[vector].low32 = IOSAPIC_MASK; INIT_LIST_HEAD(&iosapic_intr_info[vector].rtes); } @@ -524,7 +561,8 @@ static struct iosapic_rte_info *iosapic_alloc_rte (void) int preallocated = 0; if (!iosapic_kmalloc_ok && list_empty(&free_rte_list)) { - rte = alloc_bootmem(sizeof(struct iosapic_rte_info) * NR_PREALLOCATE_RTE_ENTRIES); + rte = alloc_bootmem(sizeof(struct iosapic_rte_info) * + NR_PREALLOCATE_RTE_ENTRIES); if (!rte) return NULL; for (i = 0; i < NR_PREALLOCATE_RTE_ENTRIES; i++, rte++) @@ -532,7 +570,8 @@ static struct iosapic_rte_info *iosapic_alloc_rte (void) } if (!list_empty(&free_rte_list)) { - rte = list_entry(free_rte_list.next, struct iosapic_rte_info, rte_list); + rte = list_entry(free_rte_list.next, struct iosapic_rte_info, + rte_list); list_del(&rte->rte_list); preallocated++; } else { @@ -575,7 +614,8 @@ register_intr (unsigned int gsi, int vector, unsigned char delivery, index = find_iosapic(gsi); if (index < 0) { - printk(KERN_WARNING "%s: No IOSAPIC for GSI %u\n", __FUNCTION__, gsi); + printk(KERN_WARNING "%s: No IOSAPIC for GSI %u\n", + __FUNCTION__, gsi); return -ENODEV; } @@ -586,7 +626,8 @@ register_intr (unsigned int gsi, int vector, unsigned char delivery, if (!rte) { rte = iosapic_alloc_rte(); if (!rte) { - printk(KERN_WARNING "%s: cannot allocate memory\n", __FUNCTION__); + printk(KERN_WARNING "%s: cannot allocate memory\n", + __FUNCTION__); return -ENOMEM; } @@ -602,7 +643,9 @@ register_intr (unsigned int gsi, int vector, unsigned char delivery, else if (vector_is_shared(vector)) { struct iosapic_intr_info *info = &iosapic_intr_info[vector]; if (info->trigger != trigger || info->polarity != polarity) { - printk (KERN_WARNING "%s: cannot override the interrupt\n", __FUNCTION__); + printk (KERN_WARNING + "%s: cannot override the interrupt\n", + __FUNCTION__); return -EINVAL; } } @@ -619,8 +662,10 @@ register_intr (unsigned int gsi, int vector, unsigned char delivery, idesc = irq_descp(vector); if (idesc->handler != irq_type) { if (idesc->handler != &no_irq_type) - printk(KERN_WARNING "%s: changing vector %d from %s to %s\n", - __FUNCTION__, vector, idesc->handler->typename, irq_type->typename); + printk(KERN_WARNING + "%s: changing vector %d from %s to %s\n", + __FUNCTION__, vector, + idesc->handler->typename, irq_type->typename); idesc->handler = irq_type; } return 0; @@ -681,7 +726,7 @@ get_target_cpu (unsigned int gsi, int vector) if (!num_cpus) goto skip_numa_setup; - /* Use vector assigment to distribute across cpus in node */ + /* Use vector assignment to distribute across cpus in node */ cpu_index = vector % num_cpus; for (numa_cpu = first_cpu(cpu_mask) ; i < cpu_index ; i++) @@ -703,7 +748,7 @@ skip_numa_setup: } while (!cpu_online(cpu)); return cpu_physical_id(cpu); -#else +#else /* CONFIG_SMP */ return cpu_physical_id(smp_processor_id()); #endif } @@ -755,7 +800,8 @@ again: if (list_empty(&iosapic_intr_info[vector].rtes)) free_irq_vector(vector); spin_unlock(&iosapic_lock); - spin_unlock_irqrestore(&irq_descp(vector)->lock, flags); + spin_unlock_irqrestore(&irq_descp(vector)->lock, + flags); goto again; } @@ -764,7 +810,8 @@ again: polarity, trigger); if (err < 0) { spin_unlock(&iosapic_lock); - spin_unlock_irqrestore(&irq_descp(vector)->lock, flags); + spin_unlock_irqrestore(&irq_descp(vector)->lock, + flags); return err; } @@ -806,7 +853,8 @@ iosapic_unregister_intr (unsigned int gsi) */ irq = gsi_to_irq(gsi); if (irq < 0) { - printk(KERN_ERR "iosapic_unregister_intr(%u) unbalanced\n", gsi); + printk(KERN_ERR "iosapic_unregister_intr(%u) unbalanced\n", + gsi); WARN_ON(1); return; } @@ -817,7 +865,9 @@ iosapic_unregister_intr (unsigned int gsi) spin_lock(&iosapic_lock); { if ((rte = gsi_vector_to_rte(gsi, vector)) == NULL) { - printk(KERN_ERR "iosapic_unregister_intr(%u) unbalanced\n", gsi); + printk(KERN_ERR + "iosapic_unregister_intr(%u) unbalanced\n", + gsi); WARN_ON(1); goto out; } @@ -827,7 +877,8 @@ iosapic_unregister_intr (unsigned int gsi) /* Mask the interrupt */ low32 = iosapic_intr_info[vector].low32 | IOSAPIC_MASK; - iosapic_write(rte->addr, IOSAPIC_RTE_LOW(rte->rte_index), low32); + iosapic_write(rte->addr, IOSAPIC_RTE_LOW(rte->rte_index), + low32); /* Remove the rte entry from the list */ list_del(&rte->rte_list); @@ -840,7 +891,9 @@ iosapic_unregister_intr (unsigned int gsi) trigger = iosapic_intr_info[vector].trigger; polarity = iosapic_intr_info[vector].polarity; dest = iosapic_intr_info[vector].dest; - printk(KERN_INFO "GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d unregistered\n", + printk(KERN_INFO + "GSI %u (%s, %s) -> CPU %d (0x%04x)" + " vector %d unregistered\n", gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"), (polarity == IOSAPIC_POL_HIGH ? "high" : "low"), cpu_logical_id(dest), dest, vector); @@ -853,12 +906,15 @@ iosapic_unregister_intr (unsigned int gsi) idesc->handler = &no_irq_type; /* Clear the interrupt information */ - memset(&iosapic_intr_info[vector], 0, sizeof(struct iosapic_intr_info)); + memset(&iosapic_intr_info[vector], 0, + sizeof(struct iosapic_intr_info)); iosapic_intr_info[vector].low32 |= IOSAPIC_MASK; INIT_LIST_HEAD(&iosapic_intr_info[vector].rtes); if (idesc->action) { - printk(KERN_ERR "interrupt handlers still exist on IRQ %u\n", irq); + printk(KERN_ERR + "interrupt handlers still exist on" + "IRQ %u\n", irq); WARN_ON(1); } @@ -873,7 +929,6 @@ iosapic_unregister_intr (unsigned int gsi) /* * ACPI calls this when it finds an entry for a platform interrupt. - * Note that the irq_base and IOSAPIC address must be set in iosapic_init(). */ int __init iosapic_register_platform_intr (u32 int_type, unsigned int gsi, @@ -907,13 +962,16 @@ iosapic_register_platform_intr (u32 int_type, unsigned int gsi, mask = 1; break; default: - printk(KERN_ERR "iosapic_register_platform_irq(): invalid int type 0x%x\n", int_type); + printk(KERN_ERR "%s: invalid int type 0x%x\n", __FUNCTION__, + int_type); return -1; } register_intr(gsi, vector, delivery, polarity, trigger); - printk(KERN_INFO "PLATFORM int %s (0x%x): GSI %u (%s, %s) -> CPU %d (0x%04x) vector %d\n", + printk(KERN_INFO + "PLATFORM int %s (0x%x): GSI %u (%s, %s) -> CPU %d (0x%04x)" + " vector %d\n", int_type < ARRAY_SIZE(name) ? name[int_type] : "unknown", int_type, gsi, (trigger == IOSAPIC_EDGE ? "edge" : "level"), (polarity == IOSAPIC_POL_HIGH ? "high" : "low"), @@ -923,10 +981,8 @@ iosapic_register_platform_intr (u32 int_type, unsigned int gsi, return vector; } - /* * ACPI calls this when it finds an entry for a legacy ISA IRQ override. - * Note that the gsi_base and IOSAPIC address must be set in iosapic_init(). */ void __init iosapic_override_isa_irq (unsigned int isa_irq, unsigned int gsi, @@ -955,16 +1011,19 @@ iosapic_system_init (int system_pcat_compat) for (vector = 0; vector < IA64_NUM_VECTORS; ++vector) { iosapic_intr_info[vector].low32 = IOSAPIC_MASK; - INIT_LIST_HEAD(&iosapic_intr_info[vector].rtes); /* mark as unused */ + /* mark as unused */ + INIT_LIST_HEAD(&iosapic_intr_info[vector].rtes); } pcat_compat = system_pcat_compat; if (pcat_compat) { /* - * Disable the compatibility mode interrupts (8259 style), needs IN/OUT support - * enabled. + * Disable the compatibility mode interrupts (8259 style), + * needs IN/OUT support enabled. */ - printk(KERN_INFO "%s: Disabling PC-AT compatible 8259 interrupts\n", __FUNCTION__); + printk(KERN_INFO + "%s: Disabling PC-AT compatible 8259 interrupts\n", + __FUNCTION__); outb(0xff, 0xA1); outb(0xff, 0x21); } @@ -1004,10 +1063,7 @@ iosapic_check_gsi_range (unsigned int gsi_base, unsigned int ver) base = iosapic_lists[index].gsi_base; end = base + iosapic_lists[index].num_rte - 1; - if (gsi_base < base && gsi_end < base) - continue;/* OK */ - - if (gsi_base > end && gsi_end > end) + if (gsi_end < base || end < gsi_base) continue; /* OK */ return -EBUSY; @@ -1053,12 +1109,14 @@ iosapic_init (unsigned long phys_addr, unsigned int gsi_base) if ((gsi_base == 0) && pcat_compat) { /* - * Map the legacy ISA devices into the IOSAPIC data. Some of these may - * get reprogrammed later on with data from the ACPI Interrupt Source - * Override table. + * Map the legacy ISA devices into the IOSAPIC data. Some of + * these may get reprogrammed later on with data from the ACPI + * Interrupt Source Override table. */ for (isa_irq = 0; isa_irq < 16; ++isa_irq) - iosapic_override_isa_irq(isa_irq, isa_irq, IOSAPIC_POL_HIGH, IOSAPIC_EDGE); + iosapic_override_isa_irq(isa_irq, isa_irq, + IOSAPIC_POL_HIGH, + IOSAPIC_EDGE); } return 0; } @@ -1081,7 +1139,8 @@ iosapic_remove (unsigned int gsi_base) if (iosapic_lists[index].rtes_inuse) { err = -EBUSY; - printk(KERN_WARNING "%s: IOSAPIC for GSI base %u is busy\n", + printk(KERN_WARNING + "%s: IOSAPIC for GSI base %u is busy\n", __FUNCTION__, gsi_base); goto out; } diff --git a/arch/ia64/kernel/mca.c b/arch/ia64/kernel/mca.c index 8963171788d5..5e6fdbe78bcd 100644 --- a/arch/ia64/kernel/mca.c +++ b/arch/ia64/kernel/mca.c @@ -581,10 +581,12 @@ ia64_mca_rendez_int_handler(int rendez_irq, void *arg, struct pt_regs *regs) { unsigned long flags; int cpu = smp_processor_id(); + struct ia64_mca_notify_die nd = + { .sos = NULL, .monarch_cpu = &monarch_cpu }; /* Mask all interrupts */ local_irq_save(flags); - if (notify_die(DIE_MCA_RENDZVOUS_ENTER, "MCA", regs, 0, 0, 0) + if (notify_die(DIE_MCA_RENDZVOUS_ENTER, "MCA", regs, (long)&nd, 0, 0) == NOTIFY_STOP) ia64_mca_spin(__FUNCTION__); @@ -594,7 +596,7 @@ ia64_mca_rendez_int_handler(int rendez_irq, void *arg, struct pt_regs *regs) */ ia64_sal_mc_rendez(); - if (notify_die(DIE_MCA_RENDZVOUS_PROCESS, "MCA", regs, 0, 0, 0) + if (notify_die(DIE_MCA_RENDZVOUS_PROCESS, "MCA", regs, (long)&nd, 0, 0) == NOTIFY_STOP) ia64_mca_spin(__FUNCTION__); @@ -602,7 +604,7 @@ ia64_mca_rendez_int_handler(int rendez_irq, void *arg, struct pt_regs *regs) while (monarch_cpu != -1) cpu_relax(); /* spin until monarch leaves */ - if (notify_die(DIE_MCA_RENDZVOUS_LEAVE, "MCA", regs, 0, 0, 0) + if (notify_die(DIE_MCA_RENDZVOUS_LEAVE, "MCA", regs, (long)&nd, 0, 0) == NOTIFY_STOP) ia64_mca_spin(__FUNCTION__); @@ -1023,6 +1025,8 @@ ia64_mca_handler(struct pt_regs *regs, struct switch_stack *sw, &sos->proc_state_param; int recover, cpu = smp_processor_id(); task_t *previous_current; + struct ia64_mca_notify_die nd = + { .sos = sos, .monarch_cpu = &monarch_cpu }; oops_in_progress = 1; /* FIXME: make printk NMI/MCA/INIT safe */ console_loglevel = 15; /* make sure printks make it to console */ @@ -1031,7 +1035,7 @@ ia64_mca_handler(struct pt_regs *regs, struct switch_stack *sw, previous_current = ia64_mca_modify_original_stack(regs, sw, sos, "MCA"); monarch_cpu = cpu; - if (notify_die(DIE_MCA_MONARCH_ENTER, "MCA", regs, 0, 0, 0) + if (notify_die(DIE_MCA_MONARCH_ENTER, "MCA", regs, (long)&nd, 0, 0) == NOTIFY_STOP) ia64_mca_spin(__FUNCTION__); ia64_wait_for_slaves(cpu); @@ -1043,7 +1047,7 @@ ia64_mca_handler(struct pt_regs *regs, struct switch_stack *sw, * spinning in SAL does not work. */ ia64_mca_wakeup_all(); - if (notify_die(DIE_MCA_MONARCH_PROCESS, "MCA", regs, 0, 0, 0) + if (notify_die(DIE_MCA_MONARCH_PROCESS, "MCA", regs, (long)&nd, 0, 0) == NOTIFY_STOP) ia64_mca_spin(__FUNCTION__); @@ -1064,7 +1068,7 @@ ia64_mca_handler(struct pt_regs *regs, struct switch_stack *sw, ia64_sal_clear_state_info(SAL_INFO_TYPE_MCA); sos->os_status = IA64_MCA_CORRECTED; } - if (notify_die(DIE_MCA_MONARCH_LEAVE, "MCA", regs, 0, 0, recover) + if (notify_die(DIE_MCA_MONARCH_LEAVE, "MCA", regs, (long)&nd, 0, recover) == NOTIFY_STOP) ia64_mca_spin(__FUNCTION__); @@ -1351,10 +1355,14 @@ ia64_init_handler(struct pt_regs *regs, struct switch_stack *sw, static atomic_t monarchs; task_t *previous_current; int cpu = smp_processor_id(); + struct ia64_mca_notify_die nd = + { .sos = sos, .monarch_cpu = &monarch_cpu }; oops_in_progress = 1; /* FIXME: make printk NMI/MCA/INIT safe */ console_loglevel = 15; /* make sure printks make it to console */ + (void) notify_die(DIE_INIT_ENTER, "INIT", regs, (long)&nd, 0, 0); + printk(KERN_INFO "Entered OS INIT handler. PSP=%lx cpu=%d monarch=%ld\n", sos->proc_state_param, cpu, sos->monarch); salinfo_log_wakeup(SAL_INFO_TYPE_INIT, NULL, 0, 0); @@ -1390,15 +1398,15 @@ ia64_init_handler(struct pt_regs *regs, struct switch_stack *sw, ia64_mc_info.imi_rendez_checkin[cpu] = IA64_MCA_RENDEZ_CHECKIN_INIT; while (monarch_cpu == -1) cpu_relax(); /* spin until monarch enters */ - if (notify_die(DIE_INIT_SLAVE_ENTER, "INIT", regs, 0, 0, 0) + if (notify_die(DIE_INIT_SLAVE_ENTER, "INIT", regs, (long)&nd, 0, 0) == NOTIFY_STOP) ia64_mca_spin(__FUNCTION__); - if (notify_die(DIE_INIT_SLAVE_PROCESS, "INIT", regs, 0, 0, 0) + if (notify_die(DIE_INIT_SLAVE_PROCESS, "INIT", regs, (long)&nd, 0, 0) == NOTIFY_STOP) ia64_mca_spin(__FUNCTION__); while (monarch_cpu != -1) cpu_relax(); /* spin until monarch leaves */ - if (notify_die(DIE_INIT_SLAVE_LEAVE, "INIT", regs, 0, 0, 0) + if (notify_die(DIE_INIT_SLAVE_LEAVE, "INIT", regs, (long)&nd, 0, 0) == NOTIFY_STOP) ia64_mca_spin(__FUNCTION__); printk("Slave on cpu %d returning to normal service.\n", cpu); @@ -1409,7 +1417,7 @@ ia64_init_handler(struct pt_regs *regs, struct switch_stack *sw, } monarch_cpu = cpu; - if (notify_die(DIE_INIT_MONARCH_ENTER, "INIT", regs, 0, 0, 0) + if (notify_die(DIE_INIT_MONARCH_ENTER, "INIT", regs, (long)&nd, 0, 0) == NOTIFY_STOP) ia64_mca_spin(__FUNCTION__); @@ -1426,10 +1434,10 @@ ia64_init_handler(struct pt_regs *regs, struct switch_stack *sw, * to default_monarch_init_process() above and just print all the * tasks. */ - if (notify_die(DIE_INIT_MONARCH_PROCESS, "INIT", regs, 0, 0, 0) + if (notify_die(DIE_INIT_MONARCH_PROCESS, "INIT", regs, (long)&nd, 0, 0) == NOTIFY_STOP) ia64_mca_spin(__FUNCTION__); - if (notify_die(DIE_INIT_MONARCH_LEAVE, "INIT", regs, 0, 0, 0) + if (notify_die(DIE_INIT_MONARCH_LEAVE, "INIT", regs, (long)&nd, 0, 0) == NOTIFY_STOP) ia64_mca_spin(__FUNCTION__); printk("\nINIT dump complete. Monarch on cpu %d returning to normal service.\n", cpu); @@ -1631,6 +1639,7 @@ ia64_mca_init(void) printk(KERN_INFO "Increasing MCA rendezvous timeout from " "%ld to %ld milliseconds\n", timeout, isrv.v0); timeout = isrv.v0; + (void) notify_die(DIE_MCA_NEW_TIMEOUT, "MCA", NULL, timeout, 0, 0); continue; } printk(KERN_ERR "Failed to register rendezvous interrupt " diff --git a/arch/ia64/kernel/mca_asm.S b/arch/ia64/kernel/mca_asm.S index 60a464bfd9e2..6dff024cd62b 100644 --- a/arch/ia64/kernel/mca_asm.S +++ b/arch/ia64/kernel/mca_asm.S @@ -827,7 +827,7 @@ ia64_state_restore: ld8 r9=[temp2],16 // sal_gp ;; ld8 r22=[temp1],16 // pal_min_state, virtual - ld8 r21=[temp2],16 // prev_IA64_KR_CURRENT + ld8 r13=[temp2],16 // prev_IA64_KR_CURRENT ;; ld8 r16=[temp1],16 // prev_IA64_KR_CURRENT_STACK ld8 r20=[temp2],16 // prev_task @@ -848,7 +848,7 @@ ia64_state_restore: mov cr.iim=temp3 mov cr.iha=temp4 dep r22=0,r22,62,1 // pal_min_state, physical, uncached - mov IA64_KR(CURRENT)=r21 + mov IA64_KR(CURRENT)=r13 ld8 r8=[temp1] // os_status ld8 r10=[temp2] // context @@ -856,7 +856,7 @@ ia64_state_restore: * avoid any dependencies on the algorithm in ia64_switch_to(), just * purge any existing CURRENT_STACK mapping and insert the new one. * - * r16 contains prev_IA64_KR_CURRENT_STACK, r21 contains + * r16 contains prev_IA64_KR_CURRENT_STACK, r13 contains * prev_IA64_KR_CURRENT, these values may have been changed by the C * code. Do not use r8, r9, r10, r22, they contain values ready for * the return to SAL. @@ -873,7 +873,7 @@ ia64_state_restore: ;; srlz.d - extr.u r19=r21,61,3 // r21 = prev_IA64_KR_CURRENT + extr.u r19=r13,61,3 // r13 = prev_IA64_KR_CURRENT shl r20=r16,IA64_GRANULE_SHIFT // r16 = prev_IA64_KR_CURRENT_STACK movl r21=PAGE_KERNEL // page properties ;; @@ -883,7 +883,7 @@ ia64_state_restore: (p6) br.spnt 1f // the dreaded cpu 0 idle task in region 5:( ;; mov cr.itir=r18 - mov cr.ifa=r21 + mov cr.ifa=r13 mov r20=IA64_TR_CURRENT_STACK ;; itr.d dtr[r20]=r21 diff --git a/arch/ia64/kernel/module.c b/arch/ia64/kernel/module.c index 7a2f0a798d12..3a30cfc9574f 100644 --- a/arch/ia64/kernel/module.c +++ b/arch/ia64/kernel/module.c @@ -947,7 +947,7 @@ void percpu_modcopy (void *pcpudst, const void *src, unsigned long size) { unsigned int i; - for_each_cpu(i) { + for_each_possible_cpu(i) { memcpy(pcpudst + __per_cpu_offset[i], src, size); } } diff --git a/arch/ia64/kernel/palinfo.c b/arch/ia64/kernel/palinfo.c index 89faa603c6be..6386f63c413e 100644 --- a/arch/ia64/kernel/palinfo.c +++ b/arch/ia64/kernel/palinfo.c @@ -240,7 +240,7 @@ cache_info(char *page) } p += sprintf(p, "%s Cache level %lu:\n" - "\tSize : %lu bytes\n" + "\tSize : %u bytes\n" "\tAttributes : ", cache_types[j+cci.pcci_unified], i+1, cci.pcci_cache_size); @@ -648,9 +648,9 @@ frequency_info(char *page) if (ia64_pal_freq_ratios(&proc, &bus, &itc) != 0) return 0; p += sprintf(p, - "Processor/Clock ratio : %ld/%ld\n" - "Bus/Clock ratio : %ld/%ld\n" - "ITC/Clock ratio : %ld/%ld\n", + "Processor/Clock ratio : %d/%d\n" + "Bus/Clock ratio : %d/%d\n" + "ITC/Clock ratio : %d/%d\n", proc.num, proc.den, bus.num, bus.den, itc.num, itc.den); return p - page; diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c index ac167436e936..49958904045b 100644 --- a/arch/ia64/kernel/time.c +++ b/arch/ia64/kernel/time.c @@ -188,7 +188,7 @@ ia64_init_itm (void) itc_freq = (platform_base_freq*itc_ratio.num)/itc_ratio.den; local_cpu_data->itm_delta = (itc_freq + HZ/2) / HZ; - printk(KERN_DEBUG "CPU %d: base freq=%lu.%03luMHz, ITC ratio=%lu/%lu, " + printk(KERN_DEBUG "CPU %d: base freq=%lu.%03luMHz, ITC ratio=%u/%u, " "ITC freq=%lu.%03luMHz", smp_processor_id(), platform_base_freq / 1000000, (platform_base_freq / 1000) % 1000, itc_ratio.num, itc_ratio.den, itc_freq / 1000000, (itc_freq / 1000) % 1000); diff --git a/arch/ia64/kernel/topology.c b/arch/ia64/kernel/topology.c index 3b6fd798c4d6..b47476d655f1 100644 --- a/arch/ia64/kernel/topology.c +++ b/arch/ia64/kernel/topology.c @@ -9,6 +9,8 @@ * 2002/08/07 Erich Focht * Populate cpu entries in sysfs for non-numa systems as well * Intel Corporation - Ashok Raj + * 02/27/2006 Zhang, Yanmin + * Populate cpu cache entries in sysfs for cpu cache info */ #include @@ -19,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -101,3 +104,367 @@ out: } subsys_initcall(topology_init); + + +/* + * Export cpu cache information through sysfs + */ + +/* + * A bunch of string array to get pretty printing + */ +static const char *cache_types[] = { + "", /* not used */ + "Instruction", + "Data", + "Unified" /* unified */ +}; + +static const char *cache_mattrib[]={ + "WriteThrough", + "WriteBack", + "", /* reserved */ + "" /* reserved */ +}; + +struct cache_info { + pal_cache_config_info_t cci; + cpumask_t shared_cpu_map; + int level; + int type; + struct kobject kobj; +}; + +struct cpu_cache_info { + struct cache_info *cache_leaves; + int num_cache_leaves; + struct kobject kobj; +}; + +static struct cpu_cache_info all_cpu_cache_info[NR_CPUS]; +#define LEAF_KOBJECT_PTR(x,y) (&all_cpu_cache_info[x].cache_leaves[y]) + +#ifdef CONFIG_SMP +static void cache_shared_cpu_map_setup( unsigned int cpu, + struct cache_info * this_leaf) +{ + pal_cache_shared_info_t csi; + int num_shared, i = 0; + unsigned int j; + + if (cpu_data(cpu)->threads_per_core <= 1 && + cpu_data(cpu)->cores_per_socket <= 1) { + cpu_set(cpu, this_leaf->shared_cpu_map); + return; + } + + if (ia64_pal_cache_shared_info(this_leaf->level, + this_leaf->type, + 0, + &csi) != PAL_STATUS_SUCCESS) + return; + + num_shared = (int) csi.num_shared; + do { + for_each_cpu(j) + if (cpu_data(cpu)->socket_id == cpu_data(j)->socket_id + && cpu_data(j)->core_id == csi.log1_cid + && cpu_data(j)->thread_id == csi.log1_tid) + cpu_set(j, this_leaf->shared_cpu_map); + + i++; + } while (i < num_shared && + ia64_pal_cache_shared_info(this_leaf->level, + this_leaf->type, + i, + &csi) == PAL_STATUS_SUCCESS); +} +#else +static void cache_shared_cpu_map_setup(unsigned int cpu, + struct cache_info * this_leaf) +{ + cpu_set(cpu, this_leaf->shared_cpu_map); + return; +} +#endif + +static ssize_t show_coherency_line_size(struct cache_info *this_leaf, + char *buf) +{ + return sprintf(buf, "%u\n", 1 << this_leaf->cci.pcci_line_size); +} + +static ssize_t show_ways_of_associativity(struct cache_info *this_leaf, + char *buf) +{ + return sprintf(buf, "%u\n", this_leaf->cci.pcci_assoc); +} + +static ssize_t show_attributes(struct cache_info *this_leaf, char *buf) +{ + return sprintf(buf, + "%s\n", + cache_mattrib[this_leaf->cci.pcci_cache_attr]); +} + +static ssize_t show_size(struct cache_info *this_leaf, char *buf) +{ + return sprintf(buf, "%uK\n", this_leaf->cci.pcci_cache_size / 1024); +} + +static ssize_t show_number_of_sets(struct cache_info *this_leaf, char *buf) +{ + unsigned number_of_sets = this_leaf->cci.pcci_cache_size; + number_of_sets /= this_leaf->cci.pcci_assoc; + number_of_sets /= 1 << this_leaf->cci.pcci_line_size; + + return sprintf(buf, "%u\n", number_of_sets); +} + +static ssize_t show_shared_cpu_map(struct cache_info *this_leaf, char *buf) +{ + ssize_t len; + cpumask_t shared_cpu_map; + + cpus_and(shared_cpu_map, this_leaf->shared_cpu_map, cpu_online_map); + len = cpumask_scnprintf(buf, NR_CPUS+1, shared_cpu_map); + len += sprintf(buf+len, "\n"); + return len; +} + +static ssize_t show_type(struct cache_info *this_leaf, char *buf) +{ + int type = this_leaf->type + this_leaf->cci.pcci_unified; + return sprintf(buf, "%s\n", cache_types[type]); +} + +static ssize_t show_level(struct cache_info *this_leaf, char *buf) +{ + return sprintf(buf, "%u\n", this_leaf->level); +} + +struct cache_attr { + struct attribute attr; + ssize_t (*show)(struct cache_info *, char *); + ssize_t (*store)(struct cache_info *, const char *, size_t count); +}; + +#ifdef define_one_ro + #undef define_one_ro +#endif +#define define_one_ro(_name) \ + static struct cache_attr _name = \ +__ATTR(_name, 0444, show_##_name, NULL) + +define_one_ro(level); +define_one_ro(type); +define_one_ro(coherency_line_size); +define_one_ro(ways_of_associativity); +define_one_ro(size); +define_one_ro(number_of_sets); +define_one_ro(shared_cpu_map); +define_one_ro(attributes); + +static struct attribute * cache_default_attrs[] = { + &type.attr, + &level.attr, + &coherency_line_size.attr, + &ways_of_associativity.attr, + &attributes.attr, + &size.attr, + &number_of_sets.attr, + &shared_cpu_map.attr, + NULL +}; + +#define to_object(k) container_of(k, struct cache_info, kobj) +#define to_attr(a) container_of(a, struct cache_attr, attr) + +static ssize_t cache_show(struct kobject * kobj, struct attribute * attr, char * buf) +{ + struct cache_attr *fattr = to_attr(attr); + struct cache_info *this_leaf = to_object(kobj); + ssize_t ret; + + ret = fattr->show ? fattr->show(this_leaf, buf) : 0; + return ret; +} + +static struct sysfs_ops cache_sysfs_ops = { + .show = cache_show +}; + +static struct kobj_type cache_ktype = { + .sysfs_ops = &cache_sysfs_ops, + .default_attrs = cache_default_attrs, +}; + +static struct kobj_type cache_ktype_percpu_entry = { + .sysfs_ops = &cache_sysfs_ops, +}; + +static void __cpuinit cpu_cache_sysfs_exit(unsigned int cpu) +{ + if (all_cpu_cache_info[cpu].cache_leaves) { + kfree(all_cpu_cache_info[cpu].cache_leaves); + all_cpu_cache_info[cpu].cache_leaves = NULL; + } + all_cpu_cache_info[cpu].num_cache_leaves = 0; + memset(&all_cpu_cache_info[cpu].kobj, 0, sizeof(struct kobject)); + + return; +} + +static int __cpuinit cpu_cache_sysfs_init(unsigned int cpu) +{ + u64 i, levels, unique_caches; + pal_cache_config_info_t cci; + int j; + s64 status; + struct cache_info *this_cache; + int num_cache_leaves = 0; + + if ((status = ia64_pal_cache_summary(&levels, &unique_caches)) != 0) { + printk(KERN_ERR "ia64_pal_cache_summary=%ld\n", status); + return -1; + } + + this_cache=kzalloc(sizeof(struct cache_info)*unique_caches, + GFP_KERNEL); + if (this_cache == NULL) + return -ENOMEM; + + for (i=0; i < levels; i++) { + for (j=2; j >0 ; j--) { + if ((status=ia64_pal_cache_config_info(i,j, &cci)) != + PAL_STATUS_SUCCESS) + continue; + + this_cache[num_cache_leaves].cci = cci; + this_cache[num_cache_leaves].level = i + 1; + this_cache[num_cache_leaves].type = j; + + cache_shared_cpu_map_setup(cpu, + &this_cache[num_cache_leaves]); + num_cache_leaves ++; + } + } + + all_cpu_cache_info[cpu].cache_leaves = this_cache; + all_cpu_cache_info[cpu].num_cache_leaves = num_cache_leaves; + + memset(&all_cpu_cache_info[cpu].kobj, 0, sizeof(struct kobject)); + + return 0; +} + +/* Add cache interface for CPU device */ +static int __cpuinit cache_add_dev(struct sys_device * sys_dev) +{ + unsigned int cpu = sys_dev->id; + unsigned long i, j; + struct cache_info *this_object; + int retval = 0; + cpumask_t oldmask; + + if (all_cpu_cache_info[cpu].kobj.parent) + return 0; + + oldmask = current->cpus_allowed; + retval = set_cpus_allowed(current, cpumask_of_cpu(cpu)); + if (unlikely(retval)) + return retval; + + retval = cpu_cache_sysfs_init(cpu); + set_cpus_allowed(current, oldmask); + if (unlikely(retval < 0)) + return retval; + + all_cpu_cache_info[cpu].kobj.parent = &sys_dev->kobj; + kobject_set_name(&all_cpu_cache_info[cpu].kobj, "%s", "cache"); + all_cpu_cache_info[cpu].kobj.ktype = &cache_ktype_percpu_entry; + retval = kobject_register(&all_cpu_cache_info[cpu].kobj); + + for (i = 0; i < all_cpu_cache_info[cpu].num_cache_leaves; i++) { + this_object = LEAF_KOBJECT_PTR(cpu,i); + this_object->kobj.parent = &all_cpu_cache_info[cpu].kobj; + kobject_set_name(&(this_object->kobj), "index%1lu", i); + this_object->kobj.ktype = &cache_ktype; + retval = kobject_register(&(this_object->kobj)); + if (unlikely(retval)) { + for (j = 0; j < i; j++) { + kobject_unregister( + &(LEAF_KOBJECT_PTR(cpu,j)->kobj)); + } + kobject_unregister(&all_cpu_cache_info[cpu].kobj); + cpu_cache_sysfs_exit(cpu); + break; + } + } + return retval; +} + +/* Remove cache interface for CPU device */ +static int __cpuinit cache_remove_dev(struct sys_device * sys_dev) +{ + unsigned int cpu = sys_dev->id; + unsigned long i; + + for (i = 0; i < all_cpu_cache_info[cpu].num_cache_leaves; i++) + kobject_unregister(&(LEAF_KOBJECT_PTR(cpu,i)->kobj)); + + if (all_cpu_cache_info[cpu].kobj.parent) { + kobject_unregister(&all_cpu_cache_info[cpu].kobj); + memset(&all_cpu_cache_info[cpu].kobj, + 0, + sizeof(struct kobject)); + } + + cpu_cache_sysfs_exit(cpu); + + return 0; +} + +/* + * When a cpu is hot-plugged, do a check and initiate + * cache kobject if necessary + */ +static int __cpuinit cache_cpu_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + unsigned int cpu = (unsigned long)hcpu; + struct sys_device *sys_dev; + + sys_dev = get_cpu_sysdev(cpu); + switch (action) { + case CPU_ONLINE: + cache_add_dev(sys_dev); + break; + case CPU_DEAD: + cache_remove_dev(sys_dev); + break; + } + return NOTIFY_OK; +} + +static struct notifier_block cache_cpu_notifier = +{ + .notifier_call = cache_cpu_callback +}; + +static int __cpuinit cache_sysfs_init(void) +{ + int i; + + for_each_online_cpu(i) { + cache_cpu_callback(&cache_cpu_notifier, CPU_ONLINE, + (void *)(long)i); + } + + register_cpu_notifier(&cache_cpu_notifier); + + return 0; +} + +device_initcall(cache_sysfs_init); + diff --git a/arch/ia64/kernel/vmlinux.lds.S b/arch/ia64/kernel/vmlinux.lds.S index 0b9e56dd7f05..783600fe52b2 100644 --- a/arch/ia64/kernel/vmlinux.lds.S +++ b/arch/ia64/kernel/vmlinux.lds.S @@ -70,6 +70,15 @@ SECTIONS __stop___ex_table = .; } + /* MCA table */ + . = ALIGN(16); + __mca_table : AT(ADDR(__mca_table) - LOAD_OFFSET) + { + __start___mca_table = .; + *(__mca_table) + __stop___mca_table = .; + } + /* Global data */ _data = .; @@ -130,15 +139,6 @@ SECTIONS __initcall_end = .; } - /* MCA table */ - . = ALIGN(16); - __mca_table : AT(ADDR(__mca_table) - LOAD_OFFSET) - { - __start___mca_table = .; - *(__mca_table) - __stop___mca_table = .; - } - .data.patch.vtop : AT(ADDR(.data.patch.vtop) - LOAD_OFFSET) { __start___vtop_patchlist = .; diff --git a/arch/ia64/mm/fault.c b/arch/ia64/mm/fault.c index af7eb087dca7..d98ec49570b8 100644 --- a/arch/ia64/mm/fault.c +++ b/arch/ia64/mm/fault.c @@ -60,6 +60,9 @@ ia64_do_page_fault (unsigned long address, unsigned long isr, struct pt_regs *re struct siginfo si; unsigned long mask; + /* mmap_sem is performance critical.... */ + prefetchw(&mm->mmap_sem); + /* * If we're in an interrupt or have no user context, we must not take the fault.. */ diff --git a/arch/ia64/mm/init.c b/arch/ia64/mm/init.c index 2ef1151cde90..cafa8776a53d 100644 --- a/arch/ia64/mm/init.c +++ b/arch/ia64/mm/init.c @@ -109,6 +109,7 @@ lazy_mmu_prot_update (pte_t pte) { unsigned long addr; struct page *page; + unsigned long order; if (!pte_exec(pte)) return; /* not an executable page... */ @@ -119,7 +120,12 @@ lazy_mmu_prot_update (pte_t pte) if (test_bit(PG_arch_1, &page->flags)) return; /* i-cache is already coherent with d-cache */ - flush_icache_range(addr, addr + PAGE_SIZE); + if (PageCompound(page)) { + order = (unsigned long) (page[1].lru.prev); + flush_icache_range(addr, addr + (1UL << order << PAGE_SHIFT)); + } + else + flush_icache_range(addr, addr + PAGE_SIZE); set_bit(PG_arch_1, &page->flags); /* mark page as clean */ } diff --git a/arch/ia64/mm/ioremap.c b/arch/ia64/mm/ioremap.c index 62328621f99c..643ccc6960ce 100644 --- a/arch/ia64/mm/ioremap.c +++ b/arch/ia64/mm/ioremap.c @@ -21,12 +21,12 @@ __ioremap (unsigned long offset, unsigned long size) void __iomem * ioremap (unsigned long offset, unsigned long size) { - if (efi_mem_attribute_range(offset, size, EFI_MEMORY_UC)) - return __ioremap(offset, size); - if (efi_mem_attribute_range(offset, size, EFI_MEMORY_WB)) return phys_to_virt(offset); + if (efi_mem_attribute_range(offset, size, EFI_MEMORY_UC)) + return __ioremap(offset, size); + /* * Someday this should check ACPI resources so we * can do the right thing for hot-plugged regions. diff --git a/arch/ia64/mm/tlb.c b/arch/ia64/mm/tlb.c index 6a4eec9113e8..4dbbca0b5e9c 100644 --- a/arch/ia64/mm/tlb.c +++ b/arch/ia64/mm/tlb.c @@ -156,17 +156,19 @@ flush_tlb_range (struct vm_area_struct *vma, unsigned long start, nbits = purge.max_bits; start &= ~((1UL << nbits) - 1); -# ifdef CONFIG_SMP - platform_global_tlb_purge(mm, start, end, nbits); -# else preempt_disable(); +#ifdef CONFIG_SMP + if (mm != current->active_mm || cpus_weight(mm->cpu_vm_mask) != 1) { + platform_global_tlb_purge(mm, start, end, nbits); + preempt_enable(); + return; + } +#endif do { ia64_ptcl(start, (nbits<<2)); start += (1UL << nbits); } while (start < end); preempt_enable(); -# endif - ia64_srlz_i(); /* srlz.i implies srlz.d */ } EXPORT_SYMBOL(flush_tlb_range); diff --git a/arch/ia64/sn/kernel/sn2/sn_hwperf.c b/arch/ia64/sn/kernel/sn2/sn_hwperf.c index 70db21f3df21..d917afa30b27 100644 --- a/arch/ia64/sn/kernel/sn2/sn_hwperf.c +++ b/arch/ia64/sn/kernel/sn2/sn_hwperf.c @@ -110,7 +110,11 @@ static int sn_hwperf_geoid_to_cnode(char *location) if (sn_hwperf_location_to_bpos(location, &rack, &bay, &slot, &slab)) return -1; - for_each_node(cnode) { + /* + * FIXME: replace with cleaner for_each_XXX macro which addresses + * both compute and IO nodes once ACPI3.0 is available. + */ + for (cnode = 0; cnode < num_cnodes; cnode++) { geoid = cnodeid_get_geoid(cnode); module_id = geo_module(geoid); this_rack = MODULE_GET_RACK(module_id); @@ -605,7 +609,7 @@ static int sn_hwperf_op_cpu(struct sn_hwperf_op_info *op_info) op_info->a->arg &= SN_HWPERF_ARG_OBJID_MASK; if (cpu != SN_HWPERF_ARG_ANY_CPU) { - if (cpu >= num_online_cpus() || !cpu_online(cpu)) { + if (cpu >= NR_CPUS || !cpu_online(cpu)) { r = -EINVAL; goto out; } diff --git a/arch/ia64/sn/kernel/xpc_channel.c b/arch/ia64/sn/kernel/xpc_channel.c index d0abddd9ffe6..8255a9be4632 100644 --- a/arch/ia64/sn/kernel/xpc_channel.c +++ b/arch/ia64/sn/kernel/xpc_channel.c @@ -1831,7 +1831,7 @@ xpc_initiate_allocate(partid_t partid, int ch_number, u32 flags, void **payload) { struct xpc_partition *part = &xpc_partitions[partid]; enum xpc_retval ret = xpcUnknownReason; - struct xpc_msg *msg; + struct xpc_msg *msg = NULL; DBUG_ON(partid <= 0 || partid >= XP_MAX_PARTITIONS); diff --git a/arch/m32r/Kconfig b/arch/m32r/Kconfig index 05c864c6c2d9..41fd490af3b4 100644 --- a/arch/m32r/Kconfig +++ b/arch/m32r/Kconfig @@ -285,6 +285,11 @@ config NUMA depends on SMP && BROKEN default n +config NODES_SHIFT + int + default "1" + depends on NEED_MULTIPLE_NODES + # turning this on wastes a bunch of space. # Summit needs it only when NUMA is on config BOOT_IOREMAP diff --git a/arch/m32r/kernel/m32r_ksyms.c b/arch/m32r/kernel/m32r_ksyms.c index be8b711367ec..c50330fa83b9 100644 --- a/arch/m32r/kernel/m32r_ksyms.c +++ b/arch/m32r/kernel/m32r_ksyms.c @@ -23,9 +23,6 @@ EXPORT_SYMBOL(boot_cpu_data); EXPORT_SYMBOL(dump_fpu); EXPORT_SYMBOL(__ioremap); EXPORT_SYMBOL(iounmap); -EXPORT_SYMBOL(enable_irq); -EXPORT_SYMBOL(disable_irq); -EXPORT_SYMBOL(disable_irq_nosync); EXPORT_SYMBOL(kernel_thread); EXPORT_SYMBOL(__down); EXPORT_SYMBOL(__down_interruptible); @@ -38,13 +35,6 @@ EXPORT_SYMBOL(__udelay); EXPORT_SYMBOL(__delay); EXPORT_SYMBOL(__const_udelay); -EXPORT_SYMBOL(__get_user_1); -EXPORT_SYMBOL(__get_user_2); -EXPORT_SYMBOL(__get_user_4); - -EXPORT_SYMBOL(strpbrk); -EXPORT_SYMBOL(strstr); - EXPORT_SYMBOL(strncpy_from_user); EXPORT_SYMBOL(__strncpy_from_user); EXPORT_SYMBOL(clear_user); @@ -59,11 +49,8 @@ extern void *dcache_dummy; EXPORT_SYMBOL(dcache_dummy); #endif EXPORT_SYMBOL(cpu_data); -EXPORT_SYMBOL(cpu_online_map); -EXPORT_SYMBOL(cpu_callout_map); /* Global SMP stuff */ -EXPORT_SYMBOL(synchronize_irq); EXPORT_SYMBOL(smp_call_function); /* TLB flushing */ @@ -83,27 +70,11 @@ EXPORT_SYMBOL(__lshrdi3); EXPORT_SYMBOL(__muldi3); /* memory and string operations */ -EXPORT_SYMBOL(memchr); EXPORT_SYMBOL(memcpy); -/* EXPORT_SYMBOL(memcpy_fromio); // not implement yet */ -/* EXPORT_SYMBOL(memcpy_toio); // not implement yet */ EXPORT_SYMBOL(memset); -/* EXPORT_SYMBOL(memset_io); // not implement yet */ -EXPORT_SYMBOL(memmove); -EXPORT_SYMBOL(memcmp); -EXPORT_SYMBOL(memscan); EXPORT_SYMBOL(copy_page); EXPORT_SYMBOL(clear_page); - -EXPORT_SYMBOL(strcat); -EXPORT_SYMBOL(strchr); -EXPORT_SYMBOL(strcmp); -EXPORT_SYMBOL(strcpy); EXPORT_SYMBOL(strlen); -EXPORT_SYMBOL(strncat); -EXPORT_SYMBOL(strncmp); -EXPORT_SYMBOL(strnlen); -EXPORT_SYMBOL(strncpy); EXPORT_SYMBOL(_inb); EXPORT_SYMBOL(_inw); diff --git a/arch/m32r/kernel/setup.c b/arch/m32r/kernel/setup.c index 0d78942b4c76..3cd3c2988a48 100644 --- a/arch/m32r/kernel/setup.c +++ b/arch/m32r/kernel/setup.c @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -219,8 +220,6 @@ static unsigned long __init setup_memory(void) extern unsigned long setup_memory(void); #endif /* CONFIG_DISCONTIGMEM */ -#define M32R_PCC_PCATCR 0x00ef7014 /* will move to m32r.h */ - void __init setup_arch(char **cmdline_p) { ROOT_DEV = old_decode_dev(ORIG_ROOT_DEV); @@ -269,15 +268,14 @@ void __init setup_arch(char **cmdline_p) paging_init(); } -static struct cpu cpu[NR_CPUS]; +static struct cpu cpu_devices[NR_CPUS]; static int __init topology_init(void) { - int cpu_id; + int i; - for (cpu_id = 0; cpu_id < NR_CPUS; cpu_id++) - if (cpu_possible(cpu_id)) - register_cpu(&cpu[cpu_id], cpu_id, NULL); + for_each_present_cpu(i) + register_cpu(&cpu_devices[i], i, NULL); return 0; } diff --git a/arch/m32r/kernel/smpboot.c b/arch/m32r/kernel/smpboot.c index d7ec16e7fb25..840b4348bf0c 100644 --- a/arch/m32r/kernel/smpboot.c +++ b/arch/m32r/kernel/smpboot.c @@ -39,8 +39,10 @@ * Martin J. Bligh : Added support for multi-quad systems */ +#include #include #include +#include #include #include #include @@ -72,11 +74,15 @@ physid_mask_t phys_cpu_present_map; /* Bitmask of currently online CPUs */ cpumask_t cpu_online_map; +EXPORT_SYMBOL(cpu_online_map); cpumask_t cpu_bootout_map; cpumask_t cpu_bootin_map; -cpumask_t cpu_callout_map; static cpumask_t cpu_callin_map; +cpumask_t cpu_callout_map; +EXPORT_SYMBOL(cpu_callout_map); +cpumask_t cpu_possible_map = CPU_MASK_ALL; +EXPORT_SYMBOL(cpu_possible_map); /* Per CPU bogomips and other parameters */ struct cpuinfo_m32r cpu_data[NR_CPUS] __cacheline_aligned; @@ -110,7 +116,6 @@ static unsigned int calibration_result; void smp_prepare_boot_cpu(void); void smp_prepare_cpus(unsigned int); -static void smp_tune_scheduling(void); static void init_ipi_lock(void); static void do_boot_cpu(int); int __cpu_up(unsigned int); @@ -177,6 +182,9 @@ void __init smp_prepare_cpus(unsigned int max_cpus) } for (phys_id = 0 ; phys_id < nr_cpu ; phys_id++) physid_set(phys_id, phys_cpu_present_map); +#ifndef CONFIG_HOTPLUG_CPU + cpu_present_map = cpu_possible_map; +#endif show_mp_info(nr_cpu); @@ -186,7 +194,6 @@ void __init smp_prepare_cpus(unsigned int max_cpus) * Setup boot CPU information */ smp_store_cpu_info(0); /* Final full version of the data */ - smp_tune_scheduling(); /* * If SMP should be disabled, then really disable it! @@ -230,11 +237,6 @@ smp_done: Dprintk("Boot done.\n"); } -static void __init smp_tune_scheduling(void) -{ - /* Nothing to do. */ -} - /* * init_ipi_lock : Initialize IPI locks. */ @@ -629,4 +631,3 @@ static void __init unmap_cpu_to_physid(int cpu_id, int phys_id) physid_2_cpu[phys_id] = -1; cpu_2_physid[cpu_id] = -1; } - diff --git a/arch/m32r/lib/Makefile b/arch/m32r/lib/Makefile index e632d10c7d78..d16b4e40d1ae 100644 --- a/arch/m32r/lib/Makefile +++ b/arch/m32r/lib/Makefile @@ -2,6 +2,6 @@ # Makefile for M32R-specific library files.. # -lib-y := checksum.o ashxdi3.o memset.o memcpy.o getuser.o \ - putuser.o delay.o strlen.o usercopy.o csum_partial_copy.o +lib-y := checksum.o ashxdi3.o memset.o memcpy.o \ + delay.o strlen.o usercopy.o csum_partial_copy.o diff --git a/arch/m32r/lib/getuser.S b/arch/m32r/lib/getuser.S deleted file mode 100644 index 58a0db055c5c..000000000000 --- a/arch/m32r/lib/getuser.S +++ /dev/null @@ -1,88 +0,0 @@ -/* - * __get_user functions. - * - * (C) Copyright 2001 Hirokazu Takata - * - * These functions have a non-standard call interface - * to make them more efficient, especially as they - * return an error value in addition to the "real" - * return value. - */ - -#include - -/* - * __get_user_X - * - * Inputs: r0 contains the address - * - * Outputs: r0 is error code (0 or -EFAULT) - * r1 contains zero-extended value - * - * These functions should not modify any other registers, - * as they get called from within inline assembly. - */ - -#ifdef CONFIG_ISA_DUAL_ISSUE - - .text - .balign 4 - .globl __get_user_1 -__get_user_1: -1: ldub r1, @r0 || ldi r0, #0 - jmp r14 - - .balign 4 - .globl __get_user_2 -__get_user_2: -2: lduh r1, @r0 || ldi r0, #0 - jmp r14 - - .balign 4 - .globl __get_user_4 -__get_user_4: -3: ld r1, @r0 || ldi r0, #0 - jmp r14 - -bad_get_user: - ldi r1, #0 || ldi r0, #-14 - jmp r14 - -#else /* not CONFIG_ISA_DUAL_ISSUE */ - - .text - .balign 4 - .globl __get_user_1 -__get_user_1: -1: ldub r1, @r0 - ldi r0, #0 - jmp r14 - - .balign 4 - .globl __get_user_2 -__get_user_2: -2: lduh r1, @r0 - ldi r0, #0 - jmp r14 - - .balign 4 - .globl __get_user_4 -__get_user_4: -3: ld r1, @r0 - ldi r0, #0 - jmp r14 - -bad_get_user: - ldi r1, #0 - ldi r0, #-14 - jmp r14 - -#endif /* not CONFIG_ISA_DUAL_ISSUE */ - -.section __ex_table,"a" - .long 1b,bad_get_user - .long 2b,bad_get_user - .long 3b,bad_get_user -.previous - - .end diff --git a/arch/m32r/lib/putuser.S b/arch/m32r/lib/putuser.S deleted file mode 100644 index 218154cc3890..000000000000 --- a/arch/m32r/lib/putuser.S +++ /dev/null @@ -1,84 +0,0 @@ -/* - * __put_user functions. - * - * (C) Copyright 1998 Linus Torvalds - * (C) Copyright 2001 Hirokazu Takata - * - * These functions have a non-standard call interface - * to make them more efficient. - */ - -#include - -/* - * __put_user_X - * - * Inputs: r0 contains the address - * r1 contains the value - * - * Outputs: r0 is error code (0 or -EFAULT) - * r1 is corrupted (will contain "current_task"). - * - * These functions should not modify any other registers, - * as they get called from within inline assembly. - */ - -#ifdef CONFIG_ISA_DUAL_ISSUE - - .text - .balign 4 - .globl __put_user_1 -__put_user_1: -1: stb r1, @r0 || ldi r0, #0 - jmp r14 - - .balign 4 - .globl __put_user_2 -__put_user_2: -2: sth r1, @r0 || ldi r0, #0 - jmp r14 - - .balign 4 - .globl __put_user_4 -__put_user_4: -3: st r1, @r0 || ldi r0, #0 - jmp r14 - -bad_put_user: - ldi r0, #-14 || jmp r14 - -#else /* not CONFIG_ISA_DUAL_ISSUE */ - - .text - .balign 4 - .globl __put_user_1 -__put_user_1: -1: stb r1, @r0 - ldi r0, #0 - jmp r14 - - .balign 4 - .globl __put_user_2 -__put_user_2: -2: sth r1, @r0 - ldi r0, #0 - jmp r14 - - .balign 4 - .globl __put_user_4 -__put_user_4: -3: st r1, @r0 - ldi r0, #0 - jmp r14 - -bad_put_user: - ldi r0, #-14 - jmp r14 - -#endif /* not CONFIG_ISA_DUAL_ISSUE */ - -.section __ex_table,"a" - .long 1b,bad_put_user - .long 2b,bad_put_user - .long 3b,bad_put_user -.previous diff --git a/arch/m68k/kernel/m68k_ksyms.c b/arch/m68k/kernel/m68k_ksyms.c index 3d7f2000b714..5b7952ea2bae 100644 --- a/arch/m68k/kernel/m68k_ksyms.c +++ b/arch/m68k/kernel/m68k_ksyms.c @@ -57,7 +57,6 @@ EXPORT_SYMBOL(dump_thread); EXPORT_SYMBOL(strnlen); EXPORT_SYMBOL(strrchr); EXPORT_SYMBOL(strstr); -EXPORT_SYMBOL(strpbrk); EXPORT_SYMBOL(enable_irq); EXPORT_SYMBOL(disable_irq); EXPORT_SYMBOL(kernel_thread); @@ -79,4 +78,3 @@ EXPORT_SYMBOL(__down_failed_interruptible); EXPORT_SYMBOL(__down_failed_trylock); EXPORT_SYMBOL(__up_wakeup); -EXPORT_SYMBOL(get_wchan); diff --git a/arch/m68knommu/kernel/m68k_ksyms.c b/arch/m68knommu/kernel/m68k_ksyms.c index d844c755945a..4320d5dcc9cb 100644 --- a/arch/m68knommu/kernel/m68k_ksyms.c +++ b/arch/m68knommu/kernel/m68k_ksyms.c @@ -26,7 +26,6 @@ EXPORT_SYMBOL(__ioremap); EXPORT_SYMBOL(iounmap); EXPORT_SYMBOL(dump_fpu); EXPORT_SYMBOL(strnlen); -EXPORT_SYMBOL(strpbrk); EXPORT_SYMBOL(strrchr); EXPORT_SYMBOL(strstr); EXPORT_SYMBOL(strchr); @@ -57,8 +56,6 @@ EXPORT_SYMBOL(__down_failed_interruptible); EXPORT_SYMBOL(__down_failed_trylock); EXPORT_SYMBOL(__up_wakeup); -EXPORT_SYMBOL(get_wchan); - /* * libgcc functions - functions that are used internally by the * compiler... (prototypes are not correct though, but that diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 5080ea1799a4..7aec60d40420 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -233,6 +233,7 @@ config MACH_JAZZ select ARC32 select ARCH_MAY_HAVE_PC_FDC select GENERIC_ISA_DMA + select I8253 select I8259 select ISA select SYS_HAS_CPU_R4X00 @@ -530,6 +531,7 @@ config QEMU select DMA_COHERENT select GENERIC_ISA_DMA select HAVE_STD_PC_SERIAL_PORT + select I8253 select I8259 select ISA select SWAP_IO_SPACE @@ -714,6 +716,7 @@ config SNI_RM200_PCI select HAVE_STD_PC_SERIAL_PORT select HW_HAS_EISA select HW_HAS_PCI + select I8253 select I8259 select ISA select SYS_HAS_CPU_R4X00 @@ -1587,6 +1590,11 @@ config ARCH_FLATMEM_ENABLE def_bool y depends on !NUMA +config NODES_SHIFT + int + default "6" + depends on NEED_MULTIPLE_NODES + source "mm/Kconfig" config SMP @@ -1721,6 +1729,9 @@ config MMU bool default y +config I8253 + bool + source "drivers/pcmcia/Kconfig" source "drivers/pci/hotplug/Kconfig" diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile index f36c4f20ee8a..309d54cceda3 100644 --- a/arch/mips/kernel/Makefile +++ b/arch/mips/kernel/Makefile @@ -59,6 +59,8 @@ obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_64BIT) += cpu-bugs64.o +obj-$(CONFIG_I8253) += i8253.o + CFLAGS_cpu-bugs64.o = $(shell if $(CC) $(CFLAGS) -Wa,-mdaddi -c -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-DHAVE_AS_SET_DADDI"; fi) EXTRA_AFLAGS := $(CFLAGS) diff --git a/arch/mips/kernel/i8253.c b/arch/mips/kernel/i8253.c new file mode 100644 index 000000000000..475df6904219 --- /dev/null +++ b/arch/mips/kernel/i8253.c @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2006 IBM Corporation + * + * Implements device information for i8253 timer chip + * + * 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 + +static __init int add_pcspkr(void) +{ + struct platform_device *pd; + int ret; + + pd = platform_device_alloc("pcspkr", -1); + if (!pd) + return -ENOMEM; + + ret = platform_device_add(pd); + if (ret) + platform_device_put(pd); + + return ret; +} +device_initcall(add_pcspkr); diff --git a/arch/mips/kernel/mips_ksyms.c b/arch/mips/kernel/mips_ksyms.c index 86e42c633f73..e042f9d2ba31 100644 --- a/arch/mips/kernel/mips_ksyms.c +++ b/arch/mips/kernel/mips_ksyms.c @@ -39,7 +39,6 @@ EXPORT_SYMBOL(strchr); EXPORT_SYMBOL(strncmp); #endif EXPORT_SYMBOL(strlen); -EXPORT_SYMBOL(strpbrk); EXPORT_SYMBOL(strncat); EXPORT_SYMBOL(strnlen); EXPORT_SYMBOL(strrchr); diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index a8f435d82940..c66db5e5ab62 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -419,4 +419,3 @@ unsigned long get_wchan(struct task_struct *p) return pc; } -EXPORT_SYMBOL(get_wchan); diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index 6b3c50964ca9..19f911c5dd58 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -177,14 +177,15 @@ config ARCH_DISCONTIGMEM_DEFAULT def_bool y depends on ARCH_DISCONTIGMEM_ENABLE +config NODES_SHIFT + int + default "3" + depends on NEED_MULTIPLE_NODES + +source "kernel/Kconfig.preempt" source "kernel/Kconfig.hz" source "mm/Kconfig" -config PREEMPT - bool -# bool "Preemptible Kernel" - default n - config COMPAT def_bool y depends on 64BIT diff --git a/arch/parisc/configs/712_defconfig b/arch/parisc/configs/712_defconfig index 3e013f55df64..41fd0696bbe7 100644 --- a/arch/parisc/configs/712_defconfig +++ b/arch/parisc/configs/712_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.14-rc5-pa1 -# Fri Oct 21 23:04:34 2005 +# Linux kernel version: 2.6.16-pa6 +# Sun Mar 26 19:59:51 2006 # CONFIG_PARISC=y CONFIG_MMU=y @@ -10,14 +10,11 @@ CONFIG_RWSEM_GENERIC_SPINLOCK=y CONFIG_GENERIC_CALIBRATE_DELAY=y CONFIG_GENERIC_HARDIRQS=y CONFIG_GENERIC_IRQ_PROBE=y -CONFIG_ARCH_MAY_HAVE_PC_FDC=y # # Code maturity level options # CONFIG_EXPERIMENTAL=y -# CONFIG_CLEAN_COMPILE is not set -CONFIG_BROKEN=y CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 @@ -32,17 +29,18 @@ CONFIG_POSIX_MQUEUE=y # CONFIG_BSD_PROCESS_ACCT is not set CONFIG_SYSCTL=y # CONFIG_AUDIT is not set -CONFIG_HOTPLUG=y -CONFIG_KOBJECT_UEVENT=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_INITRAMFS_SOURCE="" +CONFIG_CC_OPTIMIZE_FOR_SIZE=y # CONFIG_EMBEDDED is not set CONFIG_KALLSYMS=y CONFIG_KALLSYMS_ALL=y # CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y CONFIG_PRINTK=y CONFIG_BUG=y +CONFIG_ELF_CORE=y CONFIG_BASE_FULL=y CONFIG_FUTEX=y CONFIG_EPOLL=y @@ -51,8 +49,10 @@ CONFIG_CC_ALIGN_FUNCTIONS=0 CONFIG_CC_ALIGN_LABELS=0 CONFIG_CC_ALIGN_LOOPS=0 CONFIG_CC_ALIGN_JUMPS=0 +CONFIG_SLAB=y # CONFIG_TINY_SHMEM is not set CONFIG_BASE_SMALL=0 +# CONFIG_SLOB is not set # # Loadable module support @@ -65,6 +65,23 @@ CONFIG_OBSOLETE_MODPARM=y # CONFIG_MODULE_SRCVERSION_ALL is not set CONFIG_KMOD=y +# +# Block layer +# + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_AS=y +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="anticipatory" + # # Processor type and features # @@ -75,6 +92,10 @@ CONFIG_PA7100LC=y # CONFIG_PA8X00 is not set CONFIG_PA11=y # CONFIG_SMP is not set +CONFIG_ARCH_FLATMEM_ENABLE=y +# CONFIG_PREEMPT_NONE is not set +CONFIG_PREEMPT_VOLUNTARY=y +# CONFIG_PREEMPT is not set # CONFIG_HZ_100 is not set CONFIG_HZ_250=y # CONFIG_HZ_1000 is not set @@ -86,7 +107,7 @@ CONFIG_FLATMEM_MANUAL=y CONFIG_FLATMEM=y CONFIG_FLAT_NODE_MEM_MAP=y # CONFIG_SPARSEMEM_STATIC is not set -# CONFIG_PREEMPT is not set +CONFIG_SPLIT_PTLOCK_CPUS=4096 # CONFIG_HPUX is not set # @@ -130,6 +151,7 @@ CONFIG_NET=y # # Networking options # +# CONFIG_NETDEBUG is not set CONFIG_PACKET=y CONFIG_PACKET_MMAP=y CONFIG_UNIX=y @@ -165,7 +187,12 @@ CONFIG_TCP_CONG_BIC=y # CONFIG_IPV6 is not set CONFIG_NETFILTER=y # CONFIG_NETFILTER_DEBUG is not set + +# +# Core Netfilter Configuration +# # CONFIG_NETFILTER_NETLINK is not set +# CONFIG_NETFILTER_XTABLES is not set # # IP: Netfilter Configuration @@ -182,64 +209,6 @@ CONFIG_IP_NF_TFTP=m CONFIG_IP_NF_AMANDA=m # CONFIG_IP_NF_PPTP is not set CONFIG_IP_NF_QUEUE=m -CONFIG_IP_NF_IPTABLES=m -CONFIG_IP_NF_MATCH_LIMIT=m -CONFIG_IP_NF_MATCH_IPRANGE=m -CONFIG_IP_NF_MATCH_MAC=m -CONFIG_IP_NF_MATCH_PKTTYPE=m -CONFIG_IP_NF_MATCH_MARK=m -CONFIG_IP_NF_MATCH_MULTIPORT=m -CONFIG_IP_NF_MATCH_TOS=m -CONFIG_IP_NF_MATCH_RECENT=m -CONFIG_IP_NF_MATCH_ECN=m -CONFIG_IP_NF_MATCH_DSCP=m -CONFIG_IP_NF_MATCH_AH_ESP=m -CONFIG_IP_NF_MATCH_LENGTH=m -CONFIG_IP_NF_MATCH_TTL=m -CONFIG_IP_NF_MATCH_TCPMSS=m -CONFIG_IP_NF_MATCH_HELPER=m -CONFIG_IP_NF_MATCH_STATE=m -CONFIG_IP_NF_MATCH_CONNTRACK=m -CONFIG_IP_NF_MATCH_OWNER=m -# CONFIG_IP_NF_MATCH_ADDRTYPE is not set -# CONFIG_IP_NF_MATCH_REALM is not set -CONFIG_IP_NF_MATCH_SCTP=m -# CONFIG_IP_NF_MATCH_DCCP is not set -CONFIG_IP_NF_MATCH_COMMENT=m -CONFIG_IP_NF_MATCH_CONNMARK=m -CONFIG_IP_NF_MATCH_HASHLIMIT=m -# CONFIG_IP_NF_MATCH_STRING is not set -CONFIG_IP_NF_FILTER=m -CONFIG_IP_NF_TARGET_REJECT=m -CONFIG_IP_NF_TARGET_LOG=m -CONFIG_IP_NF_TARGET_ULOG=m -CONFIG_IP_NF_TARGET_TCPMSS=m -# CONFIG_IP_NF_TARGET_NFQUEUE is not set -CONFIG_IP_NF_NAT=m -CONFIG_IP_NF_NAT_NEEDED=y -CONFIG_IP_NF_TARGET_MASQUERADE=m -CONFIG_IP_NF_TARGET_REDIRECT=m -CONFIG_IP_NF_TARGET_NETMAP=m -CONFIG_IP_NF_TARGET_SAME=m -CONFIG_IP_NF_NAT_SNMP_BASIC=m -CONFIG_IP_NF_NAT_IRC=m -CONFIG_IP_NF_NAT_FTP=m -CONFIG_IP_NF_NAT_TFTP=m -CONFIG_IP_NF_NAT_AMANDA=m -CONFIG_IP_NF_MANGLE=m -CONFIG_IP_NF_TARGET_TOS=m -CONFIG_IP_NF_TARGET_ECN=m -CONFIG_IP_NF_TARGET_DSCP=m -CONFIG_IP_NF_TARGET_MARK=m -CONFIG_IP_NF_TARGET_CLASSIFY=m -# CONFIG_IP_NF_TARGET_TTL is not set -CONFIG_IP_NF_TARGET_CONNMARK=m -CONFIG_IP_NF_TARGET_CLUSTERIP=m -CONFIG_IP_NF_RAW=m -CONFIG_IP_NF_TARGET_NOTRACK=m -CONFIG_IP_NF_ARPTABLES=m -CONFIG_IP_NF_ARPFILTER=m -CONFIG_IP_NF_ARP_MANGLE=m # # DCCP Configuration (EXPERIMENTAL) @@ -250,6 +219,11 @@ CONFIG_IP_NF_ARP_MANGLE=m # SCTP Configuration (EXPERIMENTAL) # # CONFIG_IP_SCTP is not set + +# +# TIPC Configuration (EXPERIMENTAL) +# +# CONFIG_TIPC is not set # CONFIG_ATM is not set # CONFIG_BRIDGE is not set # CONFIG_VLAN_8021Q is not set @@ -263,8 +237,11 @@ CONFIG_LLC2=m # CONFIG_NET_DIVERT is not set # CONFIG_ECONET is not set # CONFIG_WAN_ROUTER is not set + +# +# QoS and/or fair queueing +# # CONFIG_NET_SCHED is not set -# CONFIG_NET_CLS_ROUTE is not set # # Network testing @@ -304,6 +281,7 @@ CONFIG_PARPORT=y CONFIG_PARPORT_PC=m # CONFIG_PARPORT_PC_FIFO is not set # CONFIG_PARPORT_PC_SUPERIO is not set +CONFIG_PARPORT_NOT_PC=y CONFIG_PARPORT_GSC=y # CONFIG_PARPORT_1284 is not set @@ -314,7 +292,6 @@ CONFIG_PARPORT_GSC=y # # Block devices # -# CONFIG_BLK_DEV_FD is not set # CONFIG_PARIDE is not set # CONFIG_BLK_DEV_COW_COMMON is not set CONFIG_BLK_DEV_LOOP=y @@ -325,14 +302,6 @@ CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=6144 CONFIG_BLK_DEV_INITRD=y # CONFIG_CDROM_PKTCDVD is not set - -# -# IO Schedulers -# -CONFIG_IOSCHED_NOOP=y -CONFIG_IOSCHED_AS=y -CONFIG_IOSCHED_DEADLINE=y -CONFIG_IOSCHED_CFQ=y CONFIG_ATA_OVER_ETH=m # @@ -376,6 +345,7 @@ CONFIG_SCSI_ISCSI_ATTRS=m # # SCSI low-level drivers # +# CONFIG_ISCSI_TCP is not set # CONFIG_SCSI_SATA is not set # CONFIG_SCSI_PPA is not set # CONFIG_SCSI_IMM is not set @@ -407,7 +377,6 @@ CONFIG_MD_RAID1=m # # IEEE 1394 (FireWire) support # -# CONFIG_IEEE1394 is not set # # I2O device support @@ -471,6 +440,7 @@ CONFIG_PPP_ASYNC=m CONFIG_PPP_SYNC_TTY=m CONFIG_PPP_DEFLATE=m CONFIG_PPP_BSDCOMP=m +CONFIG_PPP_MPPE=m CONFIG_PPPOE=m # CONFIG_SLIP is not set # CONFIG_SHAPER is not set @@ -516,8 +486,8 @@ CONFIG_KEYBOARD_ATKBD_HP_KEYCODES=y # CONFIG_KEYBOARD_LKKBD is not set # CONFIG_KEYBOARD_XTKBD is not set # CONFIG_KEYBOARD_NEWTON is not set -CONFIG_KEYBOARD_HIL_OLD=y -# CONFIG_KEYBOARD_HIL is not set +# CONFIG_KEYBOARD_HIL_OLD is not set +CONFIG_KEYBOARD_HIL=y CONFIG_INPUT_MOUSE=y CONFIG_MOUSE_PS2=y CONFIG_MOUSE_SERIAL=m @@ -554,6 +524,7 @@ CONFIG_HW_CONSOLE=y CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y CONFIG_SERIAL_8250_NR_UARTS=17 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 CONFIG_SERIAL_8250_EXTENDED=y CONFIG_SERIAL_8250_MANY_PORTS=y CONFIG_SERIAL_8250_SHARE_IRQ=y @@ -598,12 +569,20 @@ CONFIG_MAX_RAW_DEVS=256 # # TPM devices # +# CONFIG_TCG_TPM is not set +# CONFIG_TELCLOCK is not set # # I2C support # # CONFIG_I2C is not set +# +# SPI support +# +# CONFIG_SPI is not set +# CONFIG_SPI_MASTER is not set + # # Dallas's 1-wire bus # @@ -640,7 +619,6 @@ CONFIG_FB=y CONFIG_FB_CFB_FILLRECT=y CONFIG_FB_CFB_COPYAREA=y CONFIG_FB_CFB_IMAGEBLIT=y -CONFIG_FB_SOFT_CURSOR=y # CONFIG_FB_MACMODES is not set CONFIG_FB_MODE_HELPERS=y CONFIG_FB_TILEBLITTING=y @@ -655,6 +633,7 @@ CONFIG_DUMMY_CONSOLE=y CONFIG_DUMMY_CONSOLE_COLUMNS=128 CONFIG_DUMMY_CONSOLE_ROWS=48 CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set CONFIG_STI_CONSOLE=y CONFIG_FONTS=y CONFIG_FONT_8x8=y @@ -695,6 +674,8 @@ CONFIG_SND_OSSEMUL=y CONFIG_SND_MIXER_OSS=y CONFIG_SND_PCM_OSS=y CONFIG_SND_SEQUENCER_OSS=y +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y # CONFIG_SND_VERBOSE_PRINTK is not set # CONFIG_SND_DEBUG is not set @@ -723,6 +704,10 @@ CONFIG_SND_HARMONY=y # CONFIG_USB_ARCH_HAS_HCD is not set # CONFIG_USB_ARCH_HAS_OHCI is not set +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' +# + # # USB Gadget Support # @@ -736,10 +721,9 @@ CONFIG_SND_HARMONY=y # # InfiniBand support # -# CONFIG_INFINIBAND is not set # -# SN Devices +# EDAC - error detection and reporting (RAS) (EXPERIMENTAL) # # @@ -765,6 +749,7 @@ CONFIG_XFS_EXPORT=y # CONFIG_XFS_SECURITY is not set # CONFIG_XFS_POSIX_ACL is not set # CONFIG_XFS_RT is not set +# CONFIG_OCFS2_FS is not set # CONFIG_MINIX_FS is not set # CONFIG_ROMFS_FS is not set CONFIG_INOTIFY=y @@ -800,10 +785,10 @@ CONFIG_PROC_FS=y CONFIG_PROC_KCORE=y CONFIG_SYSFS=y CONFIG_TMPFS=y -# CONFIG_HUGETLBFS is not set # CONFIG_HUGETLB_PAGE is not set CONFIG_RAMFS=y # CONFIG_RELAYFS_FS is not set +# CONFIG_CONFIGFS_FS is not set # # Miscellaneous filesystems @@ -821,7 +806,6 @@ CONFIG_RAMFS=y # CONFIG_QNX4FS_FS is not set # CONFIG_SYSV_FS is not set CONFIG_UFS_FS=m -# CONFIG_UFS_FS_WRITE is not set # # Network File Systems @@ -917,18 +901,22 @@ CONFIG_OPROFILE=m # Kernel hacking # # CONFIG_PRINTK_TIME is not set -CONFIG_DEBUG_KERNEL=y CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_KERNEL=y CONFIG_LOG_BUF_SHIFT=16 CONFIG_DETECT_SOFTLOCKUP=y # CONFIG_SCHEDSTATS is not set # CONFIG_DEBUG_SLAB is not set +CONFIG_DEBUG_MUTEXES=y # CONFIG_DEBUG_SPINLOCK is not set # CONFIG_DEBUG_SPINLOCK_SLEEP is not set # CONFIG_DEBUG_KOBJECT is not set # CONFIG_DEBUG_INFO is not set -# CONFIG_DEBUG_IOREMAP is not set # CONFIG_DEBUG_FS is not set +# CONFIG_DEBUG_VM is not set +CONFIG_FORCED_INLINING=y +# CONFIG_RCU_TORTURE_TEST is not set +CONFIG_DEBUG_RODATA=y # # Security options diff --git a/arch/parisc/configs/a500_defconfig b/arch/parisc/configs/a500_defconfig index 959ad3c4e372..f3b812f04592 100644 --- a/arch/parisc/configs/a500_defconfig +++ b/arch/parisc/configs/a500_defconfig @@ -1031,8 +1031,8 @@ CONFIG_NLS_CODEPAGE_850=m # CONFIG_NLS_ISO8859_8 is not set # CONFIG_NLS_CODEPAGE_1250 is not set # CONFIG_NLS_CODEPAGE_1251 is not set -# CONFIG_NLS_ASCII is not set -# CONFIG_NLS_ISO8859_1 is not set +CONFIG_NLS_ASCII=m +CONFIG_NLS_ISO8859_1=m # CONFIG_NLS_ISO8859_2 is not set # CONFIG_NLS_ISO8859_3 is not set # CONFIG_NLS_ISO8859_4 is not set diff --git a/arch/parisc/configs/b180_defconfig b/arch/parisc/configs/b180_defconfig index 37e98241ce4b..35093612ad2c 100644 --- a/arch/parisc/configs/b180_defconfig +++ b/arch/parisc/configs/b180_defconfig @@ -939,10 +939,10 @@ CONFIG_MSDOS_PARTITION=y # CONFIG_NLS=y CONFIG_NLS_DEFAULT="iso8859-1" -# CONFIG_NLS_CODEPAGE_437 is not set +CONFIG_NLS_CODEPAGE_437=m # CONFIG_NLS_CODEPAGE_737 is not set # CONFIG_NLS_CODEPAGE_775 is not set -# CONFIG_NLS_CODEPAGE_850 is not set +CONFIG_NLS_CODEPAGE_850=m # CONFIG_NLS_CODEPAGE_852 is not set # CONFIG_NLS_CODEPAGE_855 is not set # CONFIG_NLS_CODEPAGE_857 is not set @@ -962,8 +962,8 @@ CONFIG_NLS_DEFAULT="iso8859-1" # CONFIG_NLS_ISO8859_8 is not set # CONFIG_NLS_CODEPAGE_1250 is not set # CONFIG_NLS_CODEPAGE_1251 is not set -# CONFIG_NLS_ASCII is not set -# CONFIG_NLS_ISO8859_1 is not set +CONFIG_NLS_ASCII=m +CONFIG_NLS_ISO8859_1=m # CONFIG_NLS_ISO8859_2 is not set # CONFIG_NLS_ISO8859_3 is not set # CONFIG_NLS_ISO8859_4 is not set @@ -973,10 +973,10 @@ CONFIG_NLS_DEFAULT="iso8859-1" # CONFIG_NLS_ISO8859_9 is not set # CONFIG_NLS_ISO8859_13 is not set # CONFIG_NLS_ISO8859_14 is not set -# CONFIG_NLS_ISO8859_15 is not set +CONFIG_NLS_ISO8859_15=m # CONFIG_NLS_KOI8_R is not set # CONFIG_NLS_KOI8_U is not set -# CONFIG_NLS_UTF8 is not set +CONFIG_NLS_UTF8=m # # Kernel hacking diff --git a/arch/parisc/configs/c3000_defconfig b/arch/parisc/configs/c3000_defconfig index 0b1c8c1fa8a3..782906b644dd 100644 --- a/arch/parisc/configs/c3000_defconfig +++ b/arch/parisc/configs/c3000_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.14-rc5-pa1 -# Fri Oct 21 23:06:31 2005 +# Linux kernel version: 2.6.16-pa6 +# Sun Mar 26 20:03:29 2006 # CONFIG_PARISC=y CONFIG_MMU=y @@ -10,14 +10,11 @@ CONFIG_RWSEM_GENERIC_SPINLOCK=y CONFIG_GENERIC_CALIBRATE_DELAY=y CONFIG_GENERIC_HARDIRQS=y CONFIG_GENERIC_IRQ_PROBE=y -CONFIG_ARCH_MAY_HAVE_PC_FDC=y # # Code maturity level options # CONFIG_EXPERIMENTAL=y -# CONFIG_CLEAN_COMPILE is not set -CONFIG_BROKEN=y CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 @@ -32,28 +29,30 @@ CONFIG_SYSVIPC=y # CONFIG_BSD_PROCESS_ACCT is not set CONFIG_SYSCTL=y # CONFIG_AUDIT is not set -CONFIG_HOTPLUG=y -CONFIG_KOBJECT_UEVENT=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_INITRAMFS_SOURCE="" +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set CONFIG_EMBEDDED=y CONFIG_KALLSYMS=y CONFIG_KALLSYMS_ALL=y # CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y CONFIG_PRINTK=y CONFIG_BUG=y +CONFIG_ELF_CORE=y CONFIG_BASE_FULL=y CONFIG_FUTEX=y CONFIG_EPOLL=y -# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set CONFIG_SHMEM=y CONFIG_CC_ALIGN_FUNCTIONS=0 CONFIG_CC_ALIGN_LABELS=0 CONFIG_CC_ALIGN_LOOPS=0 CONFIG_CC_ALIGN_JUMPS=0 +CONFIG_SLAB=y # CONFIG_TINY_SHMEM is not set CONFIG_BASE_SMALL=0 +# CONFIG_SLOB is not set # # Loadable module support @@ -66,6 +65,23 @@ CONFIG_OBSOLETE_MODPARM=y # CONFIG_MODULE_SRCVERSION_ALL is not set CONFIG_KMOD=y +# +# Block layer +# + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_AS=y +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="anticipatory" + # # Processor type and features # @@ -78,6 +94,10 @@ CONFIG_PA20=y CONFIG_PREFETCH=y # CONFIG_64BIT is not set # CONFIG_SMP is not set +CONFIG_ARCH_FLATMEM_ENABLE=y +# CONFIG_PREEMPT_NONE is not set +CONFIG_PREEMPT_VOLUNTARY=y +# CONFIG_PREEMPT is not set # CONFIG_HZ_100 is not set CONFIG_HZ_250=y # CONFIG_HZ_1000 is not set @@ -89,7 +109,7 @@ CONFIG_FLATMEM_MANUAL=y CONFIG_FLATMEM=y CONFIG_FLAT_NODE_MEM_MAP=y # CONFIG_SPARSEMEM_STATIC is not set -# CONFIG_PREEMPT is not set +CONFIG_SPLIT_PTLOCK_CPUS=4 # CONFIG_HPUX is not set # @@ -135,6 +155,7 @@ CONFIG_NET=y # # Networking options # +# CONFIG_NETDEBUG is not set CONFIG_PACKET=y CONFIG_PACKET_MMAP=y CONFIG_UNIX=y @@ -175,7 +196,12 @@ CONFIG_INET6_TUNNEL=m CONFIG_IPV6_TUNNEL=m CONFIG_NETFILTER=y CONFIG_NETFILTER_DEBUG=y + +# +# Core Netfilter Configuration +# # CONFIG_NETFILTER_NETLINK is not set +# CONFIG_NETFILTER_XTABLES is not set # # IP: Netfilter Configuration @@ -192,87 +218,11 @@ CONFIG_IP_NF_TFTP=m CONFIG_IP_NF_AMANDA=m # CONFIG_IP_NF_PPTP is not set CONFIG_IP_NF_QUEUE=m -CONFIG_IP_NF_IPTABLES=m -CONFIG_IP_NF_MATCH_LIMIT=m -CONFIG_IP_NF_MATCH_IPRANGE=m -CONFIG_IP_NF_MATCH_MAC=m -CONFIG_IP_NF_MATCH_PKTTYPE=m -CONFIG_IP_NF_MATCH_MARK=m -CONFIG_IP_NF_MATCH_MULTIPORT=m -CONFIG_IP_NF_MATCH_TOS=m -CONFIG_IP_NF_MATCH_RECENT=m -CONFIG_IP_NF_MATCH_ECN=m -CONFIG_IP_NF_MATCH_DSCP=m -CONFIG_IP_NF_MATCH_AH_ESP=m -CONFIG_IP_NF_MATCH_LENGTH=m -CONFIG_IP_NF_MATCH_TTL=m -CONFIG_IP_NF_MATCH_TCPMSS=m -CONFIG_IP_NF_MATCH_HELPER=m -CONFIG_IP_NF_MATCH_STATE=m -CONFIG_IP_NF_MATCH_CONNTRACK=m -CONFIG_IP_NF_MATCH_OWNER=m -# CONFIG_IP_NF_MATCH_ADDRTYPE is not set -# CONFIG_IP_NF_MATCH_REALM is not set -# CONFIG_IP_NF_MATCH_SCTP is not set -# CONFIG_IP_NF_MATCH_DCCP is not set -# CONFIG_IP_NF_MATCH_COMMENT is not set -# CONFIG_IP_NF_MATCH_HASHLIMIT is not set -# CONFIG_IP_NF_MATCH_STRING is not set -CONFIG_IP_NF_FILTER=m -CONFIG_IP_NF_TARGET_REJECT=m -CONFIG_IP_NF_TARGET_LOG=m -CONFIG_IP_NF_TARGET_ULOG=m -CONFIG_IP_NF_TARGET_TCPMSS=m -# CONFIG_IP_NF_TARGET_NFQUEUE is not set -CONFIG_IP_NF_NAT=m -CONFIG_IP_NF_NAT_NEEDED=y -CONFIG_IP_NF_TARGET_MASQUERADE=m -CONFIG_IP_NF_TARGET_REDIRECT=m -CONFIG_IP_NF_TARGET_NETMAP=m -CONFIG_IP_NF_TARGET_SAME=m -CONFIG_IP_NF_NAT_SNMP_BASIC=m -CONFIG_IP_NF_NAT_IRC=m -CONFIG_IP_NF_NAT_FTP=m -CONFIG_IP_NF_NAT_TFTP=m -CONFIG_IP_NF_NAT_AMANDA=m -CONFIG_IP_NF_MANGLE=m -CONFIG_IP_NF_TARGET_TOS=m -CONFIG_IP_NF_TARGET_ECN=m -CONFIG_IP_NF_TARGET_DSCP=m -CONFIG_IP_NF_TARGET_MARK=m -CONFIG_IP_NF_TARGET_CLASSIFY=m -# CONFIG_IP_NF_TARGET_TTL is not set -# CONFIG_IP_NF_RAW is not set -CONFIG_IP_NF_ARPTABLES=m -CONFIG_IP_NF_ARPFILTER=m -CONFIG_IP_NF_ARP_MANGLE=m # # IPv6: Netfilter Configuration (EXPERIMENTAL) # # CONFIG_IP6_NF_QUEUE is not set -CONFIG_IP6_NF_IPTABLES=m -# CONFIG_IP6_NF_MATCH_LIMIT is not set -CONFIG_IP6_NF_MATCH_MAC=m -CONFIG_IP6_NF_MATCH_RT=m -# CONFIG_IP6_NF_MATCH_OPTS is not set -# CONFIG_IP6_NF_MATCH_FRAG is not set -# CONFIG_IP6_NF_MATCH_HL is not set -# CONFIG_IP6_NF_MATCH_MULTIPORT is not set -CONFIG_IP6_NF_MATCH_OWNER=m -# CONFIG_IP6_NF_MATCH_MARK is not set -CONFIG_IP6_NF_MATCH_IPV6HEADER=m -# CONFIG_IP6_NF_MATCH_AHESP is not set -CONFIG_IP6_NF_MATCH_LENGTH=m -# CONFIG_IP6_NF_MATCH_EUI64 is not set -CONFIG_IP6_NF_FILTER=m -CONFIG_IP6_NF_TARGET_LOG=m -CONFIG_IP6_NF_TARGET_REJECT=m -# CONFIG_IP6_NF_TARGET_NFQUEUE is not set -CONFIG_IP6_NF_MANGLE=m -# CONFIG_IP6_NF_TARGET_MARK is not set -# CONFIG_IP6_NF_TARGET_HL is not set -# CONFIG_IP6_NF_RAW is not set # # DCCP Configuration (EXPERIMENTAL) @@ -283,6 +233,11 @@ CONFIG_IP6_NF_MANGLE=m # SCTP Configuration (EXPERIMENTAL) # # CONFIG_IP_SCTP is not set + +# +# TIPC Configuration (EXPERIMENTAL) +# +# CONFIG_TIPC is not set # CONFIG_ATM is not set # CONFIG_BRIDGE is not set # CONFIG_VLAN_8021Q is not set @@ -295,8 +250,11 @@ CONFIG_IP6_NF_MANGLE=m # CONFIG_NET_DIVERT is not set # CONFIG_ECONET is not set # CONFIG_WAN_ROUTER is not set + +# +# QoS and/or fair queueing +# # CONFIG_NET_SCHED is not set -# CONFIG_NET_CLS_ROUTE is not set # # Network testing @@ -341,7 +299,6 @@ CONFIG_FW_LOADER=y # # Block devices # -# CONFIG_BLK_DEV_FD is not set # CONFIG_BLK_CPQ_DA is not set # CONFIG_BLK_CPQ_CISS_DA is not set # CONFIG_BLK_DEV_DAC960 is not set @@ -355,14 +312,6 @@ CONFIG_BLK_DEV_CRYPTOLOOP=m # CONFIG_BLK_DEV_RAM is not set CONFIG_BLK_DEV_RAM_COUNT=16 # CONFIG_CDROM_PKTCDVD is not set - -# -# IO Schedulers -# -CONFIG_IOSCHED_NOOP=y -CONFIG_IOSCHED_AS=y -CONFIG_IOSCHED_DEADLINE=y -CONFIG_IOSCHED_CFQ=y # CONFIG_ATA_OVER_ETH is not set # @@ -458,6 +407,7 @@ CONFIG_SCSI_ISCSI_ATTRS=m # # SCSI low-level drivers # +# CONFIG_ISCSI_TCP is not set # CONFIG_BLK_DEV_3W_XXXX_RAID is not set # CONFIG_SCSI_3W_9XXX is not set # CONFIG_SCSI_ACARD is not set @@ -466,7 +416,6 @@ CONFIG_SCSI_ISCSI_ATTRS=m # CONFIG_SCSI_AIC7XXX_OLD is not set # CONFIG_SCSI_AIC79XX is not set # CONFIG_SCSI_DPT_I2O is not set -# CONFIG_SCSI_ADVANSYS is not set # CONFIG_MEGARAID_NEWGEN is not set # CONFIG_MEGARAID_LEGACY is not set # CONFIG_MEGARAID_SAS is not set @@ -476,18 +425,18 @@ CONFIG_SCSI_SATA=y CONFIG_SCSI_ATA_PIIX=m # CONFIG_SCSI_SATA_MV is not set # CONFIG_SCSI_SATA_NV is not set -CONFIG_SCSI_SATA_PROMISE=m +# CONFIG_SCSI_PDC_ADMA is not set # CONFIG_SCSI_SATA_QSTOR is not set +CONFIG_SCSI_SATA_PROMISE=m # CONFIG_SCSI_SATA_SX4 is not set CONFIG_SCSI_SATA_SIL=m +# CONFIG_SCSI_SATA_SIL24 is not set # CONFIG_SCSI_SATA_SIS is not set # CONFIG_SCSI_SATA_ULI is not set CONFIG_SCSI_SATA_VIA=m # CONFIG_SCSI_SATA_VITESSE is not set CONFIG_SCSI_SATA_INTEL_COMBINED=y -# CONFIG_SCSI_CPQFCTS is not set # CONFIG_SCSI_DMX3191D is not set -# CONFIG_SCSI_EATA_PIO is not set # CONFIG_SCSI_FUTURE_DOMAIN is not set # CONFIG_SCSI_IPS is not set # CONFIG_SCSI_INITIO is not set @@ -496,18 +445,11 @@ CONFIG_SCSI_SYM53C8XX_2=y CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0 CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16 CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64 -# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set +CONFIG_SCSI_SYM53C8XX_MMIO=y # CONFIG_SCSI_IPR is not set -# CONFIG_SCSI_QLOGIC_ISP is not set # CONFIG_SCSI_QLOGIC_FC is not set # CONFIG_SCSI_QLOGIC_1280 is not set -CONFIG_SCSI_QLA2XXX=y -# CONFIG_SCSI_QLA21XX is not set -# CONFIG_SCSI_QLA22XX is not set -# CONFIG_SCSI_QLA2300 is not set -# CONFIG_SCSI_QLA2322 is not set -# CONFIG_SCSI_QLA6312 is not set -# CONFIG_SCSI_QLA24XX is not set +# CONFIG_SCSI_QLA_FC is not set # CONFIG_SCSI_LPFC is not set # CONFIG_SCSI_DC395x is not set # CONFIG_SCSI_DC390T is not set @@ -633,6 +575,7 @@ CONFIG_E1000=m # CONFIG_R8169 is not set # CONFIG_SIS190 is not set # CONFIG_SKGE is not set +# CONFIG_SKY2 is not set # CONFIG_SK98LIN is not set # CONFIG_VIA_VELOCITY is not set CONFIG_TIGON3=m @@ -668,6 +611,7 @@ CONFIG_PPP_ASYNC=m CONFIG_PPP_SYNC_TTY=m CONFIG_PPP_DEFLATE=m CONFIG_PPP_BSDCOMP=m +# CONFIG_PPP_MPPE is not set CONFIG_PPPOE=m # CONFIG_SLIP is not set # CONFIG_NET_FC is not set @@ -744,6 +688,7 @@ CONFIG_HW_CONSOLE=y CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y CONFIG_SERIAL_8250_NR_UARTS=13 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 CONFIG_SERIAL_8250_EXTENDED=y CONFIG_SERIAL_8250_MANY_PORTS=y CONFIG_SERIAL_8250_SHARE_IRQ=y @@ -753,7 +698,6 @@ CONFIG_SERIAL_8250_SHARE_IRQ=y # # Non-8250 serial port support # -# CONFIG_SERIAL_MUX is not set # CONFIG_PDC_CONSOLE is not set CONFIG_SERIAL_CORE=y CONFIG_SERIAL_CORE_CONSOLE=y @@ -788,12 +732,19 @@ CONFIG_MAX_RAW_DEVS=256 # TPM devices # # CONFIG_TCG_TPM is not set +# CONFIG_TELCLOCK is not set # # I2C support # # CONFIG_I2C is not set +# +# SPI support +# +# CONFIG_SPI is not set +# CONFIG_SPI_MASTER is not set + # # Dallas's 1-wire bus # @@ -830,7 +781,6 @@ CONFIG_FB=y CONFIG_FB_CFB_FILLRECT=y CONFIG_FB_CFB_COPYAREA=y CONFIG_FB_CFB_IMAGEBLIT=y -CONFIG_FB_SOFT_CURSOR=y # CONFIG_FB_MACMODES is not set # CONFIG_FB_MODE_HELPERS is not set # CONFIG_FB_TILEBLITTING is not set @@ -840,6 +790,7 @@ CONFIG_FB_SOFT_CURSOR=y # CONFIG_FB_ASILIANT is not set # CONFIG_FB_IMSTT is not set CONFIG_FB_STI=y +# CONFIG_FB_S1D13XXX is not set # CONFIG_FB_NVIDIA is not set # CONFIG_FB_RIVA is not set # CONFIG_FB_MATROX is not set @@ -853,10 +804,7 @@ CONFIG_FB_STI=y # CONFIG_FB_KYRO is not set # CONFIG_FB_3DFX is not set # CONFIG_FB_VOODOO1 is not set -# CONFIG_FB_CYBLA is not set # CONFIG_FB_TRIDENT is not set -# CONFIG_FB_PM3 is not set -# CONFIG_FB_S1D13XXX is not set # CONFIG_FB_VIRTUAL is not set # @@ -866,6 +814,7 @@ CONFIG_DUMMY_CONSOLE=y CONFIG_DUMMY_CONSOLE_COLUMNS=160 CONFIG_DUMMY_CONSOLE_ROWS=64 CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set CONFIG_STI_CONSOLE=y # CONFIG_FONTS is not set CONFIG_FONT_8x8=y @@ -898,23 +847,27 @@ CONFIG_SND_OSSEMUL=y CONFIG_SND_MIXER_OSS=y CONFIG_SND_PCM_OSS=y CONFIG_SND_SEQUENCER_OSS=y +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y # CONFIG_SND_VERBOSE_PRINTK is not set # CONFIG_SND_DEBUG is not set # # Generic devices # +CONFIG_SND_AC97_CODEC=y +CONFIG_SND_AC97_BUS=y # CONFIG_SND_DUMMY is not set # CONFIG_SND_VIRMIDI is not set # CONFIG_SND_MTPAV is not set # CONFIG_SND_SERIAL_U16550 is not set # CONFIG_SND_MPU401 is not set -CONFIG_SND_AC97_CODEC=y -CONFIG_SND_AC97_BUS=y # # PCI devices # +CONFIG_SND_AD1889=y +# CONFIG_SND_AD1889_OPL3 is not set # CONFIG_SND_ALI5451 is not set # CONFIG_SND_ATIIXP is not set # CONFIG_SND_ATIIXP_MODEM is not set @@ -923,39 +876,38 @@ CONFIG_SND_AC97_BUS=y # CONFIG_SND_AU8830 is not set # CONFIG_SND_AZT3328 is not set # CONFIG_SND_BT87X is not set -# CONFIG_SND_CS46XX is not set +# CONFIG_SND_CA0106 is not set +# CONFIG_SND_CMIPCI is not set # CONFIG_SND_CS4281 is not set +# CONFIG_SND_CS46XX is not set # CONFIG_SND_EMU10K1 is not set # CONFIG_SND_EMU10K1X is not set -# CONFIG_SND_CA0106 is not set -# CONFIG_SND_KORG1212 is not set -# CONFIG_SND_MIXART is not set -# CONFIG_SND_NM256 is not set -# CONFIG_SND_RME32 is not set -# CONFIG_SND_RME96 is not set -# CONFIG_SND_RME9652 is not set -# CONFIG_SND_HDSP is not set -# CONFIG_SND_HDSPM is not set -# CONFIG_SND_TRIDENT is not set -# CONFIG_SND_YMFPCI is not set -CONFIG_SND_AD1889=y -# CONFIG_SND_AD1889_OPL3 is not set -# CONFIG_SND_CMIPCI is not set # CONFIG_SND_ENS1370 is not set # CONFIG_SND_ENS1371 is not set # CONFIG_SND_ES1938 is not set # CONFIG_SND_ES1968 is not set -# CONFIG_SND_MAESTRO3 is not set # CONFIG_SND_FM801 is not set +# CONFIG_SND_HDA_INTEL is not set +# CONFIG_SND_HDSP is not set +# CONFIG_SND_HDSPM is not set # CONFIG_SND_ICE1712 is not set # CONFIG_SND_ICE1724 is not set # CONFIG_SND_INTEL8X0 is not set # CONFIG_SND_INTEL8X0M is not set +# CONFIG_SND_KORG1212 is not set +# CONFIG_SND_MAESTRO3 is not set +# CONFIG_SND_MIXART is not set +# CONFIG_SND_NM256 is not set +# CONFIG_SND_PCXHR is not set +# CONFIG_SND_RME32 is not set +# CONFIG_SND_RME96 is not set +# CONFIG_SND_RME9652 is not set # CONFIG_SND_SONICVIBES is not set +# CONFIG_SND_TRIDENT is not set # CONFIG_SND_VIA82XX is not set # CONFIG_SND_VIA82XX_MODEM is not set # CONFIG_SND_VX222 is not set -# CONFIG_SND_HDA_INTEL is not set +# CONFIG_SND_YMFPCI is not set # # USB devices @@ -998,12 +950,15 @@ CONFIG_USB_OHCI_LITTLE_ENDIAN=y # USB Device Class drivers # # CONFIG_OBSOLETE_OSS_USB_DRIVER is not set -# CONFIG_USB_BLUETOOTH_TTY is not set # CONFIG_USB_ACM is not set CONFIG_USB_PRINTER=m # -# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' +# + +# +# may also be needed; see USB_STORAGE Help for more information # CONFIG_USB_STORAGE=m # CONFIG_USB_STORAGE_DEBUG is not set @@ -1015,12 +970,15 @@ CONFIG_USB_STORAGE_USBAT=y CONFIG_USB_STORAGE_SDDR09=y CONFIG_USB_STORAGE_SDDR55=y CONFIG_USB_STORAGE_JUMPSHOT=y +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_LIBUSUAL is not set # # USB Input Devices # CONFIG_USB_HID=y CONFIG_USB_HIDINPUT=y +# CONFIG_USB_HIDINPUT_POWERBOOK is not set # CONFIG_HID_FF is not set CONFIG_USB_HIDDEV=y # CONFIG_USB_AIPTEK is not set @@ -1034,6 +992,7 @@ CONFIG_USB_HIDDEV=y # CONFIG_USB_YEALINK is not set # CONFIG_USB_XPAD is not set # CONFIG_USB_ATI_REMOTE is not set +# CONFIG_USB_ATI_REMOTE2 is not set # CONFIG_USB_KEYSPAN_REMOTE is not set # CONFIG_USB_APPLETOUCH is not set @@ -1108,7 +1067,7 @@ CONFIG_USB_LEGOTOWER=m # CONFIG_INFINIBAND is not set # -# SN Devices +# EDAC - error detection and reporting (RAS) (EXPERIMENTAL) # # @@ -1130,6 +1089,7 @@ CONFIG_XFS_EXPORT=y # CONFIG_XFS_SECURITY is not set # CONFIG_XFS_POSIX_ACL is not set # CONFIG_XFS_RT is not set +# CONFIG_OCFS2_FS is not set # CONFIG_MINIX_FS is not set # CONFIG_ROMFS_FS is not set CONFIG_INOTIFY=y @@ -1164,10 +1124,10 @@ CONFIG_PROC_FS=y CONFIG_PROC_KCORE=y CONFIG_SYSFS=y CONFIG_TMPFS=y -# CONFIG_HUGETLBFS is not set # CONFIG_HUGETLB_PAGE is not set CONFIG_RAMFS=y # CONFIG_RELAYFS_FS is not set +# CONFIG_CONFIGFS_FS is not set # # Miscellaneous filesystems @@ -1225,10 +1185,10 @@ CONFIG_MSDOS_PARTITION=y # CONFIG_NLS=y CONFIG_NLS_DEFAULT="iso8859-1" -# CONFIG_NLS_CODEPAGE_437 is not set +CONFIG_NLS_CODEPAGE_437=m # CONFIG_NLS_CODEPAGE_737 is not set # CONFIG_NLS_CODEPAGE_775 is not set -# CONFIG_NLS_CODEPAGE_850 is not set +CONFIG_NLS_CODEPAGE_850=m # CONFIG_NLS_CODEPAGE_852 is not set # CONFIG_NLS_CODEPAGE_855 is not set # CONFIG_NLS_CODEPAGE_857 is not set @@ -1248,8 +1208,8 @@ CONFIG_NLS_DEFAULT="iso8859-1" # CONFIG_NLS_ISO8859_8 is not set # CONFIG_NLS_CODEPAGE_1250 is not set # CONFIG_NLS_CODEPAGE_1251 is not set -# CONFIG_NLS_ASCII is not set -# CONFIG_NLS_ISO8859_1 is not set +CONFIG_NLS_ASCII=m +CONFIG_NLS_ISO8859_1=m # CONFIG_NLS_ISO8859_2 is not set # CONFIG_NLS_ISO8859_3 is not set # CONFIG_NLS_ISO8859_4 is not set @@ -1259,10 +1219,10 @@ CONFIG_NLS_DEFAULT="iso8859-1" # CONFIG_NLS_ISO8859_9 is not set # CONFIG_NLS_ISO8859_13 is not set # CONFIG_NLS_ISO8859_14 is not set -# CONFIG_NLS_ISO8859_15 is not set +CONFIG_NLS_ISO8859_15=m # CONFIG_NLS_KOI8_R is not set # CONFIG_NLS_KOI8_U is not set -# CONFIG_NLS_UTF8 is not set +CONFIG_NLS_UTF8=m # # Profiling support @@ -1274,18 +1234,22 @@ CONFIG_OPROFILE=m # Kernel hacking # # CONFIG_PRINTK_TIME is not set -CONFIG_DEBUG_KERNEL=y CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_KERNEL=y CONFIG_LOG_BUF_SHIFT=16 CONFIG_DETECT_SOFTLOCKUP=y # CONFIG_SCHEDSTATS is not set # CONFIG_DEBUG_SLAB is not set +CONFIG_DEBUG_MUTEXES=y # CONFIG_DEBUG_SPINLOCK is not set # CONFIG_DEBUG_SPINLOCK_SLEEP is not set # CONFIG_DEBUG_KOBJECT is not set # CONFIG_DEBUG_INFO is not set -# CONFIG_DEBUG_IOREMAP is not set # CONFIG_DEBUG_FS is not set +# CONFIG_DEBUG_VM is not set +CONFIG_FORCED_INLINING=y +# CONFIG_RCU_TORTURE_TEST is not set +CONFIG_DEBUG_RODATA=y # # Security options diff --git a/arch/parisc/defconfig b/arch/parisc/defconfig index f38a4620d24f..59f7bc38e72e 100644 --- a/arch/parisc/defconfig +++ b/arch/parisc/defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.14-rc5-pa1 -# Fri Oct 21 23:01:33 2005 +# Linux kernel version: 2.6.16-pa6 +# Sun Mar 26 19:50:07 2006 # CONFIG_PARISC=y CONFIG_MMU=y @@ -15,7 +15,6 @@ CONFIG_GENERIC_IRQ_PROBE=y # Code maturity level options # CONFIG_EXPERIMENTAL=y -CONFIG_CLEAN_COMPILE=y CONFIG_BROKEN_ON_SMP=y CONFIG_INIT_ENV_ARG_LIMIT=32 @@ -30,17 +29,18 @@ CONFIG_SYSVIPC=y # CONFIG_BSD_PROCESS_ACCT is not set CONFIG_SYSCTL=y # CONFIG_AUDIT is not set -# CONFIG_HOTPLUG is not set -CONFIG_KOBJECT_UEVENT=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_INITRAMFS_SOURCE="" +CONFIG_CC_OPTIMIZE_FOR_SIZE=y # CONFIG_EMBEDDED is not set CONFIG_KALLSYMS=y # CONFIG_KALLSYMS_ALL is not set # CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y CONFIG_PRINTK=y CONFIG_BUG=y +CONFIG_ELF_CORE=y CONFIG_BASE_FULL=y CONFIG_FUTEX=y CONFIG_EPOLL=y @@ -49,14 +49,33 @@ CONFIG_CC_ALIGN_FUNCTIONS=0 CONFIG_CC_ALIGN_LABELS=0 CONFIG_CC_ALIGN_LOOPS=0 CONFIG_CC_ALIGN_JUMPS=0 +CONFIG_SLAB=y # CONFIG_TINY_SHMEM is not set CONFIG_BASE_SMALL=0 +# CONFIG_SLOB is not set # # Loadable module support # # CONFIG_MODULES is not set +# +# Block layer +# + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_AS=y +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="anticipatory" + # # Processor type and features # @@ -67,6 +86,10 @@ CONFIG_PA7000=y # CONFIG_PA8X00 is not set CONFIG_PA11=y # CONFIG_SMP is not set +CONFIG_ARCH_FLATMEM_ENABLE=y +CONFIG_PREEMPT_NONE=y +# CONFIG_PREEMPT_VOLUNTARY is not set +# CONFIG_PREEMPT is not set # CONFIG_HZ_100 is not set CONFIG_HZ_250=y # CONFIG_HZ_1000 is not set @@ -78,7 +101,7 @@ CONFIG_FLATMEM_MANUAL=y CONFIG_FLATMEM=y CONFIG_FLAT_NODE_MEM_MAP=y # CONFIG_SPARSEMEM_STATIC is not set -# CONFIG_PREEMPT is not set +CONFIG_SPLIT_PTLOCK_CPUS=4096 # CONFIG_HPUX is not set # @@ -132,6 +155,7 @@ CONFIG_NET=y # # Networking options # +# CONFIG_NETDEBUG is not set CONFIG_PACKET=y CONFIG_PACKET_MMAP=y CONFIG_UNIX=y @@ -174,6 +198,11 @@ CONFIG_IPV6=y # SCTP Configuration (EXPERIMENTAL) # # CONFIG_IP_SCTP is not set + +# +# TIPC Configuration (EXPERIMENTAL) +# +# CONFIG_TIPC is not set # CONFIG_ATM is not set # CONFIG_BRIDGE is not set # CONFIG_VLAN_8021Q is not set @@ -186,8 +215,11 @@ CONFIG_IPV6=y # CONFIG_NET_DIVERT is not set # CONFIG_ECONET is not set # CONFIG_WAN_ROUTER is not set + +# +# QoS and/or fair queueing +# # CONFIG_NET_SCHED is not set -# CONFIG_NET_CLS_ROUTE is not set # # Network testing @@ -228,6 +260,7 @@ CONFIG_PARPORT_PC=y # CONFIG_PARPORT_SERIAL is not set # CONFIG_PARPORT_PC_FIFO is not set # CONFIG_PARPORT_PC_SUPERIO is not set +CONFIG_PARPORT_NOT_PC=y CONFIG_PARPORT_GSC=y # CONFIG_PARPORT_1284 is not set @@ -254,14 +287,6 @@ CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=4096 CONFIG_BLK_DEV_INITRD=y # CONFIG_CDROM_PKTCDVD is not set - -# -# IO Schedulers -# -CONFIG_IOSCHED_NOOP=y -CONFIG_IOSCHED_AS=y -CONFIG_IOSCHED_DEADLINE=y -CONFIG_IOSCHED_CFQ=y # CONFIG_ATA_OVER_ETH is not set # @@ -305,6 +330,7 @@ CONFIG_SCSI_SPI_ATTRS=y # # SCSI low-level drivers # +# CONFIG_ISCSI_TCP is not set # CONFIG_BLK_DEV_3W_XXXX_RAID is not set # CONFIG_SCSI_3W_9XXX is not set # CONFIG_SCSI_ACARD is not set @@ -331,7 +357,7 @@ CONFIG_SCSI_SYM53C8XX_2=y CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=1 CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16 CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64 -# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set +CONFIG_SCSI_SYM53C8XX_MMIO=y # CONFIG_SCSI_IPR is not set CONFIG_SCSI_ZALON=y CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8 @@ -340,13 +366,7 @@ CONFIG_SCSI_NCR53C8XX_SYNC=20 # CONFIG_SCSI_NCR53C8XX_PROFILE is not set # CONFIG_SCSI_QLOGIC_FC is not set # CONFIG_SCSI_QLOGIC_1280 is not set -CONFIG_SCSI_QLA2XXX=y -# CONFIG_SCSI_QLA21XX is not set -# CONFIG_SCSI_QLA22XX is not set -# CONFIG_SCSI_QLA2300 is not set -# CONFIG_SCSI_QLA2322 is not set -# CONFIG_SCSI_QLA6312 is not set -# CONFIG_SCSI_QLA24XX is not set +# CONFIG_SCSI_QLA_FC is not set # CONFIG_SCSI_LPFC is not set # CONFIG_SCSI_SIM710 is not set # CONFIG_SCSI_DC395x is not set @@ -471,6 +491,7 @@ CONFIG_ACENIC=y # CONFIG_R8169 is not set # CONFIG_SIS190 is not set # CONFIG_SKGE is not set +# CONFIG_SKY2 is not set # CONFIG_SK98LIN is not set # CONFIG_VIA_VELOCITY is not set CONFIG_TIGON3=y @@ -562,13 +583,13 @@ CONFIG_INPUT_KEYBOARD=y # CONFIG_KEYBOARD_LKKBD is not set # CONFIG_KEYBOARD_XTKBD is not set # CONFIG_KEYBOARD_NEWTON is not set -CONFIG_KEYBOARD_HIL_OLD=y +# CONFIG_KEYBOARD_HIL_OLD is not set CONFIG_KEYBOARD_HIL=y CONFIG_INPUT_MOUSE=y # CONFIG_MOUSE_PS2 is not set # CONFIG_MOUSE_SERIAL is not set # CONFIG_MOUSE_VSXXXAA is not set -# CONFIG_MOUSE_HIL is not set +CONFIG_MOUSE_HIL=y CONFIG_INPUT_JOYSTICK=y # CONFIG_JOYSTICK_ANALOG is not set # CONFIG_JOYSTICK_A3D is not set @@ -628,6 +649,7 @@ CONFIG_HW_CONSOLE=y CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y CONFIG_SERIAL_8250_NR_UARTS=13 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 CONFIG_SERIAL_8250_EXTENDED=y CONFIG_SERIAL_8250_MANY_PORTS=y CONFIG_SERIAL_8250_SHARE_IRQ=y @@ -675,12 +697,19 @@ CONFIG_GEN_RTC=y # TPM devices # # CONFIG_TCG_TPM is not set +# CONFIG_TELCLOCK is not set # # I2C support # # CONFIG_I2C is not set +# +# SPI support +# +# CONFIG_SPI is not set +# CONFIG_SPI_MASTER is not set + # # Dallas's 1-wire bus # @@ -691,6 +720,7 @@ CONFIG_GEN_RTC=y # CONFIG_HWMON=y # CONFIG_HWMON_VID is not set +# CONFIG_SENSORS_F71805F is not set # CONFIG_HWMON_DEBUG_CHIP is not set # @@ -718,7 +748,6 @@ CONFIG_FB=y CONFIG_FB_CFB_FILLRECT=y CONFIG_FB_CFB_COPYAREA=y CONFIG_FB_CFB_IMAGEBLIT=y -CONFIG_FB_SOFT_CURSOR=y # CONFIG_FB_MACMODES is not set # CONFIG_FB_MODE_HELPERS is not set # CONFIG_FB_TILEBLITTING is not set @@ -728,6 +757,7 @@ CONFIG_FB_SOFT_CURSOR=y # CONFIG_FB_ASILIANT is not set # CONFIG_FB_IMSTT is not set CONFIG_FB_STI=y +# CONFIG_FB_S1D13XXX is not set # CONFIG_FB_NVIDIA is not set # CONFIG_FB_RIVA is not set # CONFIG_FB_MATROX is not set @@ -741,9 +771,7 @@ CONFIG_FB_STI=y # CONFIG_FB_KYRO is not set # CONFIG_FB_3DFX is not set # CONFIG_FB_VOODOO1 is not set -# CONFIG_FB_CYBLA is not set # CONFIG_FB_TRIDENT is not set -# CONFIG_FB_S1D13XXX is not set # CONFIG_FB_VIRTUAL is not set # @@ -753,15 +781,28 @@ CONFIG_DUMMY_CONSOLE=y CONFIG_DUMMY_CONSOLE_COLUMNS=160 CONFIG_DUMMY_CONSOLE_ROWS=64 CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set CONFIG_STI_CONSOLE=y -# CONFIG_FONTS is not set -CONFIG_FONT_8x8=y +CONFIG_FONTS=y +# CONFIG_FONT_8x8 is not set CONFIG_FONT_8x16=y +# CONFIG_FONT_6x11 is not set +# CONFIG_FONT_7x14 is not set +# CONFIG_FONT_PEARL_8x8 is not set +# CONFIG_FONT_ACORN_8x8 is not set +# CONFIG_FONT_MINI_4x6 is not set +# CONFIG_FONT_SUN8x16 is not set +# CONFIG_FONT_SUN12x22 is not set +# CONFIG_FONT_10x18 is not set # # Logo configuration # -# CONFIG_LOGO is not set +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +# CONFIG_LOGO_LINUX_CLUT224 is not set +CONFIG_LOGO_PARISC_CLUT224=y # CONFIG_BACKLIGHT_LCD_SUPPORT is not set # @@ -781,23 +822,27 @@ CONFIG_SND_OSSEMUL=y CONFIG_SND_MIXER_OSS=y CONFIG_SND_PCM_OSS=y CONFIG_SND_SEQUENCER_OSS=y +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y # CONFIG_SND_VERBOSE_PRINTK is not set # CONFIG_SND_DEBUG is not set # # Generic devices # +CONFIG_SND_AC97_CODEC=y +CONFIG_SND_AC97_BUS=y # CONFIG_SND_DUMMY is not set # CONFIG_SND_VIRMIDI is not set # CONFIG_SND_MTPAV is not set # CONFIG_SND_SERIAL_U16550 is not set # CONFIG_SND_MPU401 is not set -CONFIG_SND_AC97_CODEC=y -CONFIG_SND_AC97_BUS=y # # PCI devices # +CONFIG_SND_AD1889=y +# CONFIG_SND_AD1889_OPL3 is not set # CONFIG_SND_ALI5451 is not set # CONFIG_SND_ATIIXP is not set # CONFIG_SND_ATIIXP_MODEM is not set @@ -806,39 +851,38 @@ CONFIG_SND_AC97_BUS=y # CONFIG_SND_AU8830 is not set # CONFIG_SND_AZT3328 is not set # CONFIG_SND_BT87X is not set -# CONFIG_SND_CS46XX is not set +# CONFIG_SND_CA0106 is not set +# CONFIG_SND_CMIPCI is not set # CONFIG_SND_CS4281 is not set +# CONFIG_SND_CS46XX is not set # CONFIG_SND_EMU10K1 is not set # CONFIG_SND_EMU10K1X is not set -# CONFIG_SND_CA0106 is not set -# CONFIG_SND_KORG1212 is not set -# CONFIG_SND_MIXART is not set -# CONFIG_SND_NM256 is not set -# CONFIG_SND_RME32 is not set -# CONFIG_SND_RME96 is not set -# CONFIG_SND_RME9652 is not set -# CONFIG_SND_HDSP is not set -# CONFIG_SND_HDSPM is not set -# CONFIG_SND_TRIDENT is not set -# CONFIG_SND_YMFPCI is not set -CONFIG_SND_AD1889=y -# CONFIG_SND_AD1889_OPL3 is not set -# CONFIG_SND_CMIPCI is not set # CONFIG_SND_ENS1370 is not set # CONFIG_SND_ENS1371 is not set # CONFIG_SND_ES1938 is not set # CONFIG_SND_ES1968 is not set -# CONFIG_SND_MAESTRO3 is not set # CONFIG_SND_FM801 is not set +# CONFIG_SND_HDA_INTEL is not set +# CONFIG_SND_HDSP is not set +# CONFIG_SND_HDSPM is not set # CONFIG_SND_ICE1712 is not set # CONFIG_SND_ICE1724 is not set # CONFIG_SND_INTEL8X0 is not set # CONFIG_SND_INTEL8X0M is not set +# CONFIG_SND_KORG1212 is not set +# CONFIG_SND_MAESTRO3 is not set +# CONFIG_SND_MIXART is not set +# CONFIG_SND_NM256 is not set +# CONFIG_SND_PCXHR is not set +# CONFIG_SND_RME32 is not set +# CONFIG_SND_RME96 is not set +# CONFIG_SND_RME9652 is not set # CONFIG_SND_SONICVIBES is not set +# CONFIG_SND_TRIDENT is not set # CONFIG_SND_VIA82XX is not set # CONFIG_SND_VIA82XX_MODEM is not set # CONFIG_SND_VX222 is not set -# CONFIG_SND_HDA_INTEL is not set +# CONFIG_SND_YMFPCI is not set # # USB devices @@ -888,14 +932,18 @@ CONFIG_USB_OHCI_LITTLE_ENDIAN=y # USB Device Class drivers # # CONFIG_OBSOLETE_OSS_USB_DRIVER is not set -# CONFIG_USB_BLUETOOTH_TTY is not set # CONFIG_USB_ACM is not set # CONFIG_USB_PRINTER is not set # -# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' +# + +# +# may also be needed; see USB_STORAGE Help for more information # # CONFIG_USB_STORAGE is not set +# CONFIG_USB_LIBUSUAL is not set # # USB Input Devices @@ -918,6 +966,7 @@ CONFIG_USB_OHCI_LITTLE_ENDIAN=y # CONFIG_USB_YEALINK is not set # CONFIG_USB_XPAD is not set # CONFIG_USB_ATI_REMOTE is not set +# CONFIG_USB_ATI_REMOTE2 is not set # CONFIG_USB_KEYSPAN_REMOTE is not set # CONFIG_USB_APPLETOUCH is not set @@ -994,7 +1043,7 @@ CONFIG_USB_MON=y # CONFIG_INFINIBAND is not set # -# SN Devices +# EDAC - error detection and reporting (RAS) (EXPERIMENTAL) # # @@ -1011,6 +1060,7 @@ CONFIG_JBD=y # CONFIG_JFS_FS is not set # CONFIG_FS_POSIX_ACL is not set # CONFIG_XFS_FS is not set +# CONFIG_OCFS2_FS is not set # CONFIG_MINIX_FS is not set # CONFIG_ROMFS_FS is not set CONFIG_INOTIFY=y @@ -1045,6 +1095,7 @@ CONFIG_TMPFS=y # CONFIG_HUGETLB_PAGE is not set CONFIG_RAMFS=y # CONFIG_RELAYFS_FS is not set +# CONFIG_CONFIGFS_FS is not set # # Miscellaneous filesystems @@ -1151,18 +1202,22 @@ CONFIG_OPROFILE=y # Kernel hacking # # CONFIG_PRINTK_TIME is not set -CONFIG_DEBUG_KERNEL=y CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_KERNEL=y CONFIG_LOG_BUF_SHIFT=15 CONFIG_DETECT_SOFTLOCKUP=y # CONFIG_SCHEDSTATS is not set # CONFIG_DEBUG_SLAB is not set +CONFIG_DEBUG_MUTEXES=y # CONFIG_DEBUG_SPINLOCK is not set # CONFIG_DEBUG_SPINLOCK_SLEEP is not set # CONFIG_DEBUG_KOBJECT is not set # CONFIG_DEBUG_INFO is not set -# CONFIG_DEBUG_IOREMAP is not set # CONFIG_DEBUG_FS is not set +# CONFIG_DEBUG_VM is not set +CONFIG_FORCED_INLINING=y +# CONFIG_RCU_TORTURE_TEST is not set +CONFIG_DEBUG_RODATA=y # # Security options diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c index d8a4ca021aac..360b7391cb8c 100644 --- a/arch/parisc/kernel/cache.c +++ b/arch/parisc/kernel/cache.c @@ -89,7 +89,7 @@ update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte) if (pfn_valid(page_to_pfn(page)) && page_mapping(page) && test_bit(PG_dcache_dirty, &page->flags)) { - flush_kernel_dcache_page(page_address(page)); + flush_kernel_dcache_page(page); clear_bit(PG_dcache_dirty, &page->flags); } } @@ -278,7 +278,7 @@ void flush_dcache_page(struct page *page) return; } - flush_kernel_dcache_page(page_address(page)); + flush_kernel_dcache_page(page); if (!mapping) return; @@ -317,7 +317,7 @@ EXPORT_SYMBOL(flush_dcache_page); /* Defined in arch/parisc/kernel/pacache.S */ EXPORT_SYMBOL(flush_kernel_dcache_range_asm); -EXPORT_SYMBOL(flush_kernel_dcache_page); +EXPORT_SYMBOL(flush_kernel_dcache_page_asm); EXPORT_SYMBOL(flush_data_cache_local); EXPORT_SYMBOL(flush_kernel_icache_range_asm); diff --git a/arch/parisc/kernel/entry.S b/arch/parisc/kernel/entry.S index 9af4b22a6d77..7c95d7663c29 100644 --- a/arch/parisc/kernel/entry.S +++ b/arch/parisc/kernel/entry.S @@ -563,10 +563,10 @@ extrd,u,*= \pte,_PAGE_GATEWAY_BIT+32,1,%r0 depd %r0,11,2,\prot /* If Gateway, Set PL2 to 0 */ - /* Get rid of prot bits and convert to page addr for iitlbt */ + /* Get rid of prot bits and convert to page addr for iitlbt and idtlbt */ depd %r0,63,PAGE_SHIFT,\pte - extrd,u \pte,56,32,\pte + extrd,s \pte,(63-PAGE_SHIFT)+(63-58),64-PAGE_SHIFT,\pte .endm /* Identical macro to make_insert_tlb above, except it @@ -584,7 +584,7 @@ /* Get rid of prot bits and convert to page addr for iitlba */ - depi 0,31,12,\pte + depi 0,31,PAGE_SHIFT,\pte extru \pte,24,25,\pte .endm @@ -1014,14 +1014,21 @@ intr_restore: nop nop +#ifndef CONFIG_PREEMPT +# define intr_do_preempt intr_restore +#endif /* !CONFIG_PREEMPT */ + .import schedule,code intr_do_resched: - /* Only do reschedule if we are returning to user space */ + /* Only call schedule on return to userspace. If we're returning + * to kernel space, we may schedule if CONFIG_PREEMPT, otherwise + * we jump back to intr_restore. + */ LDREG PT_IASQ0(%r16), %r20 - CMPIB= 0,%r20,intr_restore /* backward */ + CMPIB= 0, %r20, intr_do_preempt nop LDREG PT_IASQ1(%r16), %r20 - CMPIB= 0,%r20,intr_restore /* backward */ + CMPIB= 0, %r20, intr_do_preempt nop #ifdef CONFIG_64BIT @@ -1037,6 +1044,32 @@ intr_do_resched: #endif ldo R%intr_check_sig(%r2), %r2 + /* preempt the current task on returning to kernel + * mode from an interrupt, iff need_resched is set, + * and preempt_count is 0. otherwise, we continue on + * our merry way back to the current running task. + */ +#ifdef CONFIG_PREEMPT + .import preempt_schedule_irq,code +intr_do_preempt: + rsm PSW_SM_I, %r0 /* disable interrupts */ + + /* current_thread_info()->preempt_count */ + mfctl %cr30, %r1 + LDREG TI_PRE_COUNT(%r1), %r19 + CMPIB<> 0, %r19, intr_restore /* if preempt_count > 0 */ + nop /* prev insn branched backwards */ + + /* check if we interrupted a critical path */ + LDREG PT_PSW(%r16), %r20 + bb,<,n %r20, 31 - PSW_SM_I, intr_restore + nop + + BL preempt_schedule_irq, %r2 + nop + + b intr_restore /* ssm PSW_SM_I done by intr_restore */ +#endif /* CONFIG_PREEMPT */ .import do_signal,code intr_do_signal: diff --git a/arch/parisc/kernel/pacache.S b/arch/parisc/kernel/pacache.S index 9534ee17b9be..7a4f07e8d3c3 100644 --- a/arch/parisc/kernel/pacache.S +++ b/arch/parisc/kernel/pacache.S @@ -621,9 +621,9 @@ __clear_user_page_asm: .procend - .export flush_kernel_dcache_page + .export flush_kernel_dcache_page_asm -flush_kernel_dcache_page: +flush_kernel_dcache_page_asm: .proc .callinfo NO_CALLS .entry diff --git a/arch/parisc/kernel/parisc_ksyms.c b/arch/parisc/kernel/parisc_ksyms.c index 1d00c365f2b1..fc107add627c 100644 --- a/arch/parisc/kernel/parisc_ksyms.c +++ b/arch/parisc/kernel/parisc_ksyms.c @@ -30,23 +30,7 @@ #include #include -EXPORT_SYMBOL(memchr); -EXPORT_SYMBOL(memcmp); -EXPORT_SYMBOL(memmove); -EXPORT_SYMBOL(memscan); EXPORT_SYMBOL(memset); -EXPORT_SYMBOL(strcat); -EXPORT_SYMBOL(strchr); -EXPORT_SYMBOL(strcmp); -EXPORT_SYMBOL(strcpy); -EXPORT_SYMBOL(strlen); -EXPORT_SYMBOL(strncat); -EXPORT_SYMBOL(strncmp); -EXPORT_SYMBOL(strncpy); -EXPORT_SYMBOL(strnlen); -EXPORT_SYMBOL(strrchr); -EXPORT_SYMBOL(strstr); -EXPORT_SYMBOL(strpbrk); #include EXPORT_SYMBOL(__xchg8); @@ -82,16 +66,12 @@ EXPORT_SYMBOL($global$); #endif #include -EXPORT_SYMBOL(__ioremap); -EXPORT_SYMBOL(iounmap); EXPORT_SYMBOL(memcpy_toio); EXPORT_SYMBOL(memcpy_fromio); EXPORT_SYMBOL(memset_io); #include -EXPORT_SYMBOL(sys_open); EXPORT_SYMBOL(sys_lseek); -EXPORT_SYMBOL(sys_read); EXPORT_SYMBOL(sys_write); #include diff --git a/arch/parisc/kernel/pdc_chassis.c b/arch/parisc/kernel/pdc_chassis.c index 0cea6958f427..a45e2e2ffd9f 100644 --- a/arch/parisc/kernel/pdc_chassis.c +++ b/arch/parisc/kernel/pdc_chassis.c @@ -5,9 +5,8 @@ * Copyright (C) 2002-2004 Thibaut VARENE * * 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. + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/arch/parisc/kernel/perf.c b/arch/parisc/kernel/perf.c index 53f861c82f93..ac8ee205c351 100644 --- a/arch/parisc/kernel/perf.c +++ b/arch/parisc/kernel/perf.c @@ -805,7 +805,7 @@ static int perf_write_image(uint64_t *memaddr) return -1; } - runway = ioremap(cpu_device->hpa.start, 4096); + runway = ioremap_nocache(cpu_device->hpa.start, 4096); /* Merge intrigue bits into Runway STATUS 0 */ tmp64 = __raw_readq(runway + RUNWAY_STATUS) & 0xffecfffffffffffful; diff --git a/arch/parisc/kernel/syscall_table.S b/arch/parisc/kernel/syscall_table.S index 89b6c56ea0a8..bbeeb614cfab 100644 --- a/arch/parisc/kernel/syscall_table.S +++ b/arch/parisc/kernel/syscall_table.S @@ -287,7 +287,7 @@ ENTRY_SAME(chown) /* 180 */ /* setsockopt() used by iptables: SO_SET_REPLACE/SO_SET_ADD_COUNTERS */ ENTRY_COMP(setsockopt) - ENTRY_SAME(getsockopt) + ENTRY_COMP(getsockopt) ENTRY_COMP(sendmsg) ENTRY_COMP(recvmsg) ENTRY_SAME(semop) /* 185 */ diff --git a/arch/parisc/lib/iomap.c b/arch/parisc/lib/iomap.c index 01bec8fcbd0d..f4a811690ab3 100644 --- a/arch/parisc/lib/iomap.c +++ b/arch/parisc/lib/iomap.c @@ -263,11 +263,7 @@ static const struct iomap_ops iomem_ops = { const struct iomap_ops *iomap_ops[8] = { [0] = &ioport_ops, -#ifdef CONFIG_DEBUG_IOREMAP - [6] = &iomem_ops, -#else [7] = &iomem_ops -#endif }; diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c index 852eda3953dc..3796be67cd53 100644 --- a/arch/parisc/mm/init.c +++ b/arch/parisc/mm/init.c @@ -1013,9 +1013,9 @@ void flush_tlb_all(void) #ifdef CONFIG_BLK_DEV_INITRD void free_initrd_mem(unsigned long start, unsigned long end) { -#if 0 - if (start < end) - printk(KERN_INFO "Freeing initrd memory: %ldk freed\n", (end - start) >> 10); + if (start >= end) + return; + printk(KERN_INFO "Freeing initrd memory: %ldk freed\n", (end - start) >> 10); for (; start < end; start += PAGE_SIZE) { ClearPageReserved(virt_to_page(start)); init_page_count(virt_to_page(start)); @@ -1023,6 +1023,5 @@ void free_initrd_mem(unsigned long start, unsigned long end) num_physpages++; totalram_pages++; } -#endif } #endif diff --git a/arch/parisc/mm/ioremap.c b/arch/parisc/mm/ioremap.c index edd9a9559cba..0db12818d7bc 100644 --- a/arch/parisc/mm/ioremap.c +++ b/arch/parisc/mm/ioremap.c @@ -72,7 +72,6 @@ remap_area_pmd(pmd_t *pmd, unsigned long address, unsigned long size, return 0; } -#if USE_HPPA_IOREMAP static int remap_area_pages(unsigned long address, unsigned long phys_addr, unsigned long size, unsigned long flags) @@ -114,31 +113,6 @@ remap_area_pages(unsigned long address, unsigned long phys_addr, return error; } -#endif /* USE_HPPA_IOREMAP */ - -#ifdef CONFIG_DEBUG_IOREMAP -static unsigned long last = 0; - -void gsc_bad_addr(unsigned long addr) -{ - if (time_after(jiffies, last + HZ*10)) { - printk("gsc_foo() called with bad address 0x%lx\n", addr); - dump_stack(); - last = jiffies; - } -} -EXPORT_SYMBOL(gsc_bad_addr); - -void __raw_bad_addr(const volatile void __iomem *addr) -{ - if (time_after(jiffies, last + HZ*10)) { - printk("__raw_foo() called with bad address 0x%p\n", addr); - dump_stack(); - last = jiffies; - } -} -EXPORT_SYMBOL(__raw_bad_addr); -#endif /* * Generic mapping function (not visible outside): @@ -154,26 +128,19 @@ EXPORT_SYMBOL(__raw_bad_addr); */ void __iomem * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) { -#if !(USE_HPPA_IOREMAP) - - unsigned long end = phys_addr + size - 1; - /* Support EISA addresses */ - if ((phys_addr >= 0x00080000 && end < 0x000fffff) - || (phys_addr >= 0x00500000 && end < 0x03bfffff)) { - phys_addr |= 0xfc000000; - } - -#ifdef CONFIG_DEBUG_IOREMAP - return (void __iomem *)(phys_addr - (0x1UL << NYBBLE_SHIFT)); -#else - return (void __iomem *)phys_addr; -#endif - -#else void *addr; struct vm_struct *area; unsigned long offset, last_addr; +#ifdef CONFIG_EISA + unsigned long end = phys_addr + size - 1; + /* Support EISA addresses */ + if ((phys_addr >= 0x00080000 && end < 0x000fffff) || + (phys_addr >= 0x00500000 && end < 0x03bfffff)) { + phys_addr |= F_EXTEND(0xfc000000); + } +#endif + /* Don't allow wraparound or zero size */ last_addr = phys_addr + size - 1; if (!size || last_addr < phys_addr) @@ -217,15 +184,12 @@ void __iomem * __ioremap(unsigned long phys_addr, unsigned long size, unsigned l } return (void __iomem *) (offset + (char *)addr); -#endif } +EXPORT_SYMBOL(__ioremap); void iounmap(void __iomem *addr) { -#if !(USE_HPPA_IOREMAP) - return; -#else if (addr > high_memory) return vfree((void *) (PAGE_MASK & (unsigned long __force) addr)); -#endif } +EXPORT_SYMBOL(iounmap); diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 2cdc35ce8045..167e70e95556 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -649,6 +649,11 @@ config NUMA depends on PPC64 default y if SMP && PPC_PSERIES +config NODES_SHIFT + int + default "4" + depends on NEED_MULTIPLE_NODES + config ARCH_SELECT_MEMORY_MODEL def_bool y depends on PPC64 diff --git a/arch/powerpc/kernel/crash_dump.c b/arch/powerpc/kernel/crash_dump.c index 211d72653ea6..764d07329716 100644 --- a/arch/powerpc/kernel/crash_dump.c +++ b/arch/powerpc/kernel/crash_dump.c @@ -61,7 +61,7 @@ static int __init parse_elfcorehdr(char *p) if (p) elfcorehdr_addr = memparse(p, &p); - return 0; + return 1; } __setup("elfcorehdr=", parse_elfcorehdr); #endif @@ -71,7 +71,7 @@ static int __init parse_savemaxmem(char *p) if (p) saved_max_pfn = (memparse(p, &p) >> PAGE_SHIFT) - 1; - return 0; + return 1; } __setup("savemaxmem=", parse_savemaxmem); diff --git a/arch/powerpc/kernel/lparcfg.c b/arch/powerpc/kernel/lparcfg.c index 1b73508ecb2b..2cbde865d4f5 100644 --- a/arch/powerpc/kernel/lparcfg.c +++ b/arch/powerpc/kernel/lparcfg.c @@ -37,7 +37,7 @@ #include #include -#define MODULE_VERS "1.6" +#define MODULE_VERS "1.7" #define MODULE_NAME "lparcfg" /* #define LPARCFG_DEBUG */ @@ -149,17 +149,17 @@ static void log_plpar_hcall_return(unsigned long rc, char *tag) if (rc == 0) /* success, return */ return; /* check for null tag ? */ - if (rc == H_Hardware) + if (rc == H_HARDWARE) printk(KERN_INFO "plpar-hcall (%s) failed with hardware fault\n", tag); - else if (rc == H_Function) + else if (rc == H_FUNCTION) printk(KERN_INFO "plpar-hcall (%s) failed; function not allowed\n", tag); - else if (rc == H_Authority) + else if (rc == H_AUTHORITY) printk(KERN_INFO - "plpar-hcall (%s) failed; not authorized to this function\n", - tag); - else if (rc == H_Parameter) + "plpar-hcall (%s) failed; not authorized to this" + " function\n", tag); + else if (rc == H_PARAMETER) printk(KERN_INFO "plpar-hcall (%s) failed; Bad parameter(s)\n", tag); else @@ -209,7 +209,7 @@ static void h_pic(unsigned long *pool_idle_time, unsigned long *num_procs) unsigned long dummy; rc = plpar_hcall(H_PIC, 0, 0, 0, 0, pool_idle_time, num_procs, &dummy); - if (rc != H_Authority) + if (rc != H_AUTHORITY) log_plpar_hcall_return(rc, "H_PIC"); } @@ -242,7 +242,7 @@ static void parse_system_parameter_string(struct seq_file *m) { int call_status; - char *local_buffer = kmalloc(SPLPAR_MAXLENGTH, GFP_KERNEL); + unsigned char *local_buffer = kmalloc(SPLPAR_MAXLENGTH, GFP_KERNEL); if (!local_buffer) { printk(KERN_ERR "%s %s kmalloc failure at line %d \n", __FILE__, __FUNCTION__, __LINE__); @@ -254,7 +254,8 @@ static void parse_system_parameter_string(struct seq_file *m) call_status = rtas_call(rtas_token("ibm,get-system-parameter"), 3, 1, NULL, SPLPAR_CHARACTERISTICS_TOKEN, - __pa(rtas_data_buf)); + __pa(rtas_data_buf), + RTAS_DATA_BUF_SIZE); memcpy(local_buffer, rtas_data_buf, SPLPAR_MAXLENGTH); spin_unlock(&rtas_data_buf_lock); @@ -275,7 +276,7 @@ static void parse_system_parameter_string(struct seq_file *m) #ifdef LPARCFG_DEBUG printk(KERN_INFO "success calling get-system-parameter \n"); #endif - splpar_strlen = local_buffer[0] * 16 + local_buffer[1]; + splpar_strlen = local_buffer[0] * 256 + local_buffer[1]; local_buffer += 2; /* step over strlen value */ memset(workbuffer, 0, SPLPAR_MAXLENGTH); @@ -529,13 +530,13 @@ static ssize_t lparcfg_write(struct file *file, const char __user * buf, retval = plpar_hcall_norets(H_SET_PPP, *new_entitled_ptr, *new_weight_ptr); - if (retval == H_Success || retval == H_Constrained) { + if (retval == H_SUCCESS || retval == H_CONSTRAINED) { retval = count; - } else if (retval == H_Busy) { + } else if (retval == H_BUSY) { retval = -EBUSY; - } else if (retval == H_Hardware) { + } else if (retval == H_HARDWARE) { retval = -EIO; - } else if (retval == H_Parameter) { + } else if (retval == H_PARAMETER) { retval = -EINVAL; } else { printk(KERN_WARNING "%s: received unknown hv return code %ld", diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 706090c99f47..2dd47d2dd998 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -834,7 +834,6 @@ unsigned long get_wchan(struct task_struct *p) } while (count++ < 16); return 0; } -EXPORT_SYMBOL(get_wchan); static int kstack_depth_to_print = 64; diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c index 06636c927a7e..0112318213ab 100644 --- a/arch/powerpc/kernel/rtas.c +++ b/arch/powerpc/kernel/rtas.c @@ -578,18 +578,18 @@ static void rtas_percpu_suspend_me(void *info) * We use "waiting" to indicate our state. As long * as it is >0, we are still trying to all join up. * If it goes to 0, we have successfully joined up and - * one thread got H_Continue. If any error happens, + * one thread got H_CONTINUE. If any error happens, * we set it to <0. */ local_irq_save(flags); do { rc = plpar_hcall_norets(H_JOIN); smp_rmb(); - } while (rc == H_Success && data->waiting > 0); - if (rc == H_Success) + } while (rc == H_SUCCESS && data->waiting > 0); + if (rc == H_SUCCESS) goto out; - if (rc == H_Continue) { + if (rc == H_CONTINUE) { data->waiting = 0; data->args->args[data->args->nargs] = rtas_call(ibm_suspend_me_token, 0, 1, NULL); @@ -597,7 +597,7 @@ static void rtas_percpu_suspend_me(void *info) plpar_hcall_norets(H_PROD,i); } else { data->waiting = -EBUSY; - printk(KERN_ERR "Error on H_Join hypervisor call\n"); + printk(KERN_ERR "Error on H_JOIN hypervisor call\n"); } out: @@ -624,7 +624,7 @@ static int rtas_ibm_suspend_me(struct rtas_args *args) printk(KERN_ERR "Error doing global join\n"); /* Prod each CPU. This won't hurt, and will wake - * anyone we successfully put to sleep with H_Join + * anyone we successfully put to sleep with H_JOIN. */ for_each_possible_cpu(i) plpar_hcall_norets(H_PROD, i); diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c index c607f3b9ca17..1d93e73a7003 100644 --- a/arch/powerpc/kernel/setup-common.c +++ b/arch/powerpc/kernel/setup-common.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -462,6 +463,29 @@ static int __init early_xmon(char *p) early_param("xmon", early_xmon); #endif +static __init int add_pcspkr(void) +{ + struct device_node *np; + struct platform_device *pd; + int ret; + + np = of_find_compatible_node(NULL, NULL, "pnpPNP,100"); + of_node_put(np); + if (!np) + return -ENODEV; + + pd = platform_device_alloc("pcspkr", -1); + if (!pd) + return -ENOMEM; + + ret = platform_device_add(pd); + if (ret) + platform_device_put(pd); + + return ret; +} +device_initcall(add_pcspkr); + void probe_machine(void) { extern struct machdep_calls __machine_desc_start; diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c index a72bf5dceeee..69ac25701344 100644 --- a/arch/powerpc/kernel/setup_32.c +++ b/arch/powerpc/kernel/setup_32.c @@ -50,7 +50,6 @@ #include #endif -extern void platform_init(void); extern void bootx_init(unsigned long r4, unsigned long phys); boot_infos_t *boot_infos; @@ -138,12 +137,7 @@ void __init machine_init(unsigned long dt_ptr, unsigned long phys) strlcpy(cmd_line, CONFIG_CMDLINE, sizeof(cmd_line)); #endif /* CONFIG_CMDLINE */ -#ifdef CONFIG_PPC_MULTIPLATFORM probe_machine(); -#else - /* Base init based on machine type. Obsoloete, please kill ! */ - platform_init(); -#endif #ifdef CONFIG_6xx if (cpu_has_feature(CPU_FTR_CAN_DOZE) || diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index 59aa92cd6fa4..13e91c4d70a8 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -215,12 +215,10 @@ void __init early_setup(unsigned long dt_ptr) /* * Initialize stab / SLB management except on iSeries */ - if (!firmware_has_feature(FW_FEATURE_ISERIES)) { - if (cpu_has_feature(CPU_FTR_SLB)) - slb_initialize(); - else - stab_initialize(get_paca()->stab_real); - } + if (cpu_has_feature(CPU_FTR_SLB)) + slb_initialize(); + else if (!firmware_has_feature(FW_FEATURE_ISERIES)) + stab_initialize(get_paca()->stab_real); DBG(" <- early_setup()\n"); } diff --git a/arch/powerpc/kernel/systbl.S b/arch/powerpc/kernel/systbl.S index 1ad55f0466fd..1424eab450ee 100644 --- a/arch/powerpc/kernel/systbl.S +++ b/arch/powerpc/kernel/systbl.S @@ -322,3 +322,4 @@ SYSCALL(spu_create) COMPAT_SYS(pselect6) COMPAT_SYS(ppoll) SYSCALL(unshare) +SYSCALL(splice) diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 4cbde211eb69..064a52564692 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -228,7 +228,7 @@ void system_reset_exception(struct pt_regs *regs) */ static inline int check_io_access(struct pt_regs *regs) { -#ifdef CONFIG_PPC_PMAC +#if defined(CONFIG_PPC_PMAC) && defined(CONFIG_PPC32) unsigned long msr = regs->msr; const struct exception_table_entry *entry; unsigned int *nip = (unsigned int *)regs->nip; @@ -261,7 +261,7 @@ static inline int check_io_access(struct pt_regs *regs) return 1; } } -#endif /* CONFIG_PPC_PMAC */ +#endif /* CONFIG_PPC_PMAC && CONFIG_PPC32 */ return 0; } @@ -308,8 +308,8 @@ platform_machine_check(struct pt_regs *regs) void machine_check_exception(struct pt_regs *regs) { -#ifdef CONFIG_PPC64 int recover = 0; + unsigned long reason = get_mc_reason(regs); /* See if any machine dependent calls */ if (ppc_md.machine_check_exception) @@ -317,8 +317,6 @@ void machine_check_exception(struct pt_regs *regs) if (recover) return; -#else - unsigned long reason = get_mc_reason(regs); if (user_mode(regs)) { regs->msr |= MSR_RI; @@ -462,7 +460,6 @@ void machine_check_exception(struct pt_regs *regs) * additional info, e.g. bus error registers. */ platform_machine_check(regs); -#endif /* CONFIG_PPC64 */ if (debugger_fault_handler(regs)) return; diff --git a/arch/powerpc/kernel/vdso32/sigtramp.S b/arch/powerpc/kernel/vdso32/sigtramp.S index e04642781917..0c6a37b29dde 100644 --- a/arch/powerpc/kernel/vdso32/sigtramp.S +++ b/arch/powerpc/kernel/vdso32/sigtramp.S @@ -261,7 +261,7 @@ V_FUNCTION_END(__kernel_sigtramp_rt32) .Lcie_start: .long 0 /* CIE ID */ .byte 1 /* Version number */ - .string "zR" /* NUL-terminated augmentation string */ + .string "zRS" /* NUL-terminated augmentation string */ .uleb128 4 /* Code alignment factor */ .sleb128 -4 /* Data alignment factor */ .byte 67 /* Return address register column, ap */ diff --git a/arch/powerpc/kernel/vdso64/sigtramp.S b/arch/powerpc/kernel/vdso64/sigtramp.S index 31b604ab56de..7479edb101b8 100644 --- a/arch/powerpc/kernel/vdso64/sigtramp.S +++ b/arch/powerpc/kernel/vdso64/sigtramp.S @@ -263,7 +263,7 @@ V_FUNCTION_END(__kernel_sigtramp_rt64) .Lcie_start: .long 0 /* CIE ID */ .byte 1 /* Version number */ - .string "zR" /* NUL-terminated augmentation string */ + .string "zRS" /* NUL-terminated augmentation string */ .uleb128 4 /* Code alignment factor */ .sleb128 -8 /* Data alignment factor */ .byte 67 /* Return address register column, ap */ diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c index 5aea0909a5ec..fdbba4206d59 100644 --- a/arch/powerpc/mm/fault.c +++ b/arch/powerpc/mm/fault.c @@ -177,15 +177,15 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address, /* When running in the kernel we expect faults to occur only to * addresses in user space. All other faults represent errors in the - * kernel and should generate an OOPS. Unfortunatly, in the case of an - * erroneous fault occuring in a code path which already holds mmap_sem + * kernel and should generate an OOPS. Unfortunately, in the case of an + * erroneous fault occurring in a code path which already holds mmap_sem * we will deadlock attempting to validate the fault against the * address space. Luckily the kernel only validly references user * space from well defined areas of code, which are listed in the * exceptions table. * * As the vast majority of faults will be valid we will only perform - * the source reference check when there is a possibilty of a deadlock. + * the source reference check when there is a possibility of a deadlock. * Attempt to lock the address space, if we cannot we then validate the * source. If this is invalid we can skip the address space check, * thus avoiding the deadlock. diff --git a/arch/powerpc/platforms/83xx/mpc834x_sys.c b/arch/powerpc/platforms/83xx/mpc834x_sys.c index 7c18b4cd5db4..7e789d2420ba 100644 --- a/arch/powerpc/platforms/83xx/mpc834x_sys.c +++ b/arch/powerpc/platforms/83xx/mpc834x_sys.c @@ -158,25 +158,25 @@ static int __init mpc834x_rtc_hookup(void) late_initcall(mpc834x_rtc_hookup); #endif -void __init platform_init(void) +/* + * Called very early, MMU is off, device-tree isn't unflattened + */ +static int __init mpc834x_sys_probe(void) { - /* setup the PowerPC module struct */ - ppc_md.setup_arch = mpc834x_sys_setup_arch; - - ppc_md.init_IRQ = mpc834x_sys_init_IRQ; - ppc_md.get_irq = ipic_get_irq; - - ppc_md.restart = mpc83xx_restart; - - ppc_md.time_init = mpc83xx_time_init; - ppc_md.set_rtc_time = NULL; - ppc_md.get_rtc_time = NULL; - ppc_md.calibrate_decr = generic_calibrate_decr; - - ppc_md.progress = udbg_progress; - - if (ppc_md.progress) - ppc_md.progress("mpc834x_sys_init(): exit", 0); - - return; + /* We always match for now, eventually we should look at the flat + dev tree to ensure this is the board we are suppose to run on + */ + return 1; } + +define_machine(mpc834x_sys) { + .name = "MPC834x SYS", + .probe = mpc834x_sys_probe, + .setup_arch = mpc834x_sys_setup_arch, + .init_IRQ = mpc834x_sys_init_IRQ, + .get_irq = ipic_get_irq, + .restart = mpc83xx_restart, + .time_init = mpc83xx_time_init, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/85xx/mpc85xx_ads.c b/arch/powerpc/platforms/85xx/mpc85xx_ads.c index b7821dbae00d..5eeff370f5fc 100644 --- a/arch/powerpc/platforms/85xx/mpc85xx_ads.c +++ b/arch/powerpc/platforms/85xx/mpc85xx_ads.c @@ -220,25 +220,25 @@ void mpc85xx_ads_show_cpuinfo(struct seq_file *m) seq_printf(m, "Memory\t\t: %d MB\n", memsize / (1024 * 1024)); } -void __init platform_init(void) +/* + * Called very early, device-tree isn't unflattened + */ +static int __init mpc85xx_ads_probe(void) { - ppc_md.setup_arch = mpc85xx_ads_setup_arch; - ppc_md.show_cpuinfo = mpc85xx_ads_show_cpuinfo; - - ppc_md.init_IRQ = mpc85xx_ads_pic_init; - ppc_md.get_irq = mpic_get_irq; - - ppc_md.restart = mpc85xx_restart; - ppc_md.power_off = NULL; - ppc_md.halt = NULL; - - ppc_md.time_init = NULL; - ppc_md.set_rtc_time = NULL; - ppc_md.get_rtc_time = NULL; - ppc_md.calibrate_decr = generic_calibrate_decr; - - ppc_md.progress = udbg_progress; - - if (ppc_md.progress) - ppc_md.progress("mpc85xx_ads platform_init(): exit", 0); + /* We always match for now, eventually we should look at the flat + dev tree to ensure this is the board we are suppose to run on + */ + return 1; } + +define_machine(mpc85xx_ads) { + .name = "MPC85xx ADS", + .probe = mpc85xx_ads_probe, + .setup_arch = mpc85xx_ads_setup_arch, + .init_IRQ = mpc85xx_ads_pic_init, + .show_cpuinfo = mpc85xx_ads_show_cpuinfo, + .get_irq = mpic_get_irq, + .restart = mpc85xx_restart, + .calibrate_decr = generic_calibrate_decr, + .progress = udbg_progress, +}; diff --git a/arch/powerpc/platforms/cell/spu_callbacks.c b/arch/powerpc/platforms/cell/spu_callbacks.c index 3a4245c926ad..6594bec73882 100644 --- a/arch/powerpc/platforms/cell/spu_callbacks.c +++ b/arch/powerpc/platforms/cell/spu_callbacks.c @@ -316,6 +316,7 @@ void *spu_syscall_table[] = { [__NR_pselect6] sys_ni_syscall, /* sys_pselect */ [__NR_ppoll] sys_ni_syscall, /* sys_ppoll */ [__NR_unshare] sys_unshare, + [__NR_splice] sys_splice, }; long spu_sys_callback(struct spu_syscall_block *s) diff --git a/arch/powerpc/platforms/cell/spufs/run.c b/arch/powerpc/platforms/cell/spufs/run.c index c04e078c0fe5..483c8b76232c 100644 --- a/arch/powerpc/platforms/cell/spufs/run.c +++ b/arch/powerpc/platforms/cell/spufs/run.c @@ -2,6 +2,7 @@ #include #include +#include #include "spufs.h" diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index 9b2b1cb117b3..780fb27a0099 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c @@ -865,7 +865,7 @@ void __init eeh_init(void) * on the CEC architecture, type of the device, on earlier boot * command-line arguments & etc. */ -void eeh_add_device_early(struct device_node *dn) +static void eeh_add_device_early(struct device_node *dn) { struct pci_controller *phb; struct eeh_early_enable_info info; @@ -882,7 +882,6 @@ void eeh_add_device_early(struct device_node *dn) info.buid_lo = BUID_LO(phb->buid); early_enable_eeh(dn, &info); } -EXPORT_SYMBOL_GPL(eeh_add_device_early); void eeh_add_device_tree_early(struct device_node *dn) { @@ -893,20 +892,6 @@ void eeh_add_device_tree_early(struct device_node *dn) } EXPORT_SYMBOL_GPL(eeh_add_device_tree_early); -void eeh_add_device_tree_late(struct pci_bus *bus) -{ - struct pci_dev *dev; - - list_for_each_entry(dev, &bus->devices, bus_list) { - eeh_add_device_late(dev); - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { - struct pci_bus *subbus = dev->subordinate; - if (subbus) - eeh_add_device_tree_late(subbus); - } - } -} - /** * eeh_add_device_late - perform EEH initialization for the indicated pci device * @dev: pci device for which to set up EEH @@ -914,7 +899,7 @@ void eeh_add_device_tree_late(struct pci_bus *bus) * This routine must be used to complete EEH initialization for PCI * devices that were added after system boot (e.g. hotplug, dlpar). */ -void eeh_add_device_late(struct pci_dev *dev) +static void eeh_add_device_late(struct pci_dev *dev) { struct device_node *dn; struct pci_dn *pdn; @@ -933,16 +918,33 @@ void eeh_add_device_late(struct pci_dev *dev) pci_addr_cache_insert_device (dev); } -EXPORT_SYMBOL_GPL(eeh_add_device_late); + +void eeh_add_device_tree_late(struct pci_bus *bus) +{ + struct pci_dev *dev; + + list_for_each_entry(dev, &bus->devices, bus_list) { + eeh_add_device_late(dev); + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { + struct pci_bus *subbus = dev->subordinate; + if (subbus) + eeh_add_device_tree_late(subbus); + } + } +} +EXPORT_SYMBOL_GPL(eeh_add_device_tree_late); /** * eeh_remove_device - undo EEH setup for the indicated pci device * @dev: pci device to be removed * - * This routine should be when a device is removed from a running - * system (e.g. by hotplug or dlpar). + * This routine should be called when a device is removed from + * a running system (e.g. by hotplug or dlpar). It unregisters + * the PCI device from the EEH subsystem. I/O errors affecting + * this device will no longer be detected after this call; thus, + * i/o errors affecting this slot may leave this device unusable. */ -void eeh_remove_device(struct pci_dev *dev) +static void eeh_remove_device(struct pci_dev *dev) { struct device_node *dn; if (!dev || !eeh_subsystem_enabled) @@ -958,21 +960,17 @@ void eeh_remove_device(struct pci_dev *dev) PCI_DN(dn)->pcidev = NULL; pci_dev_put (dev); } -EXPORT_SYMBOL_GPL(eeh_remove_device); void eeh_remove_bus_device(struct pci_dev *dev) { + struct pci_bus *bus = dev->subordinate; + struct pci_dev *child, *tmp; + eeh_remove_device(dev); - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { - struct pci_bus *bus = dev->subordinate; - struct list_head *ln; - if (!bus) - return; - for (ln = bus->devices.next; ln != &bus->devices; ln = ln->next) { - struct pci_dev *pdev = pci_dev_b(ln); - if (pdev) - eeh_remove_bus_device(pdev); - } + + if (bus && dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { + list_for_each_entry_safe(child, tmp, &bus->devices, bus_list) + eeh_remove_bus_device(child); } } EXPORT_SYMBOL_GPL(eeh_remove_bus_device); diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c index cc2495a0cdd5..1fba695e32e8 100644 --- a/arch/powerpc/platforms/pseries/eeh_driver.c +++ b/arch/powerpc/platforms/pseries/eeh_driver.c @@ -293,15 +293,16 @@ void handle_eeh_events (struct eeh_event *event) frozen_pdn = PCI_DN(frozen_dn); frozen_pdn->eeh_freeze_count++; - pci_str = pci_name (frozen_pdn->pcidev); - drv_str = pcid_name (frozen_pdn->pcidev); - if (!pci_str) { + if (frozen_pdn->pcidev) { + pci_str = pci_name (frozen_pdn->pcidev); + drv_str = pcid_name (frozen_pdn->pcidev); + } else { pci_str = pci_name (event->dev); drv_str = pcid_name (event->dev); } if (frozen_pdn->eeh_freeze_count > EEH_MAX_ALLOWED_FREEZES) - goto hard_fail; + goto excess_failures; /* If the reset state is a '5' and the time to reset is 0 (infinity) * or is more then 15 seconds, then mark this as a permanent failure. @@ -356,7 +357,7 @@ void handle_eeh_events (struct eeh_event *event) return; -hard_fail: +excess_failures: /* * About 90% of all real-life EEH failures in the field * are due to poorly seated PCI cards. Only 10% or so are @@ -367,7 +368,15 @@ hard_fail: "and has been permanently disabled. Please try reseating\n" "this device or replacing it.\n", drv_str, pci_str, frozen_pdn->eeh_freeze_count); + goto perm_error; +hard_fail: + printk(KERN_ERR + "EEH: Unable to recover from failure of PCI device %s - %s\n" + "Please try reseating this device or replacing it.\n", + drv_str, pci_str); + +perm_error: eeh_slot_error_detail(frozen_pdn, 2 /* Permanent Error */); /* Notify all devices that they're about to go down. */ diff --git a/arch/powerpc/platforms/pseries/eeh_event.c b/arch/powerpc/platforms/pseries/eeh_event.c index 9a9961f27480..a1bda6f96fd1 100644 --- a/arch/powerpc/platforms/pseries/eeh_event.c +++ b/arch/powerpc/platforms/pseries/eeh_event.c @@ -19,7 +19,9 @@ */ #include +#include #include +#include #include #include @@ -37,14 +39,18 @@ LIST_HEAD(eeh_eventlist); static void eeh_thread_launcher(void *); DECLARE_WORK(eeh_event_wq, eeh_thread_launcher, NULL); +/* Serialize reset sequences for a given pci device */ +DEFINE_MUTEX(eeh_event_mutex); + /** - * eeh_event_handler - dispatch EEH events. The detection of a frozen - * slot can occur inside an interrupt, where it can be hard to do - * anything about it. The goal of this routine is to pull these - * detection events out of the context of the interrupt handler, and - * re-dispatch them for processing at a later time in a normal context. - * + * eeh_event_handler - dispatch EEH events. * @dummy - unused + * + * The detection of a frozen slot can occur inside an interrupt, + * where it can be hard to do anything about it. The goal of this + * routine is to pull these detection events out of the context + * of the interrupt handler, and re-dispatch them for processing + * at a later time in a normal context. */ static int eeh_event_handler(void * dummy) { @@ -64,23 +70,24 @@ static int eeh_event_handler(void * dummy) event = list_entry(eeh_eventlist.next, struct eeh_event, list); list_del(&event->list); } - - if (event) - eeh_mark_slot(event->dn, EEH_MODE_RECOVERING); - spin_unlock_irqrestore(&eeh_eventlist_lock, flags); + if (event == NULL) break; + /* Serialize processing of EEH events */ + mutex_lock(&eeh_event_mutex); + eeh_mark_slot(event->dn, EEH_MODE_RECOVERING); + printk(KERN_INFO "EEH: Detected PCI bus error on device %s\n", pci_name(event->dev)); handle_eeh_events(event); eeh_clear_slot(event->dn, EEH_MODE_RECOVERING); - pci_dev_put(event->dev); kfree(event); + mutex_unlock(&eeh_event_mutex); } return 0; @@ -88,7 +95,6 @@ static int eeh_event_handler(void * dummy) /** * eeh_thread_launcher - * * @dummy - unused */ static void eeh_thread_launcher(void *dummy) diff --git a/arch/powerpc/platforms/pseries/hvCall.S b/arch/powerpc/platforms/pseries/hvCall.S index db7c19fe9297..c9ff547f9d25 100644 --- a/arch/powerpc/platforms/pseries/hvCall.S +++ b/arch/powerpc/platforms/pseries/hvCall.S @@ -127,3 +127,103 @@ _GLOBAL(plpar_hcall_4out) mtcrf 0xff,r0 blr /* return r3 = status */ + +/* plpar_hcall_7arg_7ret(unsigned long opcode, R3 + unsigned long arg1, R4 + unsigned long arg2, R5 + unsigned long arg3, R6 + unsigned long arg4, R7 + unsigned long arg5, R8 + unsigned long arg6, R9 + unsigned long arg7, R10 + unsigned long *out1, 112(R1) + unsigned long *out2, 110(R1) + unsigned long *out3, 108(R1) + unsigned long *out4, 106(R1) + unsigned long *out5, 104(R1) + unsigned long *out6, 102(R1) + unsigned long *out7); 100(R1) +*/ +_GLOBAL(plpar_hcall_7arg_7ret) + HMT_MEDIUM + + mfcr r0 + stw r0,8(r1) + + HVSC /* invoke the hypervisor */ + + lwz r0,8(r1) + + ld r11,STK_PARM(r11)(r1) /* Fetch r4 ret arg */ + std r4,0(r11) + ld r11,STK_PARM(r12)(r1) /* Fetch r5 ret arg */ + std r5,0(r11) + ld r11,STK_PARM(r13)(r1) /* Fetch r6 ret arg */ + std r6,0(r11) + ld r11,STK_PARM(r14)(r1) /* Fetch r7 ret arg */ + std r7,0(r11) + ld r11,STK_PARM(r15)(r1) /* Fetch r8 ret arg */ + std r8,0(r11) + ld r11,STK_PARM(r16)(r1) /* Fetch r9 ret arg */ + std r9,0(r11) + ld r11,STK_PARM(r17)(r1) /* Fetch r10 ret arg */ + std r10,0(r11) + + mtcrf 0xff,r0 + + blr /* return r3 = status */ + +/* plpar_hcall_9arg_9ret(unsigned long opcode, R3 + unsigned long arg1, R4 + unsigned long arg2, R5 + unsigned long arg3, R6 + unsigned long arg4, R7 + unsigned long arg5, R8 + unsigned long arg6, R9 + unsigned long arg7, R10 + unsigned long arg8, 112(R1) + unsigned long arg9, 110(R1) + unsigned long *out1, 108(R1) + unsigned long *out2, 106(R1) + unsigned long *out3, 104(R1) + unsigned long *out4, 102(R1) + unsigned long *out5, 100(R1) + unsigned long *out6, 98(R1) + unsigned long *out7); 96(R1) + unsigned long *out8, 94(R1) + unsigned long *out9, 92(R1) +*/ +_GLOBAL(plpar_hcall_9arg_9ret) + HMT_MEDIUM + + mfcr r0 + stw r0,8(r1) + + ld r11,STK_PARM(r11)(r1) /* put arg8 in R11 */ + ld r12,STK_PARM(r12)(r1) /* put arg9 in R12 */ + + HVSC /* invoke the hypervisor */ + + ld r0,STK_PARM(r13)(r1) /* Fetch r4 ret arg */ + stdx r4,r0,r0 + ld r0,STK_PARM(r14)(r1) /* Fetch r5 ret arg */ + stdx r5,r0,r0 + ld r0,STK_PARM(r15)(r1) /* Fetch r6 ret arg */ + stdx r6,r0,r0 + ld r0,STK_PARM(r16)(r1) /* Fetch r7 ret arg */ + stdx r7,r0,r0 + ld r0,STK_PARM(r17)(r1) /* Fetch r8 ret arg */ + stdx r8,r0,r0 + ld r0,STK_PARM(r18)(r1) /* Fetch r9 ret arg */ + stdx r9,r0,r0 + ld r0,STK_PARM(r19)(r1) /* Fetch r10 ret arg */ + stdx r10,r0,r0 + ld r0,STK_PARM(r20)(r1) /* Fetch r11 ret arg */ + stdx r11,r0,r0 + ld r0,STK_PARM(r21)(r1) /* Fetch r12 ret arg */ + stdx r12,r0,r0 + + lwz r0,8(r1) + mtcrf 0xff,r0 + + blr /* return r3 = status */ diff --git a/arch/powerpc/platforms/pseries/hvconsole.c b/arch/powerpc/platforms/pseries/hvconsole.c index ba6befd96636..a72a987f1d4d 100644 --- a/arch/powerpc/platforms/pseries/hvconsole.c +++ b/arch/powerpc/platforms/pseries/hvconsole.c @@ -41,7 +41,7 @@ int hvc_get_chars(uint32_t vtermno, char *buf, int count) unsigned long got; if (plpar_hcall(H_GET_TERM_CHAR, vtermno, 0, 0, 0, &got, - (unsigned long *)buf, (unsigned long *)buf+1) == H_Success) + (unsigned long *)buf, (unsigned long *)buf+1) == H_SUCCESS) return got; return 0; } @@ -69,9 +69,9 @@ int hvc_put_chars(uint32_t vtermno, const char *buf, int count) ret = plpar_hcall_norets(H_PUT_TERM_CHAR, vtermno, count, lbuf[0], lbuf[1]); - if (ret == H_Success) + if (ret == H_SUCCESS) return count; - if (ret == H_Busy) + if (ret == H_BUSY) return 0; return -EIO; } diff --git a/arch/powerpc/platforms/pseries/hvcserver.c b/arch/powerpc/platforms/pseries/hvcserver.c index 22bfb5c89db9..fcf4b4cbeaf3 100644 --- a/arch/powerpc/platforms/pseries/hvcserver.c +++ b/arch/powerpc/platforms/pseries/hvcserver.c @@ -43,21 +43,21 @@ MODULE_VERSION(HVCS_ARCH_VERSION); static int hvcs_convert(long to_convert) { switch (to_convert) { - case H_Success: + case H_SUCCESS: return 0; - case H_Parameter: + case H_PARAMETER: return -EINVAL; - case H_Hardware: + case H_HARDWARE: return -EIO; - case H_Busy: - case H_LongBusyOrder1msec: - case H_LongBusyOrder10msec: - case H_LongBusyOrder100msec: - case H_LongBusyOrder1sec: - case H_LongBusyOrder10sec: - case H_LongBusyOrder100sec: + case H_BUSY: + case H_LONG_BUSY_ORDER_1_MSEC: + case H_LONG_BUSY_ORDER_10_MSEC: + case H_LONG_BUSY_ORDER_100_MSEC: + case H_LONG_BUSY_ORDER_1_SEC: + case H_LONG_BUSY_ORDER_10_SEC: + case H_LONG_BUSY_ORDER_100_SEC: return -EBUSY; - case H_Function: /* fall through */ + case H_FUNCTION: /* fall through */ default: return -EPERM; } diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c index 8952528d31ac..634b7d06d3cc 100644 --- a/arch/powerpc/platforms/pseries/lpar.c +++ b/arch/powerpc/platforms/pseries/lpar.c @@ -54,7 +54,8 @@ EXPORT_SYMBOL(plpar_hcall); EXPORT_SYMBOL(plpar_hcall_4out); EXPORT_SYMBOL(plpar_hcall_norets); EXPORT_SYMBOL(plpar_hcall_8arg_2ret); - +EXPORT_SYMBOL(plpar_hcall_7arg_7ret); +EXPORT_SYMBOL(plpar_hcall_9arg_9ret); extern void pSeries_find_serial_port(void); @@ -72,7 +73,7 @@ static void udbg_hvsi_putc(char c) do { rc = plpar_put_term_char(vtermno, sizeof(packet), packet); - } while (rc == H_Busy); + } while (rc == H_BUSY); } static long hvsi_udbg_buf_len; @@ -85,7 +86,7 @@ static int udbg_hvsi_getc_poll(void) if (hvsi_udbg_buf_len == 0) { rc = plpar_get_term_char(vtermno, &hvsi_udbg_buf_len, hvsi_udbg_buf); - if (rc != H_Success || hvsi_udbg_buf[0] != 0xff) { + if (rc != H_SUCCESS || hvsi_udbg_buf[0] != 0xff) { /* bad read or non-data packet */ hvsi_udbg_buf_len = 0; } else { @@ -139,7 +140,7 @@ static void udbg_putcLP(char c) buf[0] = c; do { rc = plpar_put_term_char(vtermno, 1, buf); - } while(rc == H_Busy); + } while(rc == H_BUSY); } /* Buffered chars getc */ @@ -158,7 +159,7 @@ static int udbg_getc_pollLP(void) /* get some more chars. */ inbuflen = 0; rc = plpar_get_term_char(vtermno, &inbuflen, buf); - if (rc != H_Success) + if (rc != H_SUCCESS) inbuflen = 0; /* otherwise inbuflen is garbage */ } if (inbuflen <= 0 || inbuflen > 16) { @@ -304,7 +305,7 @@ long pSeries_lpar_hpte_insert(unsigned long hpte_group, lpar_rc = plpar_hcall(H_ENTER, flags, hpte_group, hpte_v, hpte_r, &slot, &dummy0, &dummy1); - if (unlikely(lpar_rc == H_PTEG_Full)) { + if (unlikely(lpar_rc == H_PTEG_FULL)) { if (!(vflags & HPTE_V_BOLTED)) DBG_LOW(" full\n"); return -1; @@ -315,7 +316,7 @@ long pSeries_lpar_hpte_insert(unsigned long hpte_group, * will fail. However we must catch the failure in hash_page * or we will loop forever, so return -2 in this case. */ - if (unlikely(lpar_rc != H_Success)) { + if (unlikely(lpar_rc != H_SUCCESS)) { if (!(vflags & HPTE_V_BOLTED)) DBG_LOW(" lpar err %d\n", lpar_rc); return -2; @@ -346,9 +347,9 @@ static long pSeries_lpar_hpte_remove(unsigned long hpte_group) /* don't remove a bolted entry */ lpar_rc = plpar_pte_remove(H_ANDCOND, hpte_group + slot_offset, (0x1UL << 4), &dummy1, &dummy2); - if (lpar_rc == H_Success) + if (lpar_rc == H_SUCCESS) return i; - BUG_ON(lpar_rc != H_Not_Found); + BUG_ON(lpar_rc != H_NOT_FOUND); slot_offset++; slot_offset &= 0x7; @@ -391,14 +392,14 @@ static long pSeries_lpar_hpte_updatepp(unsigned long slot, lpar_rc = plpar_pte_protect(flags, slot, want_v & HPTE_V_AVPN); - if (lpar_rc == H_Not_Found) { + if (lpar_rc == H_NOT_FOUND) { DBG_LOW("not found !\n"); return -1; } DBG_LOW("ok\n"); - BUG_ON(lpar_rc != H_Success); + BUG_ON(lpar_rc != H_SUCCESS); return 0; } @@ -417,7 +418,7 @@ static unsigned long pSeries_lpar_hpte_getword0(unsigned long slot) lpar_rc = plpar_pte_read(flags, slot, &dword0, &dummy_word1); - BUG_ON(lpar_rc != H_Success); + BUG_ON(lpar_rc != H_SUCCESS); return dword0; } @@ -468,7 +469,7 @@ static void pSeries_lpar_hpte_updateboltedpp(unsigned long newpp, flags = newpp & 7; lpar_rc = plpar_pte_protect(flags, slot, 0); - BUG_ON(lpar_rc != H_Success); + BUG_ON(lpar_rc != H_SUCCESS); } static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long va, @@ -484,10 +485,10 @@ static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long va, want_v = hpte_encode_v(va, psize); lpar_rc = plpar_pte_remove(H_AVPN, slot, want_v & HPTE_V_AVPN, &dummy1, &dummy2); - if (lpar_rc == H_Not_Found) + if (lpar_rc == H_NOT_FOUND) return; - BUG_ON(lpar_rc != H_Success); + BUG_ON(lpar_rc != H_SUCCESS); } /* diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index b2fbf8ba8fbb..5eb55ef1c91c 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -463,7 +463,7 @@ static void pseries_dedicated_idle_sleep(void) * very low priority. The cede enables interrupts, which * doesn't matter here. */ - if (!lppaca[cpu ^ 1].idle || poll_pending() == H_Pending) + if (!lppaca[cpu ^ 1].idle || poll_pending() == H_PENDING) cede_processor(); out: diff --git a/arch/powerpc/platforms/pseries/vio.c b/arch/powerpc/platforms/pseries/vio.c index 866379b80c09..8e53e04ada8b 100644 --- a/arch/powerpc/platforms/pseries/vio.c +++ b/arch/powerpc/platforms/pseries/vio.c @@ -258,7 +258,7 @@ EXPORT_SYMBOL(vio_find_node); int vio_enable_interrupts(struct vio_dev *dev) { int rc = h_vio_signal(dev->unit_address, VIO_IRQ_ENABLE); - if (rc != H_Success) + if (rc != H_SUCCESS) printk(KERN_ERR "vio: Error 0x%x enabling interrupts\n", rc); return rc; } @@ -267,7 +267,7 @@ EXPORT_SYMBOL(vio_enable_interrupts); int vio_disable_interrupts(struct vio_dev *dev) { int rc = h_vio_signal(dev->unit_address, VIO_IRQ_DISABLE); - if (rc != H_Success) + if (rc != H_SUCCESS) printk(KERN_ERR "vio: Error 0x%x disabling interrupts\n", rc); return rc; } diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index 4864cb32be25..2d60ea30fed6 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -168,7 +168,7 @@ static int pSeriesLP_xirr_info_get(int n_cpu) unsigned long return_value; lpar_rc = plpar_xirr(&return_value); - if (lpar_rc != H_Success) + if (lpar_rc != H_SUCCESS) panic(" bad return code xirr - rc = %lx \n", lpar_rc); return (int)return_value; } @@ -179,7 +179,7 @@ static void pSeriesLP_xirr_info_set(int n_cpu, int value) unsigned long val64 = value & 0xffffffff; lpar_rc = plpar_eoi(val64); - if (lpar_rc != H_Success) + if (lpar_rc != H_SUCCESS) panic("bad return code EOI - rc = %ld, value=%lx\n", lpar_rc, val64); } @@ -189,7 +189,7 @@ void pSeriesLP_cppr_info(int n_cpu, u8 value) unsigned long lpar_rc; lpar_rc = plpar_cppr(value); - if (lpar_rc != H_Success) + if (lpar_rc != H_SUCCESS) panic("bad return code cppr - rc = %lx\n", lpar_rc); } @@ -198,7 +198,7 @@ static void pSeriesLP_qirr_info(int n_cpu , u8 value) unsigned long lpar_rc; lpar_rc = plpar_ipi(get_hard_smp_processor_id(n_cpu), value); - if (lpar_rc != H_Success) + if (lpar_rc != H_SUCCESS) panic("bad return code qirr - rc = %lx\n", lpar_rc); } diff --git a/arch/ppc/boot/lib/Makefile b/arch/ppc/boot/lib/Makefile index d4077e69086f..80c84d562fa4 100644 --- a/arch/ppc/boot/lib/Makefile +++ b/arch/ppc/boot/lib/Makefile @@ -3,7 +3,7 @@ # CFLAGS_kbd.o := -Idrivers/char -CFLAGS_vreset.o := -I$(srctree)/arch/ppc/boot/include +CFLAGS_vreset.o := -Iarch/ppc/boot/include zlib := infblock.c infcodes.c inffast.c inflate.c inftrees.c infutil.c diff --git a/arch/s390/Makefile b/arch/s390/Makefile index 6c6b197898d0..7bb16fb97d4f 100644 --- a/arch/s390/Makefile +++ b/arch/s390/Makefile @@ -67,7 +67,6 @@ cflags-$(CONFIG_WARN_STACK) += -mwarn-framesize=$(CONFIG_WARN_STACK_SIZE) endif CFLAGS += -mbackchain -msoft-float $(cflags-y) -CFLAGS += $(call cc-option,-finline-limit=10000) CFLAGS += -pipe -fno-strength-reduce -Wno-sign-compare AFLAGS += $(aflags-y) diff --git a/arch/s390/defconfig b/arch/s390/defconfig index f8d0cd540a06..f4dfc10026d2 100644 --- a/arch/s390/defconfig +++ b/arch/s390/defconfig @@ -1,10 +1,11 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.16-rc2 -# Wed Feb 8 10:44:39 2006 +# Linux kernel version: 2.6.17-rc1 +# Mon Apr 3 14:34:15 2006 # CONFIG_MMU=y CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_GENERIC_HWEIGHT=y CONFIG_GENERIC_CALIBRATE_DELAY=y CONFIG_S390=y @@ -30,8 +31,8 @@ CONFIG_AUDIT=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y # CONFIG_CPUSETS is not set +# CONFIG_RELAY is not set CONFIG_INITRAMFS_SOURCE="" -CONFIG_UID16=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set # CONFIG_EMBEDDED is not set CONFIG_KALLSYMS=y @@ -45,10 +46,6 @@ CONFIG_BASE_FULL=y CONFIG_FUTEX=y CONFIG_EPOLL=y CONFIG_SHMEM=y -CONFIG_CC_ALIGN_FUNCTIONS=0 -CONFIG_CC_ALIGN_LABELS=0 -CONFIG_CC_ALIGN_LOOPS=0 -CONFIG_CC_ALIGN_JUMPS=0 CONFIG_SLAB=y # CONFIG_TINY_SHMEM is not set CONFIG_BASE_SMALL=0 @@ -60,7 +57,6 @@ CONFIG_BASE_SMALL=0 CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y # CONFIG_MODULE_FORCE_UNLOAD is not set -CONFIG_OBSOLETE_MODPARM=y CONFIG_MODVERSIONS=y # CONFIG_MODULE_SRCVERSION_ALL is not set CONFIG_KMOD=y @@ -69,7 +65,7 @@ CONFIG_STOP_MACHINE=y # # Block layer # -# CONFIG_LBD is not set +# CONFIG_BLK_DEV_IO_TRACE is not set # # IO Schedulers @@ -91,17 +87,20 @@ CONFIG_DEFAULT_IOSCHED="deadline" # # Processor type and features # -# CONFIG_64BIT is not set +CONFIG_64BIT=y CONFIG_SMP=y CONFIG_NR_CPUS=32 CONFIG_HOTPLUG_CPU=y -CONFIG_MATHEMU=y +CONFIG_DEFAULT_MIGRATION_COST=1000000 +CONFIG_COMPAT=y +CONFIG_SYSVIPC_COMPAT=y +CONFIG_BINFMT_ELF32=y # # Code generation options # -CONFIG_MARCH_G5=y -# CONFIG_MARCH_Z900 is not set +# CONFIG_MARCH_G5 is not set +CONFIG_MARCH_Z900=y # CONFIG_MARCH_Z990 is not set CONFIG_PACK_STACK=y # CONFIG_SMALL_STACK is not set @@ -143,7 +142,7 @@ CONFIG_VIRT_CPU_ACCOUNTING=y # CONFIG_APPLDATA_BASE is not set CONFIG_NO_IDLE_HZ=y CONFIG_NO_IDLE_HZ_INIT=y -# CONFIG_KEXEC is not set +CONFIG_KEXEC=y # # Networking @@ -173,6 +172,7 @@ CONFIG_IP_FIB_HASH=y # CONFIG_INET_AH is not set # CONFIG_INET_ESP is not set # CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set # CONFIG_INET_TUNNEL is not set CONFIG_INET_DIAG=y CONFIG_INET_TCP_DIAG=y @@ -180,9 +180,11 @@ CONFIG_INET_TCP_DIAG=y CONFIG_TCP_CONG_BIC=y CONFIG_IPV6=y # CONFIG_IPV6_PRIVACY is not set +# CONFIG_IPV6_ROUTER_PREF is not set # CONFIG_INET6_AH is not set # CONFIG_INET6_ESP is not set # CONFIG_INET6_IPCOMP is not set +# CONFIG_INET6_XFRM_TUNNEL is not set # CONFIG_INET6_TUNNEL is not set # CONFIG_IPV6_TUNNEL is not set # CONFIG_NETFILTER is not set @@ -275,6 +277,11 @@ CONFIG_PREVENT_FIRMWARE_BUILD=y # CONFIG_FW_LOADER is not set # CONFIG_DEBUG_DRIVER is not set +# +# Connector - unified userspace <-> kernelspace linker +# +# CONFIG_CONNECTOR is not set + # # SCSI device support # @@ -340,8 +347,7 @@ CONFIG_DASD_PROFILE=y CONFIG_DASD_ECKD=y CONFIG_DASD_FBA=y CONFIG_DASD_DIAG=y -CONFIG_DASD_EER=m -# CONFIG_DASD_CMB is not set +CONFIG_DASD_EER=y # CONFIG_ATA_OVER_ETH is not set # @@ -354,6 +360,7 @@ CONFIG_MD_RAID0=m CONFIG_MD_RAID1=m # CONFIG_MD_RAID10 is not set CONFIG_MD_RAID5=m +# CONFIG_MD_RAID5_RESHAPE is not set # CONFIG_MD_RAID6 is not set CONFIG_MD_MULTIPATH=m # CONFIG_MD_FAULTY is not set @@ -404,6 +411,7 @@ CONFIG_S390_TAPE_BLOCK=y # S/390 tape hardware support # CONFIG_S390_TAPE_34XX=m +# CONFIG_S390_TAPE_3590 is not set # CONFIG_VMLOGRDR is not set # CONFIG_VMCP is not set # CONFIG_MONREADER is not set @@ -529,7 +537,6 @@ CONFIG_SYSFS=y CONFIG_TMPFS=y # CONFIG_HUGETLB_PAGE is not set CONFIG_RAMFS=y -# CONFIG_RELAYFS_FS is not set # CONFIG_CONFIGFS_FS is not set # @@ -619,14 +626,15 @@ CONFIG_LOG_BUF_SHIFT=17 # CONFIG_DETECT_SOFTLOCKUP is not set # CONFIG_SCHEDSTATS is not set # CONFIG_DEBUG_SLAB is not set -# CONFIG_DEBUG_PREEMPT is not set +CONFIG_DEBUG_PREEMPT=y CONFIG_DEBUG_MUTEXES=y -# CONFIG_DEBUG_SPINLOCK is not set -# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_SPINLOCK_SLEEP=y # CONFIG_DEBUG_KOBJECT is not set # CONFIG_DEBUG_INFO is not set CONFIG_DEBUG_FS=y # CONFIG_DEBUG_VM is not set +# CONFIG_UNWIND_INFO is not set CONFIG_FORCED_INLINING=y # CONFIG_RCU_TORTURE_TEST is not set diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 2b8841f85534..343120c9223d 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -801,7 +801,7 @@ void __init smp_prepare_cpus(unsigned int max_cpus) */ print_cpu_info(&S390_lowcore.cpu_data); - for_each_cpu(i) { + for_each_possible_cpu(i) { lowcore_ptr[i] = (struct _lowcore *) __get_free_pages(GFP_KERNEL|GFP_DMA, sizeof(void*) == 8 ? 1 : 0); @@ -831,7 +831,7 @@ void __init smp_prepare_cpus(unsigned int max_cpus) #endif set_prefix((u32)(unsigned long) lowcore_ptr[smp_processor_id()]); - for_each_cpu(cpu) + for_each_possible_cpu(cpu) if (cpu != smp_processor_id()) smp_create_idle(cpu); } @@ -868,7 +868,7 @@ static int __init topology_init(void) int cpu; int ret; - for_each_cpu(cpu) { + for_each_possible_cpu(cpu) { ret = register_cpu(&per_cpu(cpu_devices, cpu), cpu, NULL); if (ret) printk(KERN_WARNING "topology_init: register_cpu %d " diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index 58583f459471..2bcecf422573 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -527,6 +527,11 @@ config CPU_HAS_SR_RB See for further information on SR.RB and register banking in the kernel in general. +config NODES_SHIFT + int + default "1" + depends on NEED_MULTIPLE_NODES + endmenu menu "Boot options" diff --git a/arch/sh/kernel/cpu/init.c b/arch/sh/kernel/cpu/init.c index cf94e8ef17c5..868e68b28880 100644 --- a/arch/sh/kernel/cpu/init.c +++ b/arch/sh/kernel/cpu/init.c @@ -30,7 +30,7 @@ static int x##_disabled __initdata = 0; \ static int __init x##_setup(char *opts) \ { \ x##_disabled = 1; \ - return 0; \ + return 1; \ } \ __setup("no" __stringify(x), x##_setup); diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c index 7ee4ca203616..bb229ef030f3 100644 --- a/arch/sh/kernel/setup.c +++ b/arch/sh/kernel/setup.c @@ -401,7 +401,7 @@ static int __init topology_init(void) { int cpu_id; - for_each_cpu(cpu_id) + for_each_possible_cpu(cpu_id) register_cpu(&cpu[cpu_id], cpu_id, NULL); return 0; diff --git a/arch/sh/kernel/sh_ksyms.c b/arch/sh/kernel/sh_ksyms.c index 1cf94a618be3..d5d032533a8b 100644 --- a/arch/sh/kernel/sh_ksyms.c +++ b/arch/sh/kernel/sh_ksyms.c @@ -37,7 +37,6 @@ EXPORT_SYMBOL(disable_irq_nosync); EXPORT_SYMBOL(irq_desc); EXPORT_SYMBOL(no_irq_type); -EXPORT_SYMBOL(strpbrk); EXPORT_SYMBOL(strstr); EXPORT_SYMBOL(strlen); EXPORT_SYMBOL(strnlen); diff --git a/arch/sh64/kernel/sh_ksyms.c b/arch/sh64/kernel/sh_ksyms.c index de29c45f23a7..6f3a1c946339 100644 --- a/arch/sh64/kernel/sh_ksyms.c +++ b/arch/sh64/kernel/sh_ksyms.c @@ -41,7 +41,6 @@ EXPORT_SYMBOL(kernel_thread); /* Networking helper routines. */ EXPORT_SYMBOL(csum_partial_copy); -EXPORT_SYMBOL(strpbrk); EXPORT_SYMBOL(strstr); #ifdef CONFIG_VT diff --git a/arch/sparc/kernel/smp.c b/arch/sparc/kernel/smp.c index 2be812115197..a93f5da6855d 100644 --- a/arch/sparc/kernel/smp.c +++ b/arch/sparc/kernel/smp.c @@ -244,7 +244,7 @@ int setup_profiling_timer(unsigned int multiplier) return -EINVAL; spin_lock_irqsave(&prof_setup_lock, flags); - for_each_cpu(i) { + for_each_possible_cpu(i) { load_profile_irq(i, lvl14_resolution / multiplier); prof_multiplier(i) = multiplier; } diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c index 2c21d7907635..ec1c9687d679 100644 --- a/arch/sparc/kernel/sparc_ksyms.c +++ b/arch/sparc/kernel/sparc_ksyms.c @@ -263,7 +263,6 @@ EXPORT_SYMBOL(strcmp); EXPORT_SYMBOL(strncmp); EXPORT_SYMBOL(strchr); EXPORT_SYMBOL(strrchr); -EXPORT_SYMBOL(strpbrk); EXPORT_SYMBOL(strstr); EXPORT_SYMBOL(page_kernel); diff --git a/arch/sparc/kernel/systbls.S b/arch/sparc/kernel/systbls.S index 768de64b371f..fbbec5e761c6 100644 --- a/arch/sparc/kernel/systbls.S +++ b/arch/sparc/kernel/systbls.S @@ -64,13 +64,13 @@ sys_call_table: /*215*/ .long sys_ipc, sys_sigreturn, sys_clone, sys_ioprio_get, sys_adjtimex /*220*/ .long sys_sigprocmask, sys_ni_syscall, sys_delete_module, sys_ni_syscall, sys_getpgid /*225*/ .long sys_bdflush, sys_sysfs, sys_nis_syscall, sys_setfsuid16, sys_setfsgid16 -/*230*/ .long sys_select, sys_time, sys_nis_syscall, sys_stime, sys_statfs64 +/*230*/ .long sys_select, sys_time, sys_splice, sys_stime, sys_statfs64 /* "We are the Knights of the Forest of Ni!!" */ /*235*/ .long sys_fstatfs64, sys_llseek, sys_mlock, sys_munlock, sys_mlockall /*240*/ .long sys_munlockall, sys_sched_setparam, sys_sched_getparam, sys_sched_setscheduler, sys_sched_getscheduler /*245*/ .long sys_sched_yield, sys_sched_get_priority_max, sys_sched_get_priority_min, sys_sched_rr_get_interval, sys_nanosleep /*250*/ .long sparc_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nfsservctl -/*255*/ .long sys_nis_syscall, sys_clock_settime, sys_clock_gettime, sys_clock_getres, sys_clock_nanosleep +/*255*/ .long sys_sync_file_range, sys_clock_settime, sys_clock_gettime, sys_clock_getres, sys_clock_nanosleep /*260*/ .long sys_sched_getaffinity, sys_sched_setaffinity, sys_timer_settime, sys_timer_gettime, sys_timer_getoverrun /*265*/ .long sys_timer_delete, sys_timer_create, sys_nis_syscall, sys_io_setup, sys_io_destroy /*270*/ .long sys_io_submit, sys_io_cancel, sys_io_getevents, sys_mq_open, sys_mq_unlink diff --git a/arch/sparc/math-emu/Makefile b/arch/sparc/math-emu/Makefile index f84a9a6162be..8136987977f4 100644 --- a/arch/sparc/math-emu/Makefile +++ b/arch/sparc/math-emu/Makefile @@ -5,4 +5,4 @@ obj-y := math.o EXTRA_AFLAGS := -ansi -EXTRA_CFLAGS = -I. -I$(TOPDIR)/include/math-emu -w +EXTRA_CFLAGS = -I. -Iinclude/math-emu -w diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig index d1e2fc566486..648047a0bce3 100644 --- a/arch/sparc64/Kconfig +++ b/arch/sparc64/Kconfig @@ -194,6 +194,9 @@ endchoice endmenu +config ARCH_SELECT_MEMORY_MODEL + def_bool y + config ARCH_SPARSEMEM_ENABLE def_bool y diff --git a/arch/sparc64/defconfig b/arch/sparc64/defconfig index 900fb0b940d8..1317380fa937 100644 --- a/arch/sparc64/defconfig +++ b/arch/sparc64/defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Linux kernel version: 2.6.16 -# Sun Mar 26 14:58:11 2006 +# Sun Apr 2 19:31:04 2006 # CONFIG_SPARC=y CONFIG_SPARC64=y @@ -180,6 +180,7 @@ CONFIG_SYN_COOKIES=y CONFIG_INET_AH=y CONFIG_INET_ESP=y CONFIG_INET_IPCOMP=y +CONFIG_INET_XFRM_TUNNEL=y CONFIG_INET_TUNNEL=y CONFIG_INET_DIAG=y CONFIG_INET_TCP_DIAG=y @@ -203,6 +204,7 @@ CONFIG_IPV6_ROUTE_INFO=y CONFIG_INET6_AH=m CONFIG_INET6_ESP=m CONFIG_INET6_IPCOMP=m +CONFIG_INET6_XFRM_TUNNEL=m CONFIG_INET6_TUNNEL=m CONFIG_IPV6_TUNNEL=m # CONFIG_NETFILTER is not set @@ -308,7 +310,6 @@ CONFIG_BLK_DEV_NBD=m # CONFIG_BLK_DEV_SX8 is not set CONFIG_BLK_DEV_UB=m # CONFIG_BLK_DEV_RAM is not set -CONFIG_BLK_DEV_RAM_COUNT=16 # CONFIG_BLK_DEV_INITRD is not set CONFIG_CDROM_PKTCDVD=m CONFIG_CDROM_PKTCDVD_BUFFERS=8 @@ -449,6 +450,7 @@ CONFIG_MD_RAID0=m CONFIG_MD_RAID1=m CONFIG_MD_RAID10=m CONFIG_MD_RAID5=m +# CONFIG_MD_RAID5_RESHAPE is not set CONFIG_MD_RAID6=m CONFIG_MD_MULTIPATH=m # CONFIG_MD_FAULTY is not set @@ -741,9 +743,7 @@ CONFIG_I2C_ALGOBIT=y # CONFIG_SENSORS_PCF8574 is not set # CONFIG_SENSORS_PCA9539 is not set # CONFIG_SENSORS_PCF8591 is not set -# CONFIG_SENSORS_RTC8564 is not set # CONFIG_SENSORS_MAX6875 is not set -# CONFIG_RTC_X1205_I2C is not set # CONFIG_I2C_DEBUG_CORE is not set # CONFIG_I2C_DEBUG_ALGO is not set # CONFIG_I2C_DEBUG_BUS is not set @@ -826,6 +826,7 @@ CONFIG_FB_CFB_FILLRECT=y CONFIG_FB_CFB_COPYAREA=y CONFIG_FB_CFB_IMAGEBLIT=y # CONFIG_FB_MACMODES is not set +# CONFIG_FB_FIRMWARE_EDID is not set CONFIG_FB_MODE_HELPERS=y CONFIG_FB_TILEBLITTING=y # CONFIG_FB_CIRRUS is not set @@ -837,7 +838,6 @@ CONFIG_FB_TILEBLITTING=y # CONFIG_FB_NVIDIA is not set # CONFIG_FB_RIVA is not set # CONFIG_FB_MATROX is not set -# CONFIG_FB_RADEON_OLD is not set CONFIG_FB_RADEON=y CONFIG_FB_RADEON_I2C=y # CONFIG_FB_RADEON_DEBUG is not set @@ -923,6 +923,7 @@ CONFIG_SND_MTPAV=m # PCI devices # # CONFIG_SND_AD1889 is not set +# CONFIG_SND_ALS300 is not set CONFIG_SND_ALI5451=m # CONFIG_SND_ATIIXP is not set # CONFIG_SND_ATIIXP_MODEM is not set @@ -954,6 +955,7 @@ CONFIG_SND_ALI5451=m # CONFIG_SND_MIXART is not set # CONFIG_SND_NM256 is not set # CONFIG_SND_PCXHR is not set +# CONFIG_SND_RIPTIDE is not set # CONFIG_SND_RME32 is not set # CONFIG_SND_RME96 is not set # CONFIG_SND_RME9652 is not set @@ -1107,6 +1109,11 @@ CONFIG_USB_HIDDEV=y # # CONFIG_MMC is not set +# +# LED devices +# +# CONFIG_NEW_LEDS is not set + # # InfiniBand support # @@ -1116,6 +1123,11 @@ CONFIG_USB_HIDDEV=y # EDAC - error detection and reporting (RAS) (EXPERIMENTAL) # +# +# Real Time Clock +# +# CONFIG_RTC_CLASS is not set + # # Misc Linux/SPARC drivers # diff --git a/arch/sparc64/kernel/pci_sun4v.c b/arch/sparc64/kernel/pci_sun4v.c index 9372d4f376d5..9e94db2573a2 100644 --- a/arch/sparc64/kernel/pci_sun4v.c +++ b/arch/sparc64/kernel/pci_sun4v.c @@ -1092,7 +1092,7 @@ void sun4v_pci_init(int node, char *model_name) } } - for_each_cpu(i) { + for_each_possible_cpu(i) { unsigned long page = get_zeroed_page(GFP_ATOMIC); if (!page) diff --git a/arch/sparc64/kernel/ptrace.c b/arch/sparc64/kernel/ptrace.c index eb93e9c52846..49e6dedd027d 100644 --- a/arch/sparc64/kernel/ptrace.c +++ b/arch/sparc64/kernel/ptrace.c @@ -244,6 +244,13 @@ asmlinkage void do_ptrace(struct pt_regs *regs) } switch(request) { + case PTRACE_PEEKUSR: + if (addr != 0) + pt_error_return(regs, EIO); + else + pt_succ_return(regs, 0); + goto out_tsk; + case PTRACE_PEEKTEXT: /* read word at location addr. */ case PTRACE_PEEKDATA: { unsigned long tmp64; @@ -602,6 +609,22 @@ asmlinkage void do_ptrace(struct pt_regs *regs) /* PTRACE_DUMPCORE unsupported... */ + case PTRACE_GETEVENTMSG: { + int err; + + if (test_thread_flag(TIF_32BIT)) + err = put_user(child->ptrace_message, + (unsigned int __user *) data); + else + err = put_user(child->ptrace_message, + (unsigned long __user *) data); + if (err) + pt_error_return(regs, -err); + else + pt_succ_return(regs, 0); + break; + } + default: { int err = ptrace_request(child, request, addr, data); if (err) diff --git a/arch/sparc64/kernel/setup.c b/arch/sparc64/kernel/setup.c index 7d0e67c1ce50..005167f82419 100644 --- a/arch/sparc64/kernel/setup.c +++ b/arch/sparc64/kernel/setup.c @@ -535,7 +535,7 @@ static int __init topology_init(void) while (!cpu_find_by_instance(ncpus_probed, NULL, NULL)) ncpus_probed++; - for_each_cpu(i) { + for_each_possible_cpu(i) { struct cpu *p = kzalloc(sizeof(*p), GFP_KERNEL); if (p) { register_cpu(p, i, NULL); diff --git a/arch/sparc64/kernel/smp.c b/arch/sparc64/kernel/smp.c index 7dc28a484268..90eaca3ec9a6 100644 --- a/arch/sparc64/kernel/smp.c +++ b/arch/sparc64/kernel/smp.c @@ -745,12 +745,21 @@ struct call_data_struct { int wait; }; -static DEFINE_SPINLOCK(call_lock); +static __cacheline_aligned_in_smp DEFINE_SPINLOCK(call_lock); static struct call_data_struct *call_data; extern unsigned long xcall_call_function; -/* +/** + * smp_call_function(): Run a function on all other CPUs. + * @func: The function to run. This must be fast and non-blocking. + * @info: An arbitrary pointer to pass to the function. + * @nonatomic: currently unused. + * @wait: If true, wait (atomically) until function has completed on other CPUs. + * + * Returns 0 on success, else a negative status code. Does not return until + * remote CPUs are nearly ready to execute <> or are or have executed. + * * You must not call this function with disabled interrupts or from a * hardware interrupt handler or from a bottom half handler. */ @@ -759,7 +768,6 @@ static int smp_call_function_mask(void (*func)(void *info), void *info, { struct call_data_struct data; int cpus; - long timeout; /* Can deadlock when called with interrupts disabled */ WARN_ON(irqs_disabled()); @@ -777,31 +785,18 @@ static int smp_call_function_mask(void (*func)(void *info), void *info, goto out_unlock; call_data = &data; + mb(); smp_cross_call_masked(&xcall_call_function, 0, 0, 0, mask); - /* - * Wait for other cpus to complete function or at - * least snap the call data. - */ - timeout = 1000000; - while (atomic_read(&data.finished) != cpus) { - if (--timeout <= 0) - goto out_timeout; - barrier(); - udelay(1); - } + /* Wait for response */ + while (atomic_read(&data.finished) != cpus) + cpu_relax(); out_unlock: spin_unlock(&call_lock); return 0; - -out_timeout: - spin_unlock(&call_lock); - printk("XCALL: Remote cpus not responding, ncpus=%d finished=%d\n", - cpus, atomic_read(&data.finished)); - return 0; } int smp_call_function(void (*func)(void *info), void *info, @@ -830,9 +825,16 @@ void smp_call_function_client(int irq, struct pt_regs *regs) static void tsb_sync(void *info) { + struct trap_per_cpu *tp = &trap_block[raw_smp_processor_id()]; struct mm_struct *mm = info; - if (current->active_mm == mm) + /* It is not valid to test "currrent->active_mm == mm" here. + * + * The value of "current" is not changed atomically with + * switch_mm(). But that's OK, we just need to check the + * current cpu's trap block PGD physical address. + */ + if (tp->pgd_paddr == __pa(mm->pgd)) tsb_context_switch(mm); } @@ -1278,7 +1280,7 @@ int setup_profiling_timer(unsigned int multiplier) return -EINVAL; spin_lock_irqsave(&prof_setup_lock, flags); - for_each_cpu(i) + for_each_possible_cpu(i) prof_multiplier(i) = multiplier; current_tick_offset = (timer_tick_offset / multiplier); spin_unlock_irqrestore(&prof_setup_lock, flags); @@ -1306,12 +1308,12 @@ void __init smp_prepare_cpus(unsigned int max_cpus) } } - for_each_cpu(i) { + for_each_possible_cpu(i) { if (tlb_type == hypervisor) { int j; /* XXX get this mapping from machine description */ - for_each_cpu(j) { + for_each_possible_cpu(j) { if ((j >> 2) == (i >> 2)) cpu_set(j, cpu_sibling_map[i]); } diff --git a/arch/sparc64/kernel/sparc64_ksyms.c b/arch/sparc64/kernel/sparc64_ksyms.c index f5e8db1de76b..62d8a99271ea 100644 --- a/arch/sparc64/kernel/sparc64_ksyms.c +++ b/arch/sparc64/kernel/sparc64_ksyms.c @@ -276,7 +276,6 @@ EXPORT_SYMBOL(__prom_getsibling); EXPORT_SYMBOL(strlen); EXPORT_SYMBOL(__strlen_user); EXPORT_SYMBOL(__strnlen_user); -EXPORT_SYMBOL(strpbrk); #ifdef CONFIG_SOLARIS_EMUL_MODULE EXPORT_SYMBOL(linux_sparc_syscall); diff --git a/arch/sparc64/kernel/sys32.S b/arch/sparc64/kernel/sys32.S index c4a1cef4b1e5..86dd5cb81e09 100644 --- a/arch/sparc64/kernel/sys32.S +++ b/arch/sparc64/kernel/sys32.S @@ -136,6 +136,8 @@ SIGN1(sys32_getpeername, sys_getpeername, %o0) SIGN1(sys32_getsockname, sys_getsockname, %o0) SIGN2(sys32_ioprio_get, sys_ioprio_get, %o0, %o1) SIGN3(sys32_ioprio_set, sys_ioprio_set, %o0, %o1, %o2) +SIGN2(sys32_splice, sys_splice, %o0, %o1) +SIGN2(sys32_sync_file_range, compat_sync_file_range, %o0, %o5) .globl sys32_mmap2 sys32_mmap2: diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index 2e906bad56fa..31030bf00f1a 100644 --- a/arch/sparc64/kernel/sys_sparc32.c +++ b/arch/sparc64/kernel/sys_sparc32.c @@ -1069,3 +1069,11 @@ long sys32_lookup_dcookie(unsigned long cookie_high, return sys_lookup_dcookie((cookie_high << 32) | cookie_low, buf, len); } + +long compat_sync_file_range(int fd, unsigned long off_high, unsigned long off_low, unsigned long nb_high, unsigned long nb_low, int flags) +{ + return sys_sync_file_range(fd, + (off_high << 32) | off_low, + (nb_high << 32) | nb_low, + flags); +} diff --git a/arch/sparc64/kernel/systbls.S b/arch/sparc64/kernel/systbls.S index 3b250f2318fd..857b82c82875 100644 --- a/arch/sparc64/kernel/systbls.S +++ b/arch/sparc64/kernel/systbls.S @@ -66,12 +66,12 @@ sys_call_table32: .word sys32_ipc, sys32_sigreturn, sys_clone, sys32_ioprio_get, compat_sys_adjtimex /*220*/ .word sys32_sigprocmask, sys_ni_syscall, sys32_delete_module, sys_ni_syscall, sys32_getpgid .word sys32_bdflush, sys32_sysfs, sys_nis_syscall, sys32_setfsuid16, sys32_setfsgid16 -/*230*/ .word sys32_select, compat_sys_time, sys_nis_syscall, compat_sys_stime, compat_sys_statfs64 +/*230*/ .word sys32_select, compat_sys_time, sys32_splice, compat_sys_stime, compat_sys_statfs64 .word compat_sys_fstatfs64, sys_llseek, sys_mlock, sys_munlock, sys32_mlockall /*240*/ .word sys_munlockall, sys32_sched_setparam, sys32_sched_getparam, sys32_sched_setscheduler, sys32_sched_getscheduler .word sys_sched_yield, sys32_sched_get_priority_max, sys32_sched_get_priority_min, sys32_sched_rr_get_interval, compat_sys_nanosleep /*250*/ .word sys32_mremap, sys32_sysctl, sys32_getsid, sys_fdatasync, sys32_nfsservctl - .word sys_ni_syscall, compat_sys_clock_settime, compat_sys_clock_gettime, compat_sys_clock_getres, sys32_clock_nanosleep + .word sys32_sync_file_range, compat_sys_clock_settime, compat_sys_clock_gettime, compat_sys_clock_getres, sys32_clock_nanosleep /*260*/ .word compat_sys_sched_getaffinity, compat_sys_sched_setaffinity, sys32_timer_settime, compat_sys_timer_gettime, sys_timer_getoverrun .word sys_timer_delete, compat_sys_timer_create, sys_ni_syscall, compat_sys_io_setup, sys_io_destroy /*270*/ .word sys32_io_submit, sys_io_cancel, compat_sys_io_getevents, sys32_mq_open, sys_mq_unlink @@ -135,12 +135,12 @@ sys_call_table: .word sys_ipc, sys_nis_syscall, sys_clone, sys_ioprio_get, sys_adjtimex /*220*/ .word sys_nis_syscall, sys_ni_syscall, sys_delete_module, sys_ni_syscall, sys_getpgid .word sys_bdflush, sys_sysfs, sys_nis_syscall, sys_setfsuid, sys_setfsgid -/*230*/ .word sys_select, sys_nis_syscall, sys_nis_syscall, sys_stime, sys_statfs64 +/*230*/ .word sys_select, sys_nis_syscall, sys_splice, sys_stime, sys_statfs64 .word sys_fstatfs64, sys_llseek, sys_mlock, sys_munlock, sys_mlockall /*240*/ .word sys_munlockall, sys_sched_setparam, sys_sched_getparam, sys_sched_setscheduler, sys_sched_getscheduler .word sys_sched_yield, sys_sched_get_priority_max, sys_sched_get_priority_min, sys_sched_rr_get_interval, sys_nanosleep /*250*/ .word sys64_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nfsservctl - .word sys_ni_syscall, sys_clock_settime, sys_clock_gettime, sys_clock_getres, sys_clock_nanosleep + .word sys_sync_file_range, sys_clock_settime, sys_clock_gettime, sys_clock_getres, sys_clock_nanosleep /*260*/ .word sys_sched_getaffinity, sys_sched_setaffinity, sys_timer_settime, sys_timer_gettime, sys_timer_getoverrun .word sys_timer_delete, sys_timer_create, sys_ni_syscall, sys_io_setup, sys_io_destroy /*270*/ .word sys_io_submit, sys_io_cancel, sys_io_getevents, sys_mq_open, sys_mq_unlink diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c index ff090bb9734b..2793a5d82380 100644 --- a/arch/sparc64/kernel/traps.c +++ b/arch/sparc64/kernel/traps.c @@ -1130,9 +1130,9 @@ static void cheetah_log_errors(struct pt_regs *regs, struct cheetah_err_info *in (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(), afsr, afar, (afsr & CHAFSR_TL1) ? 1 : 0); - printk("%s" "ERROR(%d): TPC[%016lx] TNPC[%016lx] TSTATE[%016lx]\n", + printk("%s" "ERROR(%d): TPC[%lx] TNPC[%lx] O7[%lx] TSTATE[%lx]\n", (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(), - regs->tpc, regs->tnpc, regs->tstate); + regs->tpc, regs->tnpc, regs->u_regs[UREG_I7], regs->tstate); printk("%s" "ERROR(%d): M_SYND(%lx), E_SYND(%lx)%s%s\n", (recoverable ? KERN_WARNING : KERN_CRIT), smp_processor_id(), (afsr & CHAFSR_M_SYNDROME) >> CHAFSR_M_SYNDROME_SHIFT, diff --git a/arch/sparc64/mm/fault.c b/arch/sparc64/mm/fault.c index 0db2f7d9fab5..6e002aacb961 100644 --- a/arch/sparc64/mm/fault.c +++ b/arch/sparc64/mm/fault.c @@ -327,8 +327,12 @@ asmlinkage void __kprobes do_sparc64_fault(struct pt_regs *regs) insn = get_fault_insn(regs, 0); if (!insn) goto continue_fault; + /* All loads, stores and atomics have bits 30 and 31 both set + * in the instruction. Bit 21 is set in all stores, but we + * have to avoid prefetches which also have bit 21 set. + */ if ((insn & 0xc0200000) == 0xc0200000 && - (insn & 0x1780000) != 0x1680000) { + (insn & 0x01780000) != 0x01680000) { /* Don't bother updating thread struct value, * because update_mmu_cache only cares which tlb * the access came from. diff --git a/arch/sparc64/mm/hugetlbpage.c b/arch/sparc64/mm/hugetlbpage.c index 074620d413d4..fbbbebbad8a4 100644 --- a/arch/sparc64/mm/hugetlbpage.c +++ b/arch/sparc64/mm/hugetlbpage.c @@ -198,6 +198,13 @@ pte_t *huge_pte_alloc(struct mm_struct *mm, unsigned long addr) pmd_t *pmd; pte_t *pte = NULL; + /* We must align the address, because our caller will run + * set_huge_pte_at() on whatever we return, which writes out + * all of the sub-ptes for the hugepage range. So we have + * to give it the first such sub-pte. + */ + addr &= HPAGE_MASK; + pgd = pgd_offset(mm, addr); pud = pud_alloc(mm, pgd, addr); if (pud) { diff --git a/arch/um/Kconfig b/arch/um/Kconfig index 5982fe2753e0..05fbb20636cb 100644 --- a/arch/um/Kconfig +++ b/arch/um/Kconfig @@ -22,6 +22,9 @@ config SBUS config PCI bool +config PCMCIA + bool + config GENERIC_CALIBRATE_DELAY bool default y diff --git a/arch/um/Makefile b/arch/um/Makefile index 8d14c7a831be..a508e7a02891 100644 --- a/arch/um/Makefile +++ b/arch/um/Makefile @@ -20,7 +20,7 @@ core-y += $(ARCH_DIR)/kernel/ \ # Have to precede the include because the included Makefiles reference them. SYMLINK_HEADERS := archparam.h system.h sigcontext.h processor.h ptrace.h \ - module.h vm-flags.h elf.h ldt.h + module.h vm-flags.h elf.h host_ldt.h SYMLINK_HEADERS := $(foreach header,$(SYMLINK_HEADERS),include/asm-um/$(header)) # XXX: The "os" symlink is only used by arch/um/include/os.h, which includes @@ -129,7 +129,7 @@ CPPFLAGS_vmlinux.lds = -U$(SUBARCH) \ -DSTART=$(START) -DELF_ARCH=$(ELF_ARCH) \ -DELF_FORMAT="$(ELF_FORMAT)" $(CPP_MODE-y) \ -DKERNEL_STACK_SIZE=$(STACK_SIZE) \ - -DUNMAP_PATH=arch/um/sys-$(SUBARCH)/unmap_fin.o + -DUNMAP_PATH=arch/um/sys-$(SUBARCH)/unmap.o #The wrappers will select whether using "malloc" or the kernel allocator. LINK_WRAPS = -Wl,--wrap,malloc -Wl,--wrap,free -Wl,--wrap,calloc @@ -150,8 +150,7 @@ CLEAN_FILES += linux x.i gmon.out $(ARCH_DIR)/include/uml-config.h \ $(ARCH_DIR)/include/user_constants.h \ $(ARCH_DIR)/include/kern_constants.h $(ARCH_DIR)/Kconfig.arch -MRPROPER_FILES += $(SYMLINK_HEADERS) $(ARCH_SYMLINKS) \ - $(addprefix $(ARCH_DIR)/kernel/,$(KERN_SYMLINKS)) $(ARCH_DIR)/os +MRPROPER_FILES += $(ARCH_SYMLINKS) archclean: @find . \( -name '*.bb' -o -name '*.bbg' -o -name '*.da' \ @@ -160,6 +159,7 @@ archclean: $(SYMLINK_HEADERS): @echo ' SYMLINK $@' ifneq ($(KBUILD_SRC),) + $(Q)mkdir -p $(objtree)/include/asm-um $(Q)ln -fsn $(srctree)/include/asm-um/$(basename $(notdir $@))-$(SUBARCH)$(suffix $@) $@ else $(Q)cd $(TOPDIR)/$(dir $@) ; \ @@ -169,7 +169,7 @@ endif include/asm-um/arch: @echo ' SYMLINK $@' ifneq ($(KBUILD_SRC),) - $(Q)mkdir -p include/asm-um + $(Q)mkdir -p $(objtree)/include/asm-um $(Q)ln -fsn $(srctree)/include/asm-$(SUBARCH) include/asm-um/arch else $(Q)cd $(TOPDIR)/include/asm-um && ln -sf ../asm-$(SUBARCH) arch diff --git a/arch/um/Makefile-x86_64 b/arch/um/Makefile-x86_64 index 38df311e75dc..dfd88b652fbe 100644 --- a/arch/um/Makefile-x86_64 +++ b/arch/um/Makefile-x86_64 @@ -1,7 +1,7 @@ # Copyright 2003 - 2004 Pathscale, Inc # Released under the GPL -libs-y += arch/um/sys-x86_64/ +core-y += arch/um/sys-x86_64/ START := 0x60000000 #We #undef __x86_64__ for kernelspace, not for userspace where diff --git a/arch/um/drivers/cow.h b/arch/um/drivers/cow.h index 04e3958266e0..dc36b222100b 100644 --- a/arch/um/drivers/cow.h +++ b/arch/um/drivers/cow.h @@ -46,7 +46,7 @@ extern int file_reader(__u64 offset, char *buf, int len, void *arg); extern int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg, __u32 *version_out, char **backing_file_out, time_t *mtime_out, - __u64 *size_out, int *sectorsize_out, + unsigned long long *size_out, int *sectorsize_out, __u32 *align_out, int *bitmap_offset_out); extern int write_cow_header(char *cow_file, int fd, char *backing_file, diff --git a/arch/um/drivers/cow_sys.h b/arch/um/drivers/cow_sys.h index 94de4ead4f7a..7a5b4afde692 100644 --- a/arch/um/drivers/cow_sys.h +++ b/arch/um/drivers/cow_sys.h @@ -28,7 +28,7 @@ static inline int cow_seek_file(int fd, __u64 offset) return(os_seek_file(fd, offset)); } -static inline int cow_file_size(char *file, __u64 *size_out) +static inline int cow_file_size(char *file, unsigned long long *size_out) { return(os_file_size(file, size_out)); } diff --git a/arch/um/drivers/cow_user.c b/arch/um/drivers/cow_user.c index 61951b721268..0ec4052db9c5 100644 --- a/arch/um/drivers/cow_user.c +++ b/arch/um/drivers/cow_user.c @@ -17,30 +17,34 @@ #define PATH_LEN_V1 256 -struct cow_header_v1 { - int magic; - int version; - char backing_file[PATH_LEN_V1]; - time_t mtime; - __u64 size; - int sectorsize; -}; +typedef __u32 time32_t; -#define PATH_LEN_V2 MAXPATHLEN +struct cow_header_v1 { + __s32 magic; + __s32 version; + char backing_file[PATH_LEN_V1]; + time32_t mtime; + __u64 size; + __s32 sectorsize; +} __attribute__((packed)); + +/* Define PATH_LEN_V3 as the usual value of MAXPATHLEN, just hard-code it in + * case other systems have different values for MAXPATHLEN. + * + * The same must hold for V2 - we want file format compatibility, not anything + * else. + */ +#define PATH_LEN_V3 4096 +#define PATH_LEN_V2 PATH_LEN_V3 struct cow_header_v2 { __u32 magic; __u32 version; char backing_file[PATH_LEN_V2]; - time_t mtime; + time32_t mtime; __u64 size; - int sectorsize; -}; - -/* Define PATH_LEN_V3 as the usual value of MAXPATHLEN, just hard-code it in - * case other systems have different values for MAXPATHLEN - */ -#define PATH_LEN_V3 4096 + __s32 sectorsize; +} __attribute__((packed)); /* Changes from V2 - * PATH_LEN_V3 as described above @@ -66,6 +70,15 @@ struct cow_header_v2 { * Fixed (finally!) the rounding bug */ +/* Until Dec2005, __attribute__((packed)) was left out from the below + * definition, leading on 64-bit systems to 4 bytes of padding after mtime, to + * align size to 8-byte alignment. This shifted all fields above (no padding + * was present on 32-bit, no other padding was added). + * + * However, this _can be detected_: it means that cow_format (always 0 until + * now) is shifted onto the first 4 bytes of backing_file, where it is otherwise + * impossible to find 4 zeros. -bb */ + struct cow_header_v3 { __u32 magic; __u32 version; @@ -75,6 +88,18 @@ struct cow_header_v3 { __u32 alignment; __u32 cow_format; char backing_file[PATH_LEN_V3]; +} __attribute__((packed)); + +/* This is the broken layout used by some 64-bit binaries. */ +struct cow_header_v3_broken { + __u32 magic; + __u32 version; + __s64 mtime; + __u64 size; + __u32 sectorsize; + __u32 alignment; + __u32 cow_format; + char backing_file[PATH_LEN_V3]; }; /* COW format definitions - for now, we have only the usual COW bitmap */ @@ -84,6 +109,7 @@ union cow_header { struct cow_header_v1 v1; struct cow_header_v2 v2; struct cow_header_v3 v3; + struct cow_header_v3_broken v3_b; }; #define COW_MAGIC 0x4f4f4f4d /* MOOO */ @@ -184,8 +210,9 @@ int write_cow_header(char *cow_file, int fd, char *backing_file, err = -EINVAL; if(strlen(backing_file) > sizeof(header->backing_file) - 1){ + /* Below, %zd is for a size_t value */ cow_printf("Backing file name \"%s\" is too long - names are " - "limited to %d characters\n", backing_file, + "limited to %zd characters\n", backing_file, sizeof(header->backing_file) - 1); goto out_free; } @@ -300,7 +327,8 @@ int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg, *align_out = *sectorsize_out; file = header->v2.backing_file; } - else if(version == 3){ + /* This is very subtle - see above at union cow_header definition */ + else if(version == 3 && (*((int*)header->v3.backing_file) != 0)){ if(n < sizeof(header->v3)){ cow_printf("read_cow_header - failed to read V3 " "header\n"); @@ -310,9 +338,43 @@ int read_cow_header(int (*reader)(__u64, char *, int, void *), void *arg, *size_out = ntohll(header->v3.size); *sectorsize_out = ntohl(header->v3.sectorsize); *align_out = ntohl(header->v3.alignment); + if (*align_out == 0) { + cow_printf("read_cow_header - invalid COW header, " + "align == 0\n"); + } *bitmap_offset_out = ROUND_UP(sizeof(header->v3), *align_out); file = header->v3.backing_file; } + else if(version == 3){ + cow_printf("read_cow_header - broken V3 file with" + " 64-bit layout - recovering content.\n"); + + if(n < sizeof(header->v3_b)){ + cow_printf("read_cow_header - failed to read V3 " + "header\n"); + goto out; + } + + /* this was used until Dec2005 - 64bits are needed to represent + * 2038+. I.e. we can safely do this truncating cast. + * + * Additionally, we must use ntohl() instead of ntohll(), since + * the program used to use the former (tested - I got mtime + * mismatch "0 vs whatever"). + * + * Ever heard about bug-to-bug-compatibility ? ;-) */ + *mtime_out = (time32_t) ntohl(header->v3_b.mtime); + + *size_out = ntohll(header->v3_b.size); + *sectorsize_out = ntohl(header->v3_b.sectorsize); + *align_out = ntohl(header->v3_b.alignment); + if (*align_out == 0) { + cow_printf("read_cow_header - invalid COW header, " + "align == 0\n"); + } + *bitmap_offset_out = ROUND_UP(sizeof(header->v3_b), *align_out); + file = header->v3_b.backing_file; + } else { cow_printf("read_cow_header - invalid COW version\n"); goto out; diff --git a/arch/um/drivers/daemon_kern.c b/arch/um/drivers/daemon_kern.c index a61b7b46bc02..53d09ed78b42 100644 --- a/arch/um/drivers/daemon_kern.c +++ b/arch/um/drivers/daemon_kern.c @@ -95,18 +95,7 @@ static struct transport daemon_transport = { static int register_daemon(void) { register_transport(&daemon_transport); - return(1); + return 0; } __initcall(register_daemon); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-file-style: "linux" - * End: - */ diff --git a/arch/um/drivers/harddog_kern.c b/arch/um/drivers/harddog_kern.c index 49acb2badf32..d18a974735e6 100644 --- a/arch/um/drivers/harddog_kern.c +++ b/arch/um/drivers/harddog_kern.c @@ -104,7 +104,7 @@ static int harddog_release(struct inode *inode, struct file *file) extern int ping_watchdog(int fd); -static ssize_t harddog_write(struct file *file, const char *data, size_t len, +static ssize_t harddog_write(struct file *file, const char __user *data, size_t len, loff_t *ppos) { /* @@ -118,6 +118,7 @@ static ssize_t harddog_write(struct file *file, const char *data, size_t len, static int harddog_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { + void __user *argp= (void __user *)arg; static struct watchdog_info ident = { WDIOC_SETTIMEOUT, 0, @@ -127,13 +128,12 @@ static int harddog_ioctl(struct inode *inode, struct file *file, default: return -ENOTTY; case WDIOC_GETSUPPORT: - if(copy_to_user((struct harddog_info *)arg, &ident, - sizeof(ident))) + if(copy_to_user(argp, &ident, sizeof(ident))) return -EFAULT; return 0; case WDIOC_GETSTATUS: case WDIOC_GETBOOTSTATUS: - return put_user(0,(int *)arg); + return put_user(0,(int __user *)argp); case WDIOC_KEEPALIVE: return(ping_watchdog(harddog_out_fd)); } diff --git a/arch/um/drivers/hostaudio_kern.c b/arch/um/drivers/hostaudio_kern.c index 59602b81b240..37232f908cd7 100644 --- a/arch/um/drivers/hostaudio_kern.c +++ b/arch/um/drivers/hostaudio_kern.c @@ -67,8 +67,8 @@ MODULE_PARM_DESC(mixer, MIXER_HELP); /* /dev/dsp file operations */ -static ssize_t hostaudio_read(struct file *file, char *buffer, size_t count, - loff_t *ppos) +static ssize_t hostaudio_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) { struct hostaudio_state *state = file->private_data; void *kbuf; @@ -94,7 +94,7 @@ static ssize_t hostaudio_read(struct file *file, char *buffer, size_t count, return(err); } -static ssize_t hostaudio_write(struct file *file, const char *buffer, +static ssize_t hostaudio_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { struct hostaudio_state *state = file->private_data; @@ -152,7 +152,7 @@ static int hostaudio_ioctl(struct inode *inode, struct file *file, case SNDCTL_DSP_CHANNELS: case SNDCTL_DSP_SUBDIVIDE: case SNDCTL_DSP_SETFRAGMENT: - if(get_user(data, (int *) arg)) + if(get_user(data, (int __user *) arg)) return(-EFAULT); break; default: @@ -168,7 +168,7 @@ static int hostaudio_ioctl(struct inode *inode, struct file *file, case SNDCTL_DSP_CHANNELS: case SNDCTL_DSP_SUBDIVIDE: case SNDCTL_DSP_SETFRAGMENT: - if(put_user(data, (int *) arg)) + if(put_user(data, (int __user *) arg)) return(-EFAULT); break; default: diff --git a/arch/um/drivers/mcast_kern.c b/arch/um/drivers/mcast_kern.c index c9b078fba03e..3a7af18cf944 100644 --- a/arch/um/drivers/mcast_kern.c +++ b/arch/um/drivers/mcast_kern.c @@ -124,18 +124,7 @@ static struct transport mcast_transport = { static int register_mcast(void) { register_transport(&mcast_transport); - return(1); + return 0; } __initcall(register_mcast); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-file-style: "linux" - * End: - */ diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c index 1488816588ea..6d7173fc55a3 100644 --- a/arch/um/drivers/mconsole_kern.c +++ b/arch/um/drivers/mconsole_kern.c @@ -20,6 +20,8 @@ #include "linux/namei.h" #include "linux/proc_fs.h" #include "linux/syscalls.h" +#include "linux/list.h" +#include "linux/mm.h" #include "linux/console.h" #include "asm/irq.h" #include "asm/uaccess.h" @@ -60,7 +62,7 @@ static void mc_work_proc(void *unused) unsigned long flags; while(!list_empty(&mc_requests)){ - local_save_flags(flags); + local_irq_save(flags); req = list_entry(mc_requests.next, struct mconsole_entry, list); list_del(&req->list); @@ -85,7 +87,7 @@ static irqreturn_t mconsole_interrupt(int irq, void *dev_id, if(req.cmd->context == MCONSOLE_INTR) (*req.cmd->handler)(&req); else { - new = kmalloc(sizeof(*new), GFP_ATOMIC); + new = kmalloc(sizeof(*new), GFP_NOWAIT); if(new == NULL) mconsole_reply(&req, "Out of memory", 1, 0); else { @@ -347,6 +349,141 @@ static struct mc_device *mconsole_find_dev(char *name) return(NULL); } +#define UNPLUGGED_PER_PAGE \ + ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long)) + +struct unplugged_pages { + struct list_head list; + void *pages[UNPLUGGED_PER_PAGE]; +}; + +static unsigned long long unplugged_pages_count = 0; +static struct list_head unplugged_pages = LIST_HEAD_INIT(unplugged_pages); +static int unplug_index = UNPLUGGED_PER_PAGE; + +static int mem_config(char *str) +{ + unsigned long long diff; + int err = -EINVAL, i, add; + char *ret; + + if(str[0] != '=') + goto out; + + str++; + if(str[0] == '-') + add = 0; + else if(str[0] == '+'){ + add = 1; + } + else goto out; + + str++; + diff = memparse(str, &ret); + if(*ret != '\0') + goto out; + + diff /= PAGE_SIZE; + + for(i = 0; i < diff; i++){ + struct unplugged_pages *unplugged; + void *addr; + + if(add){ + if(list_empty(&unplugged_pages)) + break; + + unplugged = list_entry(unplugged_pages.next, + struct unplugged_pages, list); + if(unplug_index > 0) + addr = unplugged->pages[--unplug_index]; + else { + list_del(&unplugged->list); + addr = unplugged; + unplug_index = UNPLUGGED_PER_PAGE; + } + + free_page((unsigned long) addr); + unplugged_pages_count--; + } + else { + struct page *page; + + page = alloc_page(GFP_ATOMIC); + if(page == NULL) + break; + + unplugged = page_address(page); + if(unplug_index == UNPLUGGED_PER_PAGE){ + list_add(&unplugged->list, &unplugged_pages); + unplug_index = 0; + } + else { + struct list_head *entry = unplugged_pages.next; + addr = unplugged; + + unplugged = list_entry(entry, + struct unplugged_pages, + list); + unplugged->pages[unplug_index++] = addr; + err = os_drop_memory(addr, PAGE_SIZE); + if(err) + printk("Failed to release memory - " + "errno = %d\n", err); + } + + unplugged_pages_count++; + } + } + + err = 0; +out: + return err; +} + +static int mem_get_config(char *name, char *str, int size, char **error_out) +{ + char buf[sizeof("18446744073709551615")]; + int len = 0; + + sprintf(buf, "%ld", uml_physmem); + CONFIG_CHUNK(str, size, len, buf, 1); + + return len; +} + +static int mem_id(char **str, int *start_out, int *end_out) +{ + *start_out = 0; + *end_out = 0; + + return 0; +} + +static int mem_remove(int n) +{ + return -EBUSY; +} + +static struct mc_device mem_mc = { + .name = "mem", + .config = mem_config, + .get_config = mem_get_config, + .id = mem_id, + .remove = mem_remove, +}; + +static int mem_mc_init(void) +{ + if(can_drop_memory()) + mconsole_register_dev(&mem_mc); + else printk("Can't release memory to the host - memory hotplug won't " + "be supported\n"); + return 0; +} + +__initcall(mem_mc_init); + #define CONFIG_BUF_SIZE 64 static void mconsole_get_config(int (*get_config)(char *, char *, int, @@ -478,7 +615,7 @@ static void console_write(struct console *console, const char *string, return; while(1){ - n = min(len, ARRAY_SIZE(console_buf) - console_index); + n = min((size_t) len, ARRAY_SIZE(console_buf) - console_index); strncpy(&console_buf[console_index], string, n); console_index += n; string += n; @@ -517,7 +654,6 @@ static void with_console(struct mc_request *req, void (*proc)(void *), struct mconsole_entry entry; unsigned long flags; - INIT_LIST_HEAD(&entry.list); entry.request = *req; list_add(&entry.list, &clients); spin_lock_irqsave(&console_lock, flags); diff --git a/arch/um/drivers/net_user.c b/arch/um/drivers/net_user.c index 0e2f06187ea7..0a7786e00cfb 100644 --- a/arch/um/drivers/net_user.c +++ b/arch/um/drivers/net_user.c @@ -182,7 +182,9 @@ static int change_tramp(char **argv, char *output, int output_len) pe_data.stdout = fds[1]; pid = run_helper(change_pre_exec, &pe_data, argv, NULL); - read_output(fds[0], output, output_len); + if (pid > 0) /* Avoid hang as we won't get data in failure case. */ + read_output(fds[0], output, output_len); + os_close_file(fds[0]); os_close_file(fds[1]); diff --git a/arch/um/drivers/pcap_kern.c b/arch/um/drivers/pcap_kern.c index 07c80f2156ef..466ff2c2f918 100644 --- a/arch/um/drivers/pcap_kern.c +++ b/arch/um/drivers/pcap_kern.c @@ -106,18 +106,7 @@ static struct transport pcap_transport = { static int register_pcap(void) { register_transport(&pcap_transport); - return(1); + return 0; } __initcall(register_pcap); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-file-style: "linux" - * End: - */ diff --git a/arch/um/drivers/slip_kern.c b/arch/um/drivers/slip_kern.c index a62f5ef445cf..163ee0d5f75e 100644 --- a/arch/um/drivers/slip_kern.c +++ b/arch/um/drivers/slip_kern.c @@ -93,18 +93,7 @@ static struct transport slip_transport = { static int register_slip(void) { register_transport(&slip_transport); - return(1); + return 0; } __initcall(register_slip); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-file-style: "linux" - * End: - */ diff --git a/arch/um/drivers/slirp_kern.c b/arch/um/drivers/slirp_kern.c index 33d7982be5d3..95e50c943e14 100644 --- a/arch/um/drivers/slirp_kern.c +++ b/arch/um/drivers/slirp_kern.c @@ -77,7 +77,7 @@ static int slirp_setup(char *str, char **mac_out, void *data) int i=0; *init = ((struct slirp_init) - { argw : { { "slirp", NULL } } }); + { .argw = { { "slirp", NULL } } }); str = split_if_spec(str, mac_out, NULL); @@ -116,18 +116,7 @@ static struct transport slirp_transport = { static int register_slirp(void) { register_transport(&slirp_transport); - return(1); + return 0; } __initcall(register_slirp); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-file-style: "linux" - * End: - */ diff --git a/arch/um/drivers/slirp_user.c b/arch/um/drivers/slirp_user.c index b94c66114bc8..33c5f6e625e8 100644 --- a/arch/um/drivers/slirp_user.c +++ b/arch/um/drivers/slirp_user.c @@ -104,7 +104,7 @@ static void slirp_close(int fd, void *data) } if(err == 0) { - printk("slirp_close: process %d has not exited\n"); + printk("slirp_close: process %d has not exited\n", pri->pid); return; } diff --git a/arch/um/drivers/ubd_kern.c b/arch/um/drivers/ubd_kern.c index 0336575d2448..0897852b09a3 100644 --- a/arch/um/drivers/ubd_kern.c +++ b/arch/um/drivers/ubd_kern.c @@ -891,7 +891,7 @@ int ubd_driver_init(void){ SA_INTERRUPT, "ubd", ubd_dev); if(err != 0) printk(KERN_ERR "um_request_irq failed - errno = %d\n", -err); - return(err); + return 0; } device_initcall(ubd_driver_init); diff --git a/arch/um/include/kern_util.h b/arch/um/include/kern_util.h index 07176d92e1c9..efa3d33c0be6 100644 --- a/arch/um/include/kern_util.h +++ b/arch/um/include/kern_util.h @@ -116,7 +116,7 @@ extern void *get_current(void); extern struct task_struct *get_task(int pid, int require); extern void machine_halt(void); extern int is_syscall(unsigned long addr); -extern void arch_switch(void); + extern void free_irq(unsigned int, void *); extern int cpu(void); diff --git a/arch/um/include/line.h b/arch/um/include/line.h index 6f4d680dc1d4..6ac0f8252e21 100644 --- a/arch/um/include/line.h +++ b/arch/um/include/line.h @@ -58,23 +58,17 @@ struct line { }; #define LINE_INIT(str, d) \ - { init_str : str, \ - init_pri : INIT_STATIC, \ - valid : 1, \ - throttled : 0, \ - lock : SPIN_LOCK_UNLOCKED, \ - buffer : NULL, \ - head : NULL, \ - tail : NULL, \ - sigio : 0, \ - driver : d, \ - have_irq : 0 } + { .init_str = str, \ + .init_pri = INIT_STATIC, \ + .valid = 1, \ + .lock = SPIN_LOCK_UNLOCKED, \ + .driver = d } struct lines { int num; }; -#define LINES_INIT(n) { num : n } +#define LINES_INIT(n) { .num = n } extern void line_close(struct tty_struct *tty, struct file * filp); extern int line_open(struct line *lines, struct tty_struct *tty); diff --git a/arch/um/include/mem_user.h b/arch/um/include/mem_user.h index a1064c5823bf..a54514d2cc3a 100644 --- a/arch/um/include/mem_user.h +++ b/arch/um/include/mem_user.h @@ -49,7 +49,6 @@ extern int iomem_size; extern unsigned long host_task_size; extern unsigned long task_size; -extern void check_devanon(void); extern int init_mem_user(void); extern void setup_memory(void *entry); extern unsigned long find_iomem(char *driver, unsigned long *len_out); diff --git a/arch/um/include/os.h b/arch/um/include/os.h index d3d1bc6074ef..f88856c28a66 100644 --- a/arch/um/include/os.h +++ b/arch/um/include/os.h @@ -13,6 +13,7 @@ #include "kern_util.h" #include "skas/mm_id.h" #include "irq_user.h" +#include "sysdep/tls.h" #define OS_TYPE_FILE 1 #define OS_TYPE_DIR 2 @@ -172,6 +173,7 @@ extern int os_fchange_dir(int fd); extern void os_early_checks(void); extern int can_do_skas(void); extern void os_check_bugs(void); +extern void check_host_supports_tls(int *supports_tls, int *tls_min); /* Make sure they are clear when running in TT mode. Required by * SEGV_MAYBE_FIXABLE */ @@ -205,6 +207,8 @@ extern int os_map_memory(void *virt, int fd, unsigned long long off, extern int os_protect_memory(void *addr, unsigned long len, int r, int w, int x); extern int os_unmap_memory(void *addr, int len); +extern int os_drop_memory(void *addr, int length); +extern int can_drop_memory(void); extern void os_flush_stdout(void); /* tt.c @@ -234,8 +238,12 @@ extern int run_helper_thread(int (*proc)(void *), void *arg, int stack_order); extern int helper_wait(int pid); -/* umid.c */ +/* tls.c */ +extern int os_set_thread_area(user_desc_t *info, int pid); +extern int os_get_thread_area(user_desc_t *info, int pid); + +/* umid.c */ extern int umid_file_name(char *name, char *buf, int len); extern int set_umid(char *name); extern char *get_umid(void); diff --git a/arch/um/include/sysdep-i386/checksum.h b/arch/um/include/sysdep-i386/checksum.h index 7d3d202d7fff..052bb061a978 100644 --- a/arch/um/include/sysdep-i386/checksum.h +++ b/arch/um/include/sysdep-i386/checksum.h @@ -48,7 +48,8 @@ unsigned int csum_partial_copy_nocheck(const unsigned char *src, unsigned char * */ static __inline__ -unsigned int csum_partial_copy_from_user(const unsigned char *src, unsigned char *dst, +unsigned int csum_partial_copy_from_user(const unsigned char __user *src, + unsigned char *dst, int len, int sum, int *err_ptr) { if(copy_from_user(dst, src, len)){ @@ -192,7 +193,7 @@ static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr, */ #define HAVE_CSUM_COPY_USER static __inline__ unsigned int csum_and_copy_to_user(const unsigned char *src, - unsigned char *dst, + unsigned char __user *dst, int len, int sum, int *err_ptr) { if (access_ok(VERIFY_WRITE, dst, len)){ diff --git a/arch/um/include/sysdep-i386/ptrace.h b/arch/um/include/sysdep-i386/ptrace.h index c8ee9559f3ab..6670cc992ecb 100644 --- a/arch/um/include/sysdep-i386/ptrace.h +++ b/arch/um/include/sysdep-i386/ptrace.h @@ -14,7 +14,12 @@ #define MAX_REG_NR (UM_FRAME_SIZE / sizeof(unsigned long)) #define MAX_REG_OFFSET (UM_FRAME_SIZE) +#ifdef UML_CONFIG_PT_PROXY extern void update_debugregs(int seq); +#else +static inline void update_debugregs(int seq) {} +#endif + /* syscall emulation path in ptrace */ diff --git a/arch/um/include/sysdep-i386/tls.h b/arch/um/include/sysdep-i386/tls.h new file mode 100644 index 000000000000..918fd3c5ff9c --- /dev/null +++ b/arch/um/include/sysdep-i386/tls.h @@ -0,0 +1,32 @@ +#ifndef _SYSDEP_TLS_H +#define _SYSDEP_TLS_H + +# ifndef __KERNEL__ + +/* Change name to avoid conflicts with the original one from , which + * may be named user_desc (but in 2.4 and in header matching its API was named + * modify_ldt_ldt_s). */ + +typedef struct um_dup_user_desc { + unsigned int entry_number; + unsigned int base_addr; + unsigned int limit; + unsigned int seg_32bit:1; + unsigned int contents:2; + unsigned int read_exec_only:1; + unsigned int limit_in_pages:1; + unsigned int seg_not_present:1; + unsigned int useable:1; +} user_desc_t; + +# else /* __KERNEL__ */ + +# include +typedef struct user_desc user_desc_t; + +# endif /* __KERNEL__ */ + +#define GDT_ENTRY_TLS_MIN_I386 6 +#define GDT_ENTRY_TLS_MIN_X86_64 12 + +#endif /* _SYSDEP_TLS_H */ diff --git a/arch/um/include/sysdep-x86_64/tls.h b/arch/um/include/sysdep-x86_64/tls.h new file mode 100644 index 000000000000..35f19f25bd3b --- /dev/null +++ b/arch/um/include/sysdep-x86_64/tls.h @@ -0,0 +1,29 @@ +#ifndef _SYSDEP_TLS_H +#define _SYSDEP_TLS_H + +# ifndef __KERNEL__ + +/* Change name to avoid conflicts with the original one from , which + * may be named user_desc (but in 2.4 and in header matching its API was named + * modify_ldt_ldt_s). */ + +typedef struct um_dup_user_desc { + unsigned int entry_number; + unsigned int base_addr; + unsigned int limit; + unsigned int seg_32bit:1; + unsigned int contents:2; + unsigned int read_exec_only:1; + unsigned int limit_in_pages:1; + unsigned int seg_not_present:1; + unsigned int useable:1; + unsigned int lm:1; +} user_desc_t; + +# else /* __KERNEL__ */ + +# include +typedef struct user_desc user_desc_t; + +# endif /* __KERNEL__ */ +#endif /* _SYSDEP_TLS_H */ diff --git a/arch/um/include/tt/tt.h b/arch/um/include/tt/tt.h index 808521980186..acb8356e1f98 100644 --- a/arch/um/include/tt/tt.h +++ b/arch/um/include/tt/tt.h @@ -19,7 +19,8 @@ extern int fork_tramp(void *sig_stack); extern int do_proc_op(void *t, int proc_id); extern int tracer(int (*init_proc)(void *), void *sp); extern void attach_process(int pid); -extern void tracer_panic(char *format, ...); +extern void tracer_panic(char *format, ...) + __attribute__ ((format (printf, 1, 2))); extern void set_init_pid(int pid); extern int set_user_mode(void *task); extern void set_tracing(void *t, int tracing); diff --git a/arch/um/include/user.h b/arch/um/include/user.h index 91b0ac4ad88c..39f8c8801076 100644 --- a/arch/um/include/user.h +++ b/arch/um/include/user.h @@ -6,8 +6,10 @@ #ifndef __USER_H__ #define __USER_H__ -extern void panic(const char *fmt, ...); -extern int printk(const char *fmt, ...); +extern void panic(const char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); +extern int printk(const char *fmt, ...) + __attribute__ ((format (printf, 1, 2))); extern void schedule(void); extern void *um_kmalloc(int size); extern void *um_kmalloc_atomic(int size); diff --git a/arch/um/include/user_util.h b/arch/um/include/user_util.h index 992a7e1e0fca..802d7842514d 100644 --- a/arch/um/include/user_util.h +++ b/arch/um/include/user_util.h @@ -8,6 +8,9 @@ #include "sysdep/ptrace.h" +/* Copied from kernel.h */ +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + #define CATCH_EINTR(expr) while ((errno = 0, ((expr) < 0)) && (errno == EINTR)) extern int mode_tt; @@ -31,7 +34,7 @@ extern unsigned long uml_physmem; extern unsigned long uml_reserved; extern unsigned long end_vm; extern unsigned long start_vm; -extern unsigned long highmem; +extern unsigned long long highmem; extern char host_info[]; @@ -52,7 +55,8 @@ extern int get_pty(void); extern void *um_kmalloc(int size); extern int switcheroo(int fd, int prot, void *from, void *to, int size); extern void do_exec(int old_pid, int new_pid); -extern void tracer_panic(char *msg, ...); +extern void tracer_panic(char *msg, ...) + __attribute__ ((format (printf, 1, 2))); extern int detach(int pid, int sig); extern int attach(int pid); extern void kill_child_dead(int pid); diff --git a/arch/um/kernel/exec_kern.c b/arch/um/kernel/exec_kern.c index 1ca84319317d..c0cb627bf594 100644 --- a/arch/um/kernel/exec_kern.c +++ b/arch/um/kernel/exec_kern.c @@ -22,6 +22,7 @@ void flush_thread(void) { + arch_flush_thread(¤t->thread.arch); CHOOSE_MODE(flush_thread_tt(), flush_thread_skas()); } @@ -58,14 +59,14 @@ long um_execve(char *file, char __user *__user *argv, char __user *__user *env) return(err); } -long sys_execve(char *file, char __user *__user *argv, +long sys_execve(char __user *file, char __user *__user *argv, char __user *__user *env) { long error; char *filename; lock_kernel(); - filename = getname((char __user *) file); + filename = getname(file); error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; error = execve1(filename, argv, env); @@ -74,14 +75,3 @@ long sys_execve(char *file, char __user *__user *argv, unlock_kernel(); return(error); } - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-file-style: "linux" - * End: - */ diff --git a/arch/um/kernel/ksyms.c b/arch/um/kernel/ksyms.c index 7713e7a6f476..432cf0b97a13 100644 --- a/arch/um/kernel/ksyms.c +++ b/arch/um/kernel/ksyms.c @@ -39,7 +39,6 @@ EXPORT_SYMBOL(um_virt_to_phys); EXPORT_SYMBOL(mode_tt); EXPORT_SYMBOL(handle_page_fault); EXPORT_SYMBOL(find_iomem); -EXPORT_SYMBOL(end_iomem); #ifdef CONFIG_MODE_TT EXPORT_SYMBOL(strncpy_from_user_tt); @@ -89,12 +88,10 @@ EXPORT_SYMBOL(dump_thread); EXPORT_SYMBOL(do_gettimeofday); EXPORT_SYMBOL(do_settimeofday); -/* This is here because UML expands open to sys_open, not to a system +/* This is here because UML expands lseek to sys_lseek, not to a system * call instruction. */ -EXPORT_SYMBOL(sys_open); EXPORT_SYMBOL(sys_lseek); -EXPORT_SYMBOL(sys_read); EXPORT_SYMBOL(sys_wait4); #ifdef CONFIG_SMP diff --git a/arch/um/kernel/mem.c b/arch/um/kernel/mem.c index 92cce96b5e24..44e41a35f000 100644 --- a/arch/um/kernel/mem.c +++ b/arch/um/kernel/mem.c @@ -30,7 +30,7 @@ extern char __binary_start; unsigned long *empty_zero_page = NULL; unsigned long *empty_bad_page = NULL; pgd_t swapper_pg_dir[PTRS_PER_PGD]; -unsigned long highmem; +unsigned long long highmem; int kmalloc_ok = 0; static unsigned long brk_end; diff --git a/arch/um/kernel/process_kern.c b/arch/um/kernel/process_kern.c index 3113cab8675e..f6a5a502120b 100644 --- a/arch/um/kernel/process_kern.c +++ b/arch/um/kernel/process_kern.c @@ -156,9 +156,25 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp, unsigned long stack_top, struct task_struct * p, struct pt_regs *regs) { + int ret; + p->thread = (struct thread_struct) INIT_THREAD; - return(CHOOSE_MODE_PROC(copy_thread_tt, copy_thread_skas, nr, - clone_flags, sp, stack_top, p, regs)); + ret = CHOOSE_MODE_PROC(copy_thread_tt, copy_thread_skas, nr, + clone_flags, sp, stack_top, p, regs); + + if (ret || !current->thread.forking) + goto out; + + clear_flushed_tls(p); + + /* + * Set a new TLS for the child thread? + */ + if (clone_flags & CLONE_SETTLS) + ret = arch_copy_tls(p); + +out: + return ret; } void initial_thread_cb(void (*proc)(void *), void *arg) @@ -185,10 +201,6 @@ void default_idle(void) { CHOOSE_MODE(uml_idle_timer(), (void) 0); - atomic_inc(&init_mm.mm_count); - current->mm = &init_mm; - current->active_mm = &init_mm; - while(1){ /* endless idle loop with no priority at all */ @@ -407,7 +419,7 @@ static int proc_read_sysemu(char *buf, char **start, off_t offset, int size,int return strlen(buf); } -static int proc_write_sysemu(struct file *file,const char *buf, unsigned long count,void *data) +static int proc_write_sysemu(struct file *file,const char __user *buf, unsigned long count,void *data) { char tmp[2]; diff --git a/arch/um/kernel/ptrace.c b/arch/um/kernel/ptrace.c index 98e09395c093..60d2eda995c1 100644 --- a/arch/um/kernel/ptrace.c +++ b/arch/um/kernel/ptrace.c @@ -46,6 +46,7 @@ extern int poke_user(struct task_struct * child, long addr, long data); long arch_ptrace(struct task_struct *child, long request, long addr, long data) { int i, ret; + unsigned long __user *p = (void __user *)(unsigned long)data; switch (request) { /* when I and D space are separate, these will need to be fixed. */ @@ -58,7 +59,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); if (copied != sizeof(tmp)) break; - ret = put_user(tmp, (unsigned long __user *) data); + ret = put_user(tmp, p); break; } @@ -136,15 +137,13 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) #ifdef PTRACE_GETREGS case PTRACE_GETREGS: { /* Get all gp regs from the child. */ - if (!access_ok(VERIFY_WRITE, (unsigned long *)data, - MAX_REG_OFFSET)) { + if (!access_ok(VERIFY_WRITE, p, MAX_REG_OFFSET)) { ret = -EIO; break; } for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) { - __put_user(getreg(child, i), - (unsigned long __user *) data); - data += sizeof(long); + __put_user(getreg(child, i), p); + p++; } ret = 0; break; @@ -153,15 +152,14 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) #ifdef PTRACE_SETREGS case PTRACE_SETREGS: { /* Set all gp regs in the child. */ unsigned long tmp = 0; - if (!access_ok(VERIFY_READ, (unsigned *)data, - MAX_REG_OFFSET)) { + if (!access_ok(VERIFY_READ, p, MAX_REG_OFFSET)) { ret = -EIO; break; } for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) { - __get_user(tmp, (unsigned long __user *) data); + __get_user(tmp, p); putreg(child, i, tmp); - data += sizeof(long); + p++; } ret = 0; break; @@ -187,14 +185,23 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) ret = set_fpxregs(data, child); break; #endif + case PTRACE_GET_THREAD_AREA: + ret = ptrace_get_thread_area(child, addr, + (struct user_desc __user *) data); + break; + + case PTRACE_SET_THREAD_AREA: + ret = ptrace_set_thread_area(child, addr, + (struct user_desc __user *) data); + break; + case PTRACE_FAULTINFO: { - /* Take the info from thread->arch->faultinfo, - * but transfer max. sizeof(struct ptrace_faultinfo). - * On i386, ptrace_faultinfo is smaller! - */ - ret = copy_to_user((unsigned long __user *) data, - &child->thread.arch.faultinfo, - sizeof(struct ptrace_faultinfo)); + /* Take the info from thread->arch->faultinfo, + * but transfer max. sizeof(struct ptrace_faultinfo). + * On i386, ptrace_faultinfo is smaller! + */ + ret = copy_to_user(p, &child->thread.arch.faultinfo, + sizeof(struct ptrace_faultinfo)); if(ret) break; break; @@ -204,8 +211,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) case PTRACE_LDT: { struct ptrace_ldt ldt; - if(copy_from_user(&ldt, (unsigned long __user *) data, - sizeof(ldt))){ + if(copy_from_user(&ldt, p, sizeof(ldt))){ ret = -EIO; break; } diff --git a/arch/um/kernel/skas/process_kern.c b/arch/um/kernel/skas/process_kern.c index 3f70a2e12f06..2135eaf98a93 100644 --- a/arch/um/kernel/skas/process_kern.c +++ b/arch/um/kernel/skas/process_kern.c @@ -35,6 +35,8 @@ void switch_to_skas(void *prev, void *next) switch_threads(&from->thread.mode.skas.switch_buf, to->thread.mode.skas.switch_buf); + arch_switch_to_skas(current->thread.prev_sched, current); + if(current->pid == 0) switch_timers(1); } @@ -89,10 +91,17 @@ void fork_handler(int sig) panic("blech"); schedule_tail(current->thread.prev_sched); + + /* XXX: if interrupt_end() calls schedule, this call to + * arch_switch_to_skas isn't needed. We could want to apply this to + * improve performance. -bb */ + arch_switch_to_skas(current->thread.prev_sched, current); + current->thread.prev_sched = NULL; /* Handle any immediate reschedules or signals */ interrupt_end(); + userspace(¤t->thread.regs.regs); } @@ -109,6 +118,8 @@ int copy_thread_skas(int nr, unsigned long clone_flags, unsigned long sp, if(sp != 0) REGS_SP(p->thread.regs.regs.skas.regs) = sp; handler = fork_handler; + + arch_copy_thread(¤t->thread.arch, &p->thread.arch); } else { init_thread_registers(&p->thread.regs.regs); diff --git a/arch/um/kernel/syscall_kern.c b/arch/um/kernel/syscall_kern.c index 8e1a3501ff46..37d3978337d8 100644 --- a/arch/um/kernel/syscall_kern.c +++ b/arch/um/kernel/syscall_kern.c @@ -104,7 +104,7 @@ long sys_pipe(unsigned long __user * fildes) } -long sys_uname(struct old_utsname * name) +long sys_uname(struct old_utsname __user * name) { long err; if (!name) @@ -115,7 +115,7 @@ long sys_uname(struct old_utsname * name) return err?-EFAULT:0; } -long sys_olduname(struct oldold_utsname * name) +long sys_olduname(struct oldold_utsname __user * name) { long error; diff --git a/arch/um/kernel/trap_kern.c b/arch/um/kernel/trap_kern.c index d56046c2aba2..02f6d4d8dc3a 100644 --- a/arch/um/kernel/trap_kern.c +++ b/arch/um/kernel/trap_kern.c @@ -198,7 +198,7 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, void *sc) si.si_signo = SIGBUS; si.si_errno = 0; si.si_code = BUS_ADRERR; - si.si_addr = (void *)address; + si.si_addr = (void __user *)address; current->thread.arch.faultinfo = fi; force_sig_info(SIGBUS, &si, current); } else if (err == -ENOMEM) { @@ -207,7 +207,7 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, void *sc) } else { BUG_ON(err != -EFAULT); si.si_signo = SIGSEGV; - si.si_addr = (void *) address; + si.si_addr = (void __user *) address; current->thread.arch.faultinfo = fi; force_sig_info(SIGSEGV, &si, current); } @@ -220,8 +220,8 @@ void bad_segv(struct faultinfo fi, unsigned long ip) si.si_signo = SIGSEGV; si.si_code = SEGV_ACCERR; - si.si_addr = (void *) FAULT_ADDRESS(fi); - current->thread.arch.faultinfo = fi; + si.si_addr = (void __user *) FAULT_ADDRESS(fi); + current->thread.arch.faultinfo = fi; force_sig_info(SIGSEGV, &si, current); } diff --git a/arch/um/kernel/tt/process_kern.c b/arch/um/kernel/tt/process_kern.c index 295c1ac817b3..a9c1443fc548 100644 --- a/arch/um/kernel/tt/process_kern.c +++ b/arch/um/kernel/tt/process_kern.c @@ -51,6 +51,13 @@ void switch_to_tt(void *prev, void *next) c = 0; + /* Notice that here we "up" the semaphore on which "to" is waiting, and + * below (the read) we wait on this semaphore (which is implemented by + * switch_pipe) and go sleeping. Thus, after that, we have resumed in + * "to", and can't use any more the value of "from" (which is outdated), + * nor the value in "to" (since it was the task which stole us the CPU, + * which we don't care about). */ + err = os_write_file(to->thread.mode.tt.switch_pipe[1], &c, sizeof(c)); if(err != sizeof(c)) panic("write of switch_pipe failed, err = %d", -err); @@ -77,7 +84,7 @@ void switch_to_tt(void *prev, void *next) change_sig(SIGALRM, alrm); change_sig(SIGPROF, prof); - arch_switch(); + arch_switch_to_tt(prev_sched, current); flush_tlb_all(); local_irq_restore(flags); @@ -141,7 +148,6 @@ static void new_thread_handler(int sig) set_cmdline("(kernel thread)"); change_sig(SIGUSR1, 1); - change_sig(SIGVTALRM, 1); change_sig(SIGPROF, 1); local_irq_enable(); if(!run_kernel_thread(fn, arg, ¤t->thread.exec_buf)) diff --git a/arch/um/os-Linux/Makefile b/arch/um/os-Linux/Makefile index 1659386b42bb..f4bfc4c7ccac 100644 --- a/arch/um/os-Linux/Makefile +++ b/arch/um/os-Linux/Makefile @@ -4,7 +4,7 @@ # obj-y = aio.o elf_aux.o file.o helper.o irq.o main.o mem.o process.o sigio.o \ - signal.o start_up.o time.o trap.o tt.o tty.o uaccess.o umid.o \ + signal.o start_up.o time.o trap.o tt.o tty.o uaccess.o umid.o tls.o \ user_syms.o util.o drivers/ sys-$(SUBARCH)/ obj-$(CONFIG_MODE_SKAS) += skas/ @@ -12,12 +12,9 @@ obj-$(CONFIG_TTY_LOG) += tty_log.o user-objs-$(CONFIG_TTY_LOG) += tty_log.o USER_OBJS := $(user-objs-y) aio.o elf_aux.o file.o helper.o irq.o main.o mem.o \ - process.o sigio.o signal.o start_up.o time.o trap.o tt.o tty.o \ + process.o sigio.o signal.o start_up.o time.o trap.o tt.o tty.o tls.o \ uaccess.o umid.o util.o -elf_aux.o: $(ARCH_DIR)/kernel-offsets.h -CFLAGS_elf_aux.o += -I$(objtree)/arch/um - CFLAGS_user_syms.o += -DSUBARCH_$(SUBARCH) HAVE_AIO_ABI := $(shell [ -r /usr/include/linux/aio_abi.h ] && \ diff --git a/arch/um/os-Linux/drivers/ethertap_kern.c b/arch/um/os-Linux/drivers/ethertap_kern.c index 6ae4b19d9f50..768606bec233 100644 --- a/arch/um/os-Linux/drivers/ethertap_kern.c +++ b/arch/um/os-Linux/drivers/ethertap_kern.c @@ -102,18 +102,7 @@ static struct transport ethertap_transport = { static int register_ethertap(void) { register_transport(ðertap_transport); - return(1); + return 0; } __initcall(register_ethertap); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-file-style: "linux" - * End: - */ diff --git a/arch/um/os-Linux/drivers/ethertap_user.c b/arch/um/os-Linux/drivers/ethertap_user.c index 901b85e8a1c6..8f49507e64ef 100644 --- a/arch/um/os-Linux/drivers/ethertap_user.c +++ b/arch/um/os-Linux/drivers/ethertap_user.c @@ -40,7 +40,7 @@ static void etap_change(int op, unsigned char *addr, unsigned char *netmask, int fd) { struct addr_change change; - void *output; + char *output; int n; change.what = op; diff --git a/arch/um/os-Linux/drivers/tuntap_kern.c b/arch/um/os-Linux/drivers/tuntap_kern.c index 4202b9ebad4c..190009a6f89c 100644 --- a/arch/um/os-Linux/drivers/tuntap_kern.c +++ b/arch/um/os-Linux/drivers/tuntap_kern.c @@ -87,18 +87,7 @@ static struct transport tuntap_transport = { static int register_tuntap(void) { register_transport(&tuntap_transport); - return(1); + return 0; } __initcall(register_tuntap); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * Emacs will notice this stuff at the end of the file and automatically - * adjust the settings for this buffer only. This must remain at the end - * of the file. - * --------------------------------------------------------------------------- - * Local variables: - * c-file-style: "linux" - * End: - */ diff --git a/arch/um/os-Linux/helper.c b/arch/um/os-Linux/helper.c index 6490a4ff40ac..6987d1d247a2 100644 --- a/arch/um/os-Linux/helper.c +++ b/arch/um/os-Linux/helper.c @@ -43,7 +43,7 @@ static int helper_child(void *arg) (*data->pre_exec)(data->pre_data); execvp(argv[0], argv); errval = errno; - printk("execvp of '%s' failed - errno = %d\n", argv[0], errno); + printk("helper_child - execve of '%s' failed - errno = %d\n", argv[0], errno); os_write_file(data->fd, &errval, sizeof(errval)); kill(os_getpid(), SIGKILL); return(0); @@ -92,15 +92,15 @@ int run_helper(void (*pre_exec)(void *), void *pre_data, char **argv, close(fds[1]); fds[1] = -1; - /*Read the errno value from the child.*/ + /* Read the errno value from the child, if the exec failed, or get 0 if + * the exec succeeded because the pipe fd was set as close-on-exec. */ n = os_read_file(fds[0], &ret, sizeof(ret)); - if(n < 0){ + if (n < 0) { printk("run_helper : read on pipe failed, ret = %d\n", -n); ret = n; kill(pid, SIGKILL); CATCH_EINTR(waitpid(pid, NULL, 0)); - } - else if(n != 0){ + } else if(n != 0){ CATCH_EINTR(n = waitpid(pid, NULL, 0)); ret = -errno; } else { diff --git a/arch/um/os-Linux/mem.c b/arch/um/os-Linux/mem.c index 9d7d69a523bb..71bb90a7606d 100644 --- a/arch/um/os-Linux/mem.c +++ b/arch/um/os-Linux/mem.c @@ -53,33 +53,36 @@ static void __init find_tempdir(void) */ int make_tempfile(const char *template, char **out_tempname, int do_unlink) { - char tempname[MAXPATHLEN]; + char *tempname; int fd; + tempname = malloc(MAXPATHLEN); + find_tempdir(); - if (*template != '/') + if (template[0] != '/') strcpy(tempname, tempdir); else - *tempname = 0; + tempname[0] = '\0'; strcat(tempname, template); fd = mkstemp(tempname); if(fd < 0){ fprintf(stderr, "open - cannot create %s: %s\n", tempname, strerror(errno)); - return -1; + goto out; } if(do_unlink && (unlink(tempname) < 0)){ perror("unlink"); - return -1; + goto out; } if(out_tempname){ - *out_tempname = strdup(tempname); - if(*out_tempname == NULL){ - perror("strdup"); - return -1; - } + *out_tempname = tempname; + } else { + free(tempname); } return(fd); +out: + free(tempname); + return -1; } #define TEMPNAME_TEMPLATE "vm_file-XXXXXX" @@ -121,36 +124,11 @@ int create_tmp_file(unsigned long long len) return(fd); } -static int create_anon_file(unsigned long long len) -{ - void *addr; - int fd; - - fd = open("/dev/anon", O_RDWR); - if(fd < 0) { - perror("opening /dev/anon"); - exit(1); - } - - addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - if(addr == MAP_FAILED){ - perror("mapping physmem file"); - exit(1); - } - munmap(addr, len); - - return(fd); -} - -extern int have_devanon; - int create_mem_file(unsigned long long len) { int err, fd; - if(have_devanon) - fd = create_anon_file(len); - else fd = create_tmp_file(len); + fd = create_tmp_file(len); err = os_set_exec_close(fd, 1); if(err < 0){ diff --git a/arch/um/os-Linux/process.c b/arch/um/os-Linux/process.c index d261888f39c4..8176b0b52047 100644 --- a/arch/um/os-Linux/process.c +++ b/arch/um/os-Linux/process.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "ptrace_user.h" #include "os.h" #include "user.h" @@ -20,6 +21,7 @@ #include "kern_util.h" #include "longjmp.h" #include "skas_ptrace.h" +#include "kern_constants.h" #define ARBITRARY_ADDR -1 #define FAILURE_PID -1 @@ -187,6 +189,48 @@ int os_unmap_memory(void *addr, int len) return(0); } +#ifndef MADV_REMOVE +#define MADV_REMOVE 0x5 /* remove these pages & resources */ +#endif + +int os_drop_memory(void *addr, int length) +{ + int err; + + err = madvise(addr, length, MADV_REMOVE); + if(err < 0) + err = -errno; + return err; +} + +int can_drop_memory(void) +{ + void *addr; + int fd; + + printk("Checking host MADV_REMOVE support..."); + fd = create_mem_file(UM_KERN_PAGE_SIZE); + if(fd < 0){ + printk("Creating test memory file failed, err = %d\n", -fd); + return 0; + } + + addr = mmap64(NULL, UM_KERN_PAGE_SIZE, PROT_READ | PROT_WRITE, + MAP_PRIVATE, fd, 0); + if(addr == MAP_FAILED){ + printk("Mapping test memory file failed, err = %d\n", -errno); + return 0; + } + + if(madvise(addr, UM_KERN_PAGE_SIZE, MADV_REMOVE) != 0){ + printk("MADV_REMOVE failed, err = %d\n", -errno); + return 0; + } + + printk("OK\n"); + return 1; +} + void init_new_thread_stack(void *sig_stack, void (*usr1_handler)(int)) { int flags = 0, pages; diff --git a/arch/um/os-Linux/sigio.c b/arch/um/os-Linux/sigio.c index 9ba942947146..00e9388e947a 100644 --- a/arch/um/os-Linux/sigio.c +++ b/arch/um/os-Linux/sigio.c @@ -304,8 +304,8 @@ out_clear_poll: .size = 0, .used = 0 }); out_free: - kfree(p); sigio_unlock(); + kfree(p); out_close2: close(l_sigio_private[0]); close(l_sigio_private[1]); diff --git a/arch/um/os-Linux/skas/mem.c b/arch/um/os-Linux/skas/mem.c index fbb080c2fc26..b3c11cfa995a 100644 --- a/arch/um/os-Linux/skas/mem.c +++ b/arch/um/os-Linux/skas/mem.c @@ -82,8 +82,8 @@ static inline long do_syscall_stub(struct mm_id * mm_idp, void **addr) if (offset) { data = (unsigned long *)(mm_idp->stack + offset - UML_CONFIG_STUB_DATA); - printk("do_syscall_stub : ret = %d, offset = %d, " - "data = 0x%x\n", ret, offset, data); + printk("do_syscall_stub : ret = %ld, offset = %ld, " + "data = %p\n", ret, offset, data); syscall = (unsigned long *)((unsigned long)data + data[0]); printk("do_syscall_stub: syscall %ld failed, return value = " "0x%lx, expected return value = 0x%lx\n", diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c index bbf34cb91ce1..045ae0037456 100644 --- a/arch/um/os-Linux/skas/process.c +++ b/arch/um/os-Linux/skas/process.c @@ -265,7 +265,7 @@ void userspace(union uml_pt_regs *regs) if(err) panic("userspace - could not resume userspace process, " "pid=%d, ptrace operation = %d, errno = %d\n", - op, errno); + pid, op, errno); CATCH_EINTR(err = waitpid(pid, &status, WUNTRACED)); if(err < 0) @@ -369,7 +369,7 @@ int copy_context_skas0(unsigned long new_stack, int pid) */ wait_stub_done(pid, -1, "copy_context_skas0"); if (child_data->err != UML_CONFIG_STUB_DATA) - panic("copy_context_skas0 - stub-child reports error %d\n", + panic("copy_context_skas0 - stub-child reports error %ld\n", child_data->err); if (ptrace(PTRACE_OLDSETOPTIONS, pid, NULL, diff --git a/arch/um/os-Linux/start_up.c b/arch/um/os-Linux/start_up.c index 32753131f8d8..387e26af301a 100644 --- a/arch/um/os-Linux/start_up.c +++ b/arch/um/os-Linux/start_up.c @@ -470,25 +470,6 @@ int can_do_skas(void) } #endif -int have_devanon = 0; - -/* Runs on boot kernel stack - already safe to use printk. */ - -void check_devanon(void) -{ - int fd; - - printk("Checking for /dev/anon on the host..."); - fd = open("/dev/anon", O_RDWR); - if(fd < 0){ - printk("Not available (open failed with errno %d)\n", errno); - return; - } - - printk("OK\n"); - have_devanon = 1; -} - int __init parse_iomem(char *str, int *add) { struct iomem_region *new; @@ -664,6 +645,5 @@ void os_check_bugs(void) { check_ptrace(); check_sigio(); - check_devanon(); } diff --git a/arch/um/os-Linux/sys-i386/Makefile b/arch/um/os-Linux/sys-i386/Makefile index 340ef26f5944..b3213613c41c 100644 --- a/arch/um/os-Linux/sys-i386/Makefile +++ b/arch/um/os-Linux/sys-i386/Makefile @@ -3,7 +3,7 @@ # Licensed under the GPL # -obj-$(CONFIG_MODE_SKAS) = registers.o +obj-$(CONFIG_MODE_SKAS) = registers.o tls.o USER_OBJS := $(obj-y) diff --git a/arch/um/os-Linux/sys-i386/tls.c b/arch/um/os-Linux/sys-i386/tls.c new file mode 100644 index 000000000000..120abbe4e3ce --- /dev/null +++ b/arch/um/os-Linux/sys-i386/tls.c @@ -0,0 +1,34 @@ +#include +#include +#include "sysdep/tls.h" +#include "user_util.h" + +static _syscall1(int, get_thread_area, user_desc_t *, u_info); + +/* Checks whether host supports TLS, and sets *tls_min according to the value + * valid on the host. + * i386 host have it == 6; x86_64 host have it == 12, for i386 emulation. */ +void check_host_supports_tls(int *supports_tls, int *tls_min) { + /* Values for x86 and x86_64.*/ + int val[] = {GDT_ENTRY_TLS_MIN_I386, GDT_ENTRY_TLS_MIN_X86_64}; + int i; + + for (i = 0; i < ARRAY_SIZE(val); i++) { + user_desc_t info; + info.entry_number = val[i]; + + if (get_thread_area(&info) == 0) { + *tls_min = val[i]; + *supports_tls = 1; + return; + } else { + if (errno == EINVAL) + continue; + else if (errno == ENOSYS) + *supports_tls = 0; + return; + } + } + + *supports_tls = 0; +} diff --git a/arch/um/os-Linux/tls.c b/arch/um/os-Linux/tls.c new file mode 100644 index 000000000000..9cb09a45546b --- /dev/null +++ b/arch/um/os-Linux/tls.c @@ -0,0 +1,76 @@ +#include +#include +#include +#include "sysdep/tls.h" +#include "uml-config.h" + +/* TLS support - we basically rely on the host's one.*/ + +/* In TT mode, this should be called only by the tracing thread, and makes sense + * only for PTRACE_SET_THREAD_AREA. In SKAS mode, it's used normally. + * + */ + +#ifndef PTRACE_GET_THREAD_AREA +#define PTRACE_GET_THREAD_AREA 25 +#endif + +#ifndef PTRACE_SET_THREAD_AREA +#define PTRACE_SET_THREAD_AREA 26 +#endif + +int os_set_thread_area(user_desc_t *info, int pid) +{ + int ret; + + ret = ptrace(PTRACE_SET_THREAD_AREA, pid, info->entry_number, + (unsigned long) info); + if (ret < 0) + ret = -errno; + return ret; +} + +#ifdef UML_CONFIG_MODE_SKAS + +int os_get_thread_area(user_desc_t *info, int pid) +{ + int ret; + + ret = ptrace(PTRACE_GET_THREAD_AREA, pid, info->entry_number, + (unsigned long) info); + if (ret < 0) + ret = -errno; + return ret; +} + +#endif + +#ifdef UML_CONFIG_MODE_TT +#include "linux/unistd.h" + +static _syscall1(int, get_thread_area, user_desc_t *, u_info); +static _syscall1(int, set_thread_area, user_desc_t *, u_info); + +int do_set_thread_area_tt(user_desc_t *info) +{ + int ret; + + ret = set_thread_area(info); + if (ret < 0) { + ret = -errno; + } + return ret; +} + +int do_get_thread_area_tt(user_desc_t *info) +{ + int ret; + + ret = get_thread_area(info); + if (ret < 0) { + ret = -errno; + } + return ret; +} + +#endif /* UML_CONFIG_MODE_TT */ diff --git a/arch/um/os-Linux/umid.c b/arch/um/os-Linux/umid.c index 198e59163288..34bfc1bb9e38 100644 --- a/arch/um/os-Linux/umid.c +++ b/arch/um/os-Linux/umid.c @@ -120,7 +120,8 @@ static int not_dead_yet(char *dir) dead = 0; fd = open(file, O_RDONLY); - if(fd < 0){ + if(fd < 0) { + fd = -errno; if(fd != -ENOENT){ printk("not_dead_yet : couldn't open pid file '%s', " "err = %d\n", file, -fd); @@ -130,9 +131,13 @@ static int not_dead_yet(char *dir) err = 0; n = read(fd, pid, sizeof(pid)); - if(n <= 0){ + if(n < 0){ printk("not_dead_yet : couldn't read pid file '%s', " - "err = %d\n", file, -n); + "err = %d\n", file, errno); + goto out_close; + } else if(n == 0){ + printk("not_dead_yet : couldn't read pid file '%s', " + "0-byte read\n", file); goto out_close; } @@ -155,9 +160,9 @@ static int not_dead_yet(char *dir) return err; - out_close: +out_close: close(fd); - out: +out: return 0; } diff --git a/arch/um/os-Linux/user_syms.c b/arch/um/os-Linux/user_syms.c index 8da6ab31152a..2598158e1f53 100644 --- a/arch/um/os-Linux/user_syms.c +++ b/arch/um/os-Linux/user_syms.c @@ -18,14 +18,19 @@ extern void *memmove(void *, const void *, size_t); extern void *memset(void *, int, size_t); extern int printf(const char *, ...); +/* If they're not defined, the export is included in lib/string.c.*/ +#ifdef __HAVE_ARCH_STRLEN EXPORT_SYMBOL(strlen); +#endif +#ifdef __HAVE_ARCH_STRSTR +EXPORT_SYMBOL(strstr); +#endif + EXPORT_SYMBOL(memcpy); EXPORT_SYMBOL(memmove); EXPORT_SYMBOL(memset); EXPORT_SYMBOL(printf); -EXPORT_SYMBOL(strstr); - /* Here, instead, I can provide a fake prototype. Yes, someone cares: genksyms. * However, the modules will use the CRC defined *here*, no matter if it is * good; so the versions of these symbols will always match diff --git a/arch/um/scripts/Makefile.rules b/arch/um/scripts/Makefile.rules index 2e41cabd3d93..5e7a9c310aa5 100644 --- a/arch/um/scripts/Makefile.rules +++ b/arch/um/scripts/Makefile.rules @@ -9,10 +9,8 @@ USER_OBJS := $(foreach file,$(USER_OBJS),$(obj)/$(file)) $(USER_OBJS) $(USER_OBJS:.o=.i) $(USER_OBJS:.o=.s) $(USER_OBJS:.o=.lst): \ c_flags = -Wp,-MD,$(depfile) $(USER_CFLAGS) $(CFLAGS_$(notdir $@)) -$(USER_OBJS): cmd_checksrc = -$(USER_OBJS): quiet_cmd_checksrc = -$(USER_OBJS): cmd_force_checksrc = -$(USER_OBJS): quiet_cmd_force_checksrc = +$(USER_OBJS) : CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ \ + -Dunix -D__unix__ -D__$(SUBARCH)__ # The stubs and unmap.o can't try to call mcount or update basic block data @@ -20,25 +18,7 @@ define unprofile $(patsubst -pg,,$(patsubst -fprofile-arcs -ftest-coverage,,$(1))) endef - -# cmd_make_link checks to see if the $(foo-dir) variable starts with a /. If -# so, it's considered to be a path relative to $(srcdir) rather than -# $(srcdir)/arch/$(SUBARCH). This is because x86_64 wants to get ldt.c from -# arch/um/sys-i386 rather than arch/i386 like the other borrowed files. So, -# it sets $(ldt.c-dir) to /arch/um/sys-i386. -quiet_cmd_make_link = SYMLINK $@ -cmd_make_link = rm -f $@; ln -sf $(srctree)$(if $(filter-out /%,$($(notdir $@)-dir)),/arch/$(SUBARCH))/$($(notdir $@)-dir)/$(notdir $@) $@ - -# this needs to be before the foreach, because targets does not accept -# complete paths like $(obj)/$(f). To make sure this works, use a := assignment -# or we will get $(obj)/$(f) in the "targets" value. -# Also, this forces you to use the := syntax when assigning to targets. -# Otherwise the line below will cause an infinite loop (if you don't know why, -# just do it). - -targets := $(targets) $(SYMLINKS) - -SYMLINKS := $(foreach f,$(SYMLINKS),$(obj)/$(f)) - -$(SYMLINKS): FORCE - $(call if_changed,make_link) +ifdef subarch-obj-y +obj-y += subarch.o +subarch-y = $(addprefix ../../$(SUBARCH)/,$(subarch-obj-y)) +endif diff --git a/arch/um/scripts/Makefile.unmap b/arch/um/scripts/Makefile.unmap deleted file mode 100644 index b2165188d942..000000000000 --- a/arch/um/scripts/Makefile.unmap +++ /dev/null @@ -1,22 +0,0 @@ -clean-files += unmap_tmp.o unmap_fin.o unmap.o - -ifdef CONFIG_MODE_TT - -#Always build unmap_fin.o -extra-y += unmap_fin.o -#Do dependency tracking for unmap.o (it will be always built, but won't get the tracking unless we use this). -targets += unmap.o - -#XXX: partially copied from arch/um/scripts/Makefile.rules -$(obj)/unmap.o: _c_flags = $(call unprofile,$(CFLAGS)) - -quiet_cmd_wrapld = LD $@ -define cmd_wrapld - $(LD) $(LDFLAGS) -r -o $(obj)/unmap_tmp.o $< ; \ - $(OBJCOPY) $(UML_OBJCOPYFLAGS) $(obj)/unmap_tmp.o $@ -G switcheroo -endef - -$(obj)/unmap_fin.o : $(obj)/unmap.o FORCE - $(call if_changed,wrapld) - -endif diff --git a/arch/um/sys-i386/Makefile b/arch/um/sys-i386/Makefile index f5fd5b0156d0..98b20b7bba4f 100644 --- a/arch/um/sys-i386/Makefile +++ b/arch/um/sys-i386/Makefile @@ -1,23 +1,18 @@ -obj-y := bitops.o bugs.o checksum.o delay.o fault.o ksyms.o ldt.o ptrace.o \ - ptrace_user.o semaphore.o signal.o sigcontext.o syscalls.o sysrq.o \ - sys_call_table.o +obj-y = bugs.o checksum.o delay.o fault.o ksyms.o ldt.o ptrace.o \ + ptrace_user.o signal.o sigcontext.o syscalls.o sysrq.o \ + sys_call_table.o tls.o obj-$(CONFIG_MODE_SKAS) += stub.o stub_segv.o -obj-$(CONFIG_HIGHMEM) += highmem.o -obj-$(CONFIG_MODULES) += module.o +subarch-obj-y = lib/bitops.o kernel/semaphore.o +subarch-obj-$(CONFIG_HIGHMEM) += mm/highmem.o +subarch-obj-$(CONFIG_MODULES) += kernel/module.o USER_OBJS := bugs.o ptrace_user.o sigcontext.o fault.o stub_segv.o -SYMLINKS = bitops.c semaphore.c highmem.c module.c - include arch/um/scripts/Makefile.rules -bitops.c-dir = lib -semaphore.c-dir = kernel -highmem.c-dir = mm -module.c-dir = kernel +extra-$(CONFIG_MODE_TT) += unmap.o -$(obj)/stub_segv.o : _c_flags = $(call unprofile,$(CFLAGS)) - -include arch/um/scripts/Makefile.unmap +$(obj)/stub_segv.o $(obj)/unmap.o: \ + _c_flags = $(call unprofile,$(CFLAGS)) diff --git a/arch/um/sys-i386/ksyms.c b/arch/um/sys-i386/ksyms.c index db524ab3f743..2a1eac1859ce 100644 --- a/arch/um/sys-i386/ksyms.c +++ b/arch/um/sys-i386/ksyms.c @@ -15,7 +15,3 @@ EXPORT_SYMBOL(__up_wakeup); /* Networking helper routines. */ EXPORT_SYMBOL(csum_partial); - -/* delay core functions */ -EXPORT_SYMBOL(__const_udelay); -EXPORT_SYMBOL(__udelay); diff --git a/arch/um/sys-i386/ptrace.c b/arch/um/sys-i386/ptrace.c index 8032a105949a..6028bc7cc01b 100644 --- a/arch/um/sys-i386/ptrace.c +++ b/arch/um/sys-i386/ptrace.c @@ -15,9 +15,22 @@ #include "sysdep/sigcontext.h" #include "sysdep/sc.h" -void arch_switch(void) +void arch_switch_to_tt(struct task_struct *from, struct task_struct *to) { - update_debugregs(current->thread.arch.debugregs_seq); + update_debugregs(to->thread.arch.debugregs_seq); + arch_switch_tls_tt(from, to); +} + +void arch_switch_to_skas(struct task_struct *from, struct task_struct *to) +{ + int err = arch_switch_tls_skas(from, to); + if (!err) + return; + + if (err != -EINVAL) + printk(KERN_WARNING "arch_switch_tls_skas failed, errno %d, not EINVAL\n", -err); + else + printk(KERN_WARNING "arch_switch_tls_skas failed, errno = EINVAL\n"); } int is_syscall(unsigned long addr) @@ -124,22 +137,22 @@ unsigned long getreg(struct task_struct *child, int regno) int peek_user(struct task_struct *child, long addr, long data) { /* read the word at location addr in the USER area. */ - unsigned long tmp; + unsigned long tmp; - if ((addr & 3) || addr < 0) - return -EIO; + if ((addr & 3) || addr < 0) + return -EIO; - tmp = 0; /* Default return condition */ - if(addr < MAX_REG_OFFSET){ - tmp = getreg(child, addr); - } - else if((addr >= offsetof(struct user, u_debugreg[0])) && - (addr <= offsetof(struct user, u_debugreg[7]))){ - addr -= offsetof(struct user, u_debugreg[0]); - addr = addr >> 2; - tmp = child->thread.arch.debugregs[addr]; - } - return put_user(tmp, (unsigned long *) data); + tmp = 0; /* Default return condition */ + if(addr < MAX_REG_OFFSET){ + tmp = getreg(child, addr); + } + else if((addr >= offsetof(struct user, u_debugreg[0])) && + (addr <= offsetof(struct user, u_debugreg[7]))){ + addr -= offsetof(struct user, u_debugreg[0]); + addr = addr >> 2; + tmp = child->thread.arch.debugregs[addr]; + } + return put_user(tmp, (unsigned long __user *) data); } struct i387_fxsave_struct { diff --git a/arch/um/sys-i386/ptrace_user.c b/arch/um/sys-i386/ptrace_user.c index 7c376c95de50..40aa88531446 100644 --- a/arch/um/sys-i386/ptrace_user.c +++ b/arch/um/sys-i386/ptrace_user.c @@ -14,6 +14,7 @@ #include "sysdep/thread.h" #include "user.h" #include "os.h" +#include "uml-config.h" int ptrace_getregs(long pid, unsigned long *regs_out) { @@ -43,6 +44,7 @@ int ptrace_setfpregs(long pid, unsigned long *regs) return 0; } +/* All the below stuff is of interest for TT mode only */ static void write_debugregs(int pid, unsigned long *regs) { struct user *dummy; @@ -55,7 +57,7 @@ static void write_debugregs(int pid, unsigned long *regs) if(ptrace(PTRACE_POKEUSR, pid, &dummy->u_debugreg[i], regs[i]) < 0) printk("write_debugregs - ptrace failed on " - "register %d, value = 0x%x, errno = %d\n", i, + "register %d, value = 0x%lx, errno = %d\n", i, regs[i], errno); } } @@ -75,7 +77,6 @@ static void read_debugregs(int pid, unsigned long *regs) /* Accessed only by the tracing thread */ static unsigned long kernel_debugregs[8] = { [ 0 ... 7 ] = 0 }; -static int debugregs_seq = 0; void arch_enter_kernel(void *task, int pid) { @@ -89,6 +90,11 @@ void arch_leave_kernel(void *task, int pid) write_debugregs(pid, TASK_DEBUGREGS(task)); } +#ifdef UML_CONFIG_PT_PROXY +/* Accessed only by the tracing thread */ +static int debugregs_seq; + +/* Only called by the ptrace proxy */ void ptrace_pokeuser(unsigned long addr, unsigned long data) { if((addr < offsetof(struct user, u_debugreg[0])) || @@ -109,6 +115,7 @@ static void update_debugregs_cb(void *arg) write_debugregs(pid, kernel_debugregs); } +/* Optimized out in its header when not defined */ void update_debugregs(int seq) { int me; @@ -118,6 +125,7 @@ void update_debugregs(int seq) me = os_getpid(); initial_thread_cb(update_debugregs_cb, &me); } +#endif /* * Overrides for Emacs so that we follow Linus's tabbing style. diff --git a/arch/um/sys-i386/signal.c b/arch/um/sys-i386/signal.c index 33a40f5ef0d2..618fd8594643 100644 --- a/arch/um/sys-i386/signal.c +++ b/arch/um/sys-i386/signal.c @@ -19,7 +19,7 @@ #include "skas.h" static int copy_sc_from_user_skas(struct pt_regs *regs, - struct sigcontext *from) + struct sigcontext __user *from) { struct sigcontext sc; unsigned long fpregs[HOST_FP_SIZE]; @@ -57,7 +57,7 @@ static int copy_sc_from_user_skas(struct pt_regs *regs, return(0); } -int copy_sc_to_user_skas(struct sigcontext *to, struct _fpstate *to_fp, +int copy_sc_to_user_skas(struct sigcontext *to, struct _fpstate __user *to_fp, struct pt_regs *regs, unsigned long sp) { struct sigcontext sc; @@ -92,7 +92,7 @@ int copy_sc_to_user_skas(struct sigcontext *to, struct _fpstate *to_fp, "errno = %d\n", err); return(1); } - to_fp = (to_fp ? to_fp : (struct _fpstate *) (to + 1)); + to_fp = (to_fp ? to_fp : (struct _fpstate __user *) (to + 1)); sc.fpstate = to_fp; if(err) @@ -113,10 +113,11 @@ int copy_sc_to_user_skas(struct sigcontext *to, struct _fpstate *to_fp, * saved pointer is in the kernel, but the sigcontext is in userspace, so we * copy_to_user it. */ -int copy_sc_from_user_tt(struct sigcontext *to, struct sigcontext *from, +int copy_sc_from_user_tt(struct sigcontext *to, struct sigcontext __user *from, int fpsize) { - struct _fpstate *to_fp, *from_fp; + struct _fpstate *to_fp; + struct _fpstate __user *from_fp; unsigned long sigs; int err; @@ -131,13 +132,14 @@ int copy_sc_from_user_tt(struct sigcontext *to, struct sigcontext *from, return(err); } -int copy_sc_to_user_tt(struct sigcontext *to, struct _fpstate *fp, +int copy_sc_to_user_tt(struct sigcontext *to, struct _fpstate __user *fp, struct sigcontext *from, int fpsize, unsigned long sp) { - struct _fpstate *to_fp, *from_fp; + struct _fpstate __user *to_fp; + struct _fpstate *from_fp; int err; - to_fp = (fp ? fp : (struct _fpstate *) (to + 1)); + to_fp = (fp ? fp : (struct _fpstate __user *) (to + 1)); from_fp = from->fpstate; err = copy_to_user(to, from, sizeof(*to)); @@ -145,7 +147,7 @@ int copy_sc_to_user_tt(struct sigcontext *to, struct _fpstate *fp, * delivery. The sp passed in is the original, and this needs * to be restored, so we stick it in separately. */ - err |= copy_to_user(&SC_SP(to), sp, sizeof(sp)); + err |= copy_to_user(&SC_SP(to), &sp, sizeof(sp)); if(from_fp != NULL){ err |= copy_to_user(&to->fpstate, &to_fp, sizeof(to->fpstate)); @@ -165,7 +167,7 @@ static int copy_sc_from_user(struct pt_regs *to, void __user *from) return(ret); } -static int copy_sc_to_user(struct sigcontext *to, struct _fpstate *fp, +static int copy_sc_to_user(struct sigcontext *to, struct _fpstate __user *fp, struct pt_regs *from, unsigned long sp) { return(CHOOSE_MODE(copy_sc_to_user_tt(to, fp, UPT_SC(&from->regs), @@ -173,7 +175,7 @@ static int copy_sc_to_user(struct sigcontext *to, struct _fpstate *fp, copy_sc_to_user_skas(to, fp, from, sp))); } -static int copy_ucontext_to_user(struct ucontext *uc, struct _fpstate *fp, +static int copy_ucontext_to_user(struct ucontext __user *uc, struct _fpstate __user *fp, sigset_t *set, unsigned long sp) { int err = 0; @@ -188,7 +190,7 @@ static int copy_ucontext_to_user(struct ucontext *uc, struct _fpstate *fp, struct sigframe { - char *pretcode; + char __user *pretcode; int sig; struct sigcontext sc; struct _fpstate fpstate; @@ -198,10 +200,10 @@ struct sigframe struct rt_sigframe { - char *pretcode; + char __user *pretcode; int sig; - struct siginfo *pinfo; - void *puc; + struct siginfo __user *pinfo; + void __user *puc; struct siginfo info; struct ucontext uc; struct _fpstate fpstate; @@ -213,16 +215,16 @@ int setup_signal_stack_sc(unsigned long stack_top, int sig, sigset_t *mask) { struct sigframe __user *frame; - void *restorer; + void __user *restorer; unsigned long save_sp = PT_REGS_SP(regs); int err = 0; stack_top &= -8UL; - frame = (struct sigframe *) stack_top - 1; + frame = (struct sigframe __user *) stack_top - 1; if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) return 1; - restorer = (void *) frame->retcode; + restorer = frame->retcode; if(ka->sa.sa_flags & SA_RESTORER) restorer = ka->sa.sa_restorer; @@ -278,16 +280,16 @@ int setup_signal_stack_si(unsigned long stack_top, int sig, siginfo_t *info, sigset_t *mask) { struct rt_sigframe __user *frame; - void *restorer; + void __user *restorer; unsigned long save_sp = PT_REGS_SP(regs); int err = 0; stack_top &= -8UL; - frame = (struct rt_sigframe *) stack_top - 1; + frame = (struct rt_sigframe __user *) stack_top - 1; if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) return 1; - restorer = (void *) frame->retcode; + restorer = frame->retcode; if(ka->sa.sa_flags & SA_RESTORER) restorer = ka->sa.sa_restorer; @@ -333,7 +335,7 @@ err: long sys_sigreturn(struct pt_regs regs) { unsigned long sp = PT_REGS_SP(¤t->thread.regs); - struct sigframe __user *frame = (struct sigframe *)(sp - 8); + struct sigframe __user *frame = (struct sigframe __user *)(sp - 8); sigset_t set; struct sigcontext __user *sc = &frame->sc; unsigned long __user *oldmask = &sc->oldmask; @@ -365,8 +367,8 @@ long sys_sigreturn(struct pt_regs regs) long sys_rt_sigreturn(struct pt_regs regs) { - unsigned long __user sp = PT_REGS_SP(¤t->thread.regs); - struct rt_sigframe __user *frame = (struct rt_sigframe *) (sp - 4); + unsigned long sp = PT_REGS_SP(¤t->thread.regs); + struct rt_sigframe __user *frame = (struct rt_sigframe __user *) (sp - 4); sigset_t set; struct ucontext __user *uc = &frame->uc; int sig_size = _NSIG_WORDS * sizeof(unsigned long); diff --git a/arch/um/sys-i386/sys_call_table.S b/arch/um/sys-i386/sys_call_table.S index ad75c27afe38..1ff61474b25c 100644 --- a/arch/um/sys-i386/sys_call_table.S +++ b/arch/um/sys-i386/sys_call_table.S @@ -6,8 +6,6 @@ #define sys_vm86old sys_ni_syscall #define sys_vm86 sys_ni_syscall -#define sys_set_thread_area sys_ni_syscall -#define sys_get_thread_area sys_ni_syscall #define sys_stime um_stime #define sys_time um_time diff --git a/arch/um/sys-i386/syscalls.c b/arch/um/sys-i386/syscalls.c index 83e9be820a86..749dd1bfe60f 100644 --- a/arch/um/sys-i386/syscalls.c +++ b/arch/um/sys-i386/syscalls.c @@ -61,21 +61,27 @@ long old_select(struct sel_arg_struct __user *arg) return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp); } -/* The i386 version skips reading from %esi, the fourth argument. So we must do - * this, too. +/* + * The prototype on i386 is: + * + * int clone(int flags, void * child_stack, int * parent_tidptr, struct user_desc * newtls, int * child_tidptr) + * + * and the "newtls" arg. on i386 is read by copy_thread directly from the + * register saved on the stack. */ long sys_clone(unsigned long clone_flags, unsigned long newsp, - int __user *parent_tid, int unused, int __user *child_tid) + int __user *parent_tid, void *newtls, int __user *child_tid) { long ret; if (!newsp) newsp = UPT_SP(¤t->thread.regs.regs); + current->thread.forking = 1; ret = do_fork(clone_flags, newsp, ¤t->thread.regs, 0, parent_tid, child_tid); current->thread.forking = 0; - return(ret); + return ret; } /* @@ -104,7 +110,7 @@ long sys_ipc (uint call, int first, int second, union semun fourth; if (!ptr) return -EINVAL; - if (get_user(fourth.__pad, (void **) ptr)) + if (get_user(fourth.__pad, (void __user * __user *) ptr)) return -EFAULT; return sys_semctl (first, second, third, fourth); } diff --git a/arch/um/sys-i386/tls.c b/arch/um/sys-i386/tls.c new file mode 100644 index 000000000000..71b9796258ef --- /dev/null +++ b/arch/um/sys-i386/tls.c @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2005 Paolo 'Blaisorblade' Giarrusso + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/kernel.h" +#include "linux/sched.h" +#include "linux/slab.h" +#include "linux/types.h" +#include "asm/uaccess.h" +#include "asm/ptrace.h" +#include "asm/segment.h" +#include "asm/smp.h" +#include "asm/desc.h" +#include "choose-mode.h" +#include "kern.h" +#include "kern_util.h" +#include "mode_kern.h" +#include "os.h" +#include "mode.h" + +#ifdef CONFIG_MODE_SKAS +#include "skas.h" +#endif + +/* If needed we can detect when it's uninitialized. */ +static int host_supports_tls = -1; +int host_gdt_entry_tls_min = -1; + +#ifdef CONFIG_MODE_SKAS +int do_set_thread_area_skas(struct user_desc *info) +{ + int ret; + u32 cpu; + + cpu = get_cpu(); + ret = os_set_thread_area(info, userspace_pid[cpu]); + put_cpu(); + return ret; +} + +int do_get_thread_area_skas(struct user_desc *info) +{ + int ret; + u32 cpu; + + cpu = get_cpu(); + ret = os_get_thread_area(info, userspace_pid[cpu]); + put_cpu(); + return ret; +} +#endif + +/* + * sys_get_thread_area: get a yet unused TLS descriptor index. + * XXX: Consider leaving one free slot for glibc usage at first place. This must + * be done here (and by changing GDT_ENTRY_TLS_* macros) and nowhere else. + * + * Also, this must be tested when compiling in SKAS mode with dinamic linking + * and running against NPTL. + */ +static int get_free_idx(struct task_struct* task) +{ + struct thread_struct *t = &task->thread; + int idx; + + if (!t->arch.tls_array) + return GDT_ENTRY_TLS_MIN; + + for (idx = 0; idx < GDT_ENTRY_TLS_ENTRIES; idx++) + if (!t->arch.tls_array[idx].present) + return idx + GDT_ENTRY_TLS_MIN; + return -ESRCH; +} + +static inline void clear_user_desc(struct user_desc* info) +{ + /* Postcondition: LDT_empty(info) returns true. */ + memset(info, 0, sizeof(*info)); + + /* Check the LDT_empty or the i386 sys_get_thread_area code - we obtain + * indeed an empty user_desc. + */ + info->read_exec_only = 1; + info->seg_not_present = 1; +} + +#define O_FORCE 1 + +static int load_TLS(int flags, struct task_struct *to) +{ + int ret = 0; + int idx; + + for (idx = GDT_ENTRY_TLS_MIN; idx < GDT_ENTRY_TLS_MAX; idx++) { + struct uml_tls_struct* curr = &to->thread.arch.tls_array[idx - GDT_ENTRY_TLS_MIN]; + + /* Actually, now if it wasn't flushed it gets cleared and + * flushed to the host, which will clear it.*/ + if (!curr->present) { + if (!curr->flushed) { + clear_user_desc(&curr->tls); + curr->tls.entry_number = idx; + } else { + WARN_ON(!LDT_empty(&curr->tls)); + continue; + } + } + + if (!(flags & O_FORCE) && curr->flushed) + continue; + + ret = do_set_thread_area(&curr->tls); + if (ret) + goto out; + + curr->flushed = 1; + } +out: + return ret; +} + +/* Verify if we need to do a flush for the new process, i.e. if there are any + * present desc's, only if they haven't been flushed. + */ +static inline int needs_TLS_update(struct task_struct *task) +{ + int i; + int ret = 0; + + for (i = GDT_ENTRY_TLS_MIN; i < GDT_ENTRY_TLS_MAX; i++) { + struct uml_tls_struct* curr = &task->thread.arch.tls_array[i - GDT_ENTRY_TLS_MIN]; + + /* Can't test curr->present, we may need to clear a descriptor + * which had a value. */ + if (curr->flushed) + continue; + ret = 1; + break; + } + return ret; +} + +/* On a newly forked process, the TLS descriptors haven't yet been flushed. So + * we mark them as such and the first switch_to will do the job. + */ +void clear_flushed_tls(struct task_struct *task) +{ + int i; + + for (i = GDT_ENTRY_TLS_MIN; i < GDT_ENTRY_TLS_MAX; i++) { + struct uml_tls_struct* curr = &task->thread.arch.tls_array[i - GDT_ENTRY_TLS_MIN]; + + /* Still correct to do this, if it wasn't present on the host it + * will remain as flushed as it was. */ + if (!curr->present) + continue; + + curr->flushed = 0; + } +} + +/* In SKAS0 mode, currently, multiple guest threads sharing the same ->mm have a + * common host process. So this is needed in SKAS0 too. + * + * However, if each thread had a different host process (and this was discussed + * for SMP support) this won't be needed. + * + * And this will not need be used when (and if) we'll add support to the host + * SKAS patch. */ + +int arch_switch_tls_skas(struct task_struct *from, struct task_struct *to) +{ + if (!host_supports_tls) + return 0; + + /* We have no need whatsoever to switch TLS for kernel threads; beyond + * that, that would also result in us calling os_set_thread_area with + * userspace_pid[cpu] == 0, which gives an error. */ + if (likely(to->mm)) + return load_TLS(O_FORCE, to); + + return 0; +} + +int arch_switch_tls_tt(struct task_struct *from, struct task_struct *to) +{ + if (!host_supports_tls) + return 0; + + if (needs_TLS_update(to)) + return load_TLS(0, to); + + return 0; +} + +static int set_tls_entry(struct task_struct* task, struct user_desc *info, + int idx, int flushed) +{ + struct thread_struct *t = &task->thread; + + if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) + return -EINVAL; + + t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].tls = *info; + t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].present = 1; + t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].flushed = flushed; + + return 0; +} + +int arch_copy_tls(struct task_struct *new) +{ + struct user_desc info; + int idx, ret = -EFAULT; + + if (copy_from_user(&info, + (void __user *) UPT_ESI(&new->thread.regs.regs), + sizeof(info))) + goto out; + + ret = -EINVAL; + if (LDT_empty(&info)) + goto out; + + idx = info.entry_number; + + ret = set_tls_entry(new, &info, idx, 0); +out: + return ret; +} + +/* XXX: use do_get_thread_area to read the host value? I'm not at all sure! */ +static int get_tls_entry(struct task_struct* task, struct user_desc *info, int idx) +{ + struct thread_struct *t = &task->thread; + + if (!t->arch.tls_array) + goto clear; + + if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) + return -EINVAL; + + if (!t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].present) + goto clear; + + *info = t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].tls; + +out: + /* Temporary debugging check, to make sure that things have been + * flushed. This could be triggered if load_TLS() failed. + */ + if (unlikely(task == current && !t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].flushed)) { + printk(KERN_ERR "get_tls_entry: task with pid %d got here " + "without flushed TLS.", current->pid); + } + + return 0; +clear: + /* When the TLS entry has not been set, the values read to user in the + * tls_array are 0 (because it's cleared at boot, see + * arch/i386/kernel/head.S:cpu_gdt_table). Emulate that. + */ + clear_user_desc(info); + info->entry_number = idx; + goto out; +} + +asmlinkage int sys_set_thread_area(struct user_desc __user *user_desc) +{ + struct user_desc info; + int idx, ret; + + if (!host_supports_tls) + return -ENOSYS; + + if (copy_from_user(&info, user_desc, sizeof(info))) + return -EFAULT; + + idx = info.entry_number; + + if (idx == -1) { + idx = get_free_idx(current); + if (idx < 0) + return idx; + info.entry_number = idx; + /* Tell the user which slot we chose for him.*/ + if (put_user(idx, &user_desc->entry_number)) + return -EFAULT; + } + + ret = CHOOSE_MODE_PROC(do_set_thread_area_tt, do_set_thread_area_skas, &info); + if (ret) + return ret; + return set_tls_entry(current, &info, idx, 1); +} + +/* + * Perform set_thread_area on behalf of the traced child. + * Note: error handling is not done on the deferred load, and this differ from + * i386. However the only possible error are caused by bugs. + */ +int ptrace_set_thread_area(struct task_struct *child, int idx, + struct user_desc __user *user_desc) +{ + struct user_desc info; + + if (!host_supports_tls) + return -EIO; + + if (copy_from_user(&info, user_desc, sizeof(info))) + return -EFAULT; + + return set_tls_entry(child, &info, idx, 0); +} + +asmlinkage int sys_get_thread_area(struct user_desc __user *user_desc) +{ + struct user_desc info; + int idx, ret; + + if (!host_supports_tls) + return -ENOSYS; + + if (get_user(idx, &user_desc->entry_number)) + return -EFAULT; + + ret = get_tls_entry(current, &info, idx); + if (ret < 0) + goto out; + + if (copy_to_user(user_desc, &info, sizeof(info))) + ret = -EFAULT; + +out: + return ret; +} + +/* + * Perform get_thread_area on behalf of the traced child. + */ +int ptrace_get_thread_area(struct task_struct *child, int idx, + struct user_desc __user *user_desc) +{ + struct user_desc info; + int ret; + + if (!host_supports_tls) + return -EIO; + + ret = get_tls_entry(child, &info, idx); + if (ret < 0) + goto out; + + if (copy_to_user(user_desc, &info, sizeof(info))) + ret = -EFAULT; +out: + return ret; +} + + +/* XXX: This part is probably common to i386 and x86-64. Don't create a common + * file for now, do that when implementing x86-64 support.*/ +static int __init __setup_host_supports_tls(void) { + check_host_supports_tls(&host_supports_tls, &host_gdt_entry_tls_min); + if (host_supports_tls) { + printk(KERN_INFO "Host TLS support detected\n"); + printk(KERN_INFO "Detected host type: "); + switch (host_gdt_entry_tls_min) { + case GDT_ENTRY_TLS_MIN_I386: + printk("i386\n"); + break; + case GDT_ENTRY_TLS_MIN_X86_64: + printk("x86_64\n"); + break; + } + } else + printk(KERN_ERR " Host TLS support NOT detected! " + "TLS support inside UML will not work\n"); + return 0; +} + +__initcall(__setup_host_supports_tls); diff --git a/arch/um/sys-x86_64/Makefile b/arch/um/sys-x86_64/Makefile index a351091fbd99..b5fc22babddf 100644 --- a/arch/um/sys-x86_64/Makefile +++ b/arch/um/sys-x86_64/Makefile @@ -4,31 +4,23 @@ # Licensed under the GPL # -#XXX: why into lib-y? -lib-y = bitops.o bugs.o csum-partial.o delay.o fault.o ldt.o mem.o memcpy.o \ - ptrace.o ptrace_user.o sigcontext.o signal.o syscalls.o \ - syscall_table.o sysrq.o thunk.o -lib-$(CONFIG_MODE_SKAS) += stub.o stub_segv.o +obj-y = bugs.o delay.o fault.o ldt.o mem.o ptrace.o ptrace_user.o \ + sigcontext.o signal.o syscalls.o syscall_table.o sysrq.o ksyms.o \ + tls.o -obj-y := ksyms.o -obj-$(CONFIG_MODULES) += module.o um_module.o +obj-$(CONFIG_MODE_SKAS) += stub.o stub_segv.o +obj-$(CONFIG_MODULES) += um_module.o + +subarch-obj-y = lib/bitops.o lib/csum-partial.o lib/memcpy.o lib/thunk.o +subarch-obj-$(CONFIG_MODULES) += kernel/module.o + +ldt-y = ../sys-i386/ldt.o USER_OBJS := ptrace_user.o sigcontext.o stub_segv.o -SYMLINKS = bitops.c csum-copy.S csum-partial.c csum-wrappers.c ldt.c memcpy.S \ - thunk.S module.c - include arch/um/scripts/Makefile.rules -bitops.c-dir = lib -csum-copy.S-dir = lib -csum-partial.c-dir = lib -csum-wrappers.c-dir = lib -ldt.c-dir = /arch/um/sys-i386 -memcpy.S-dir = lib -thunk.S-dir = lib -module.c-dir = kernel +extra-$(CONFIG_MODE_TT) += unmap.o -$(obj)/stub_segv.o: _c_flags = $(call unprofile,$(CFLAGS)) - -include arch/um/scripts/Makefile.unmap +$(obj)/stub_segv.o $(obj)/unmap.o: \ + _c_flags = $(call unprofile,$(CFLAGS)) diff --git a/arch/um/sys-x86_64/signal.c b/arch/um/sys-x86_64/signal.c index e75c4e1838b0..a4c46a8af008 100644 --- a/arch/um/sys-x86_64/signal.c +++ b/arch/um/sys-x86_64/signal.c @@ -137,7 +137,7 @@ int copy_sc_to_user_tt(struct sigcontext *to, struct _fpstate *fp, * delivery. The sp passed in is the original, and this needs * to be restored, so we stick it in separately. */ - err |= copy_to_user(&SC_SP(to), sp, sizeof(sp)); + err |= copy_to_user(&SC_SP(to), &sp, sizeof(sp)); if(from_fp != NULL){ err |= copy_to_user(&to->fpstate, &to_fp, sizeof(to->fpstate)); diff --git a/arch/um/sys-x86_64/tls.c b/arch/um/sys-x86_64/tls.c new file mode 100644 index 000000000000..ce1bf1b81c43 --- /dev/null +++ b/arch/um/sys-x86_64/tls.c @@ -0,0 +1,14 @@ +#include "linux/sched.h" + +void debug_arch_force_load_TLS(void) +{ +} + +void clear_flushed_tls(struct task_struct *task) +{ +} + +int arch_copy_tls(struct task_struct *t) +{ + return 0; +} diff --git a/arch/v850/kernel/v850_ksyms.c b/arch/v850/kernel/v850_ksyms.c index 8ffc29c1c89d..6bcfcfe88384 100644 --- a/arch/v850/kernel/v850_ksyms.c +++ b/arch/v850/kernel/v850_ksyms.c @@ -43,7 +43,6 @@ EXPORT_SYMBOL (strncmp); EXPORT_SYMBOL (strchr); EXPORT_SYMBOL (strlen); EXPORT_SYMBOL (strnlen); -EXPORT_SYMBOL (strpbrk); EXPORT_SYMBOL (strrchr); EXPORT_SYMBOL (strstr); EXPORT_SYMBOL (memset); diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig index 4310b4a311a5..408d44a59756 100644 --- a/arch/x86_64/Kconfig +++ b/arch/x86_64/Kconfig @@ -136,6 +136,11 @@ config X86_L1_CACHE_SHIFT default "7" if GENERIC_CPU || MPSC default "6" if MK8 +config X86_INTERNODE_CACHE_BYTES + int + default "4096" if X86_VSMP + default X86_L1_CACHE_BYTES if !X86_VSMP + config X86_TSC bool default y @@ -283,6 +288,11 @@ config K8_NUMA Northbridge of Opteron. It is recommended to use X86_64_ACPI_NUMA instead, which also takes priority if both are compiled in. +config NODES_SHIFT + int + default "6" + depends on NEED_MULTIPLE_NODES + # Dummy CONFIG option to select ACPI_NUMA from drivers/acpi/Kconfig. config X86_64_ACPI_NUMA diff --git a/arch/x86_64/Makefile b/arch/x86_64/Makefile index 585fd4a559c8..e573e2ab5510 100644 --- a/arch/x86_64/Makefile +++ b/arch/x86_64/Makefile @@ -24,37 +24,37 @@ LDFLAGS := -m elf_x86_64 OBJCOPYFLAGS := -O binary -R .note -R .comment -S LDFLAGS_vmlinux := - CHECKFLAGS += -D__x86_64__ -m64 +cflags-y := cflags-$(CONFIG_MK8) += $(call cc-option,-march=k8) cflags-$(CONFIG_MPSC) += $(call cc-option,-march=nocona) cflags-$(CONFIG_GENERIC_CPU) += $(call cc-option,-mtune=generic) -CFLAGS += $(cflags-y) -CFLAGS += -m64 -CFLAGS += -mno-red-zone -CFLAGS += -mcmodel=kernel -CFLAGS += -pipe +cflags-y += -m64 +cflags-y += -mno-red-zone +cflags-y += -mcmodel=kernel +cflags-y += -pipe cflags-$(CONFIG_REORDER) += -ffunction-sections # this makes reading assembly source easier, but produces worse code # actually it makes the kernel smaller too. -CFLAGS += -fno-reorder-blocks -CFLAGS += -Wno-sign-compare +cflags-y += -fno-reorder-blocks +cflags-y += -Wno-sign-compare ifneq ($(CONFIG_UNWIND_INFO),y) -CFLAGS += -fno-asynchronous-unwind-tables +cflags-y += -fno-asynchronous-unwind-tables endif ifneq ($(CONFIG_DEBUG_INFO),y) # -fweb shrinks the kernel a bit, but the difference is very small # it also messes up debugging, so don't use it for now. -#CFLAGS += $(call cc-option,-fweb) +#cflags-y += $(call cc-option,-fweb) endif # -funit-at-a-time shrinks the kernel .text considerably # unfortunately it makes reading oopses harder. -CFLAGS += $(call cc-option,-funit-at-a-time) +cflags-y += $(call cc-option,-funit-at-a-time) # prevent gcc from generating any FP code by mistake -CFLAGS += $(call cc-option,-mno-sse -mno-mmx -mno-sse2 -mno-3dnow,) +cflags-y += $(call cc-option,-mno-sse -mno-mmx -mno-sse2 -mno-3dnow,) +CFLAGS += $(cflags-y) AFLAGS += -m64 head-y := arch/x86_64/kernel/head.o arch/x86_64/kernel/head64.o arch/x86_64/kernel/init_task.o diff --git a/arch/x86_64/boot/video.S b/arch/x86_64/boot/video.S index 0587477c99f2..32327bb37aff 100644 --- a/arch/x86_64/boot/video.S +++ b/arch/x86_64/boot/video.S @@ -97,6 +97,7 @@ #define PARAM_VESAPM_OFF 0x30 #define PARAM_LFB_PAGES 0x32 #define PARAM_VESA_ATTRIB 0x34 +#define PARAM_CAPABILITIES 0x36 /* Define DO_STORE according to CONFIG_VIDEO_RETAIN */ #ifdef CONFIG_VIDEO_RETAIN @@ -233,6 +234,10 @@ mopar_gr: movw 18(%di), %ax movl %eax, %fs:(PARAM_LFB_SIZE) +# store mode capabilities + movl 10(%di), %eax + movl %eax, %fs:(PARAM_CAPABILITIES) + # switching the DAC to 8-bit is for <= 8 bpp only movw %fs:(PARAM_LFB_DEPTH), %ax cmpw $8, %ax diff --git a/arch/x86_64/defconfig b/arch/x86_64/defconfig index 566ecc97ee5a..3c45ec22b3fe 100644 --- a/arch/x86_64/defconfig +++ b/arch/x86_64/defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.16-git9 -# Sat Mar 25 15:18:40 2006 +# Linux kernel version: 2.6.17-rc1 +# Mon Apr 3 16:11:14 2006 # CONFIG_X86_64=y CONFIG_64BIT=y @@ -9,6 +9,7 @@ CONFIG_X86=y CONFIG_SEMAPHORE_SLEEPERS=y CONFIG_MMU=y CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_HWEIGHT=y CONFIG_GENERIC_CALIBRATE_DELAY=y CONFIG_X86_CMPXCHG=y CONFIG_EARLY_PRINTK=y @@ -55,10 +56,6 @@ CONFIG_BASE_FULL=y CONFIG_FUTEX=y CONFIG_EPOLL=y CONFIG_SHMEM=y -CONFIG_CC_ALIGN_FUNCTIONS=0 -CONFIG_CC_ALIGN_LABELS=0 -CONFIG_CC_ALIGN_LOOPS=0 -CONFIG_CC_ALIGN_JUMPS=0 CONFIG_SLAB=y # CONFIG_TINY_SHMEM is not set CONFIG_BASE_SMALL=0 @@ -70,7 +67,6 @@ CONFIG_BASE_SMALL=0 CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_MODULE_FORCE_UNLOAD=y -CONFIG_OBSOLETE_MODPARM=y # CONFIG_MODVERSIONS is not set # CONFIG_MODULE_SRCVERSION_ALL is not set # CONFIG_KMOD is not set @@ -81,6 +77,7 @@ CONFIG_STOP_MACHINE=y # CONFIG_LBD=y # CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_LSF is not set # # IO Schedulers @@ -105,6 +102,7 @@ CONFIG_X86_PC=y CONFIG_GENERIC_CPU=y CONFIG_X86_L1_CACHE_BYTES=128 CONFIG_X86_L1_CACHE_SHIFT=7 +CONFIG_X86_INTERNODE_CACHE_BYTES=128 CONFIG_X86_TSC=y CONFIG_X86_GOOD_APIC=y # CONFIG_MICROCODE is not set @@ -116,6 +114,7 @@ CONFIG_X86_LOCAL_APIC=y CONFIG_MTRR=y CONFIG_SMP=y CONFIG_SCHED_SMT=y +CONFIG_SCHED_MC=y # CONFIG_PREEMPT_NONE is not set CONFIG_PREEMPT_VOLUNTARY=y # CONFIG_PREEMPT is not set @@ -138,6 +137,7 @@ CONFIG_NEED_MULTIPLE_NODES=y CONFIG_SPLIT_PTLOCK_CPUS=4 CONFIG_MIGRATION=y CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID=y +CONFIG_OUT_OF_LINE_PFN_TO_PAGE=y CONFIG_NR_CPUS=32 CONFIG_HOTPLUG_CPU=y CONFIG_HPET_TIMER=y @@ -289,6 +289,7 @@ CONFIG_IP_PNP_DHCP=y # CONFIG_INET_AH is not set # CONFIG_INET_ESP is not set # CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set # CONFIG_INET_TUNNEL is not set CONFIG_INET_DIAG=y CONFIG_INET_TCP_DIAG=y @@ -300,6 +301,7 @@ CONFIG_IPV6=y # CONFIG_INET6_AH is not set # CONFIG_INET6_ESP is not set # CONFIG_INET6_IPCOMP is not set +# CONFIG_INET6_XFRM_TUNNEL is not set # CONFIG_INET6_TUNNEL is not set # CONFIG_IPV6_TUNNEL is not set # CONFIG_NETFILTER is not set @@ -704,7 +706,6 @@ CONFIG_S2IO=m # Wireless LAN (non-hamradio) # # CONFIG_NET_RADIO is not set -# CONFIG_NET_WIRELESS_RTNETLINK is not set # # Wan interfaces @@ -791,7 +792,7 @@ CONFIG_HW_CONSOLE=y # CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y -# CONFIG_SERIAL_8250_ACPI is not set +CONFIG_SERIAL_8250_PCI=y CONFIG_SERIAL_8250_NR_UARTS=4 CONFIG_SERIAL_8250_RUNTIME_UARTS=4 # CONFIG_SERIAL_8250_EXTENDED is not set @@ -921,6 +922,7 @@ CONFIG_HWMON=y # Digital Video Broadcasting Devices # # CONFIG_DVB is not set +# CONFIG_USB_DABUSB is not set # # Graphics support @@ -932,6 +934,8 @@ CONFIG_VIDEO_SELECT=y # Console display driver support # CONFIG_VGA_CONSOLE=y +CONFIG_VGACON_SOFT_SCROLLBACK=y +CONFIG_VGACON_SOFT_SCROLLBACK_SIZE=256 CONFIG_DUMMY_CONSOLE=y # @@ -1057,15 +1061,6 @@ CONFIG_USB_HIDINPUT=y # CONFIG_USB_MDC800 is not set # CONFIG_USB_MICROTEK is not set -# -# USB Multimedia devices -# -# CONFIG_USB_DABUSB is not set - -# -# Video4Linux support is needed for USB Multimedia device support -# - # # USB Network Adapters # @@ -1117,16 +1112,27 @@ CONFIG_USB_MON=y # # CONFIG_MMC is not set +# +# LED devices +# +# CONFIG_NEW_LEDS is not set + # # InfiniBand support # # CONFIG_INFINIBAND is not set +# CONFIG_IPATH_CORE is not set # # EDAC - error detection and reporting (RAS) (EXPERIMENTAL) # # CONFIG_EDAC is not set +# +# Real Time Clock +# +# CONFIG_RTC_CLASS is not set + # # Firmware Drivers # diff --git a/arch/x86_64/ia32/ia32entry.S b/arch/x86_64/ia32/ia32entry.S index 35b2faccdc6c..5a9802676689 100644 --- a/arch/x86_64/ia32/ia32entry.S +++ b/arch/x86_64/ia32/ia32entry.S @@ -15,6 +15,8 @@ #include #include +#define IA32_NR_syscalls ((ia32_syscall_end - ia32_sys_call_table)/8) + .macro IA32_ARG_FIXUP noebp=0 movl %edi,%r8d .if \noebp @@ -109,8 +111,8 @@ ENTRY(ia32_sysenter_target) CFI_REMEMBER_STATE jnz sysenter_tracesys sysenter_do_call: - cmpl $(IA32_NR_syscalls),%eax - jae ia32_badsys + cmpl $(IA32_NR_syscalls-1),%eax + ja ia32_badsys IA32_ARG_FIXUP 1 call *ia32_sys_call_table(,%rax,8) movq %rax,RAX-ARGOFFSET(%rsp) @@ -210,8 +212,8 @@ ENTRY(ia32_cstar_target) CFI_REMEMBER_STATE jnz cstar_tracesys cstar_do_call: - cmpl $IA32_NR_syscalls,%eax - jae ia32_badsys + cmpl $IA32_NR_syscalls-1,%eax + ja ia32_badsys IA32_ARG_FIXUP 1 call *ia32_sys_call_table(,%rax,8) movq %rax,RAX-ARGOFFSET(%rsp) @@ -296,8 +298,8 @@ ENTRY(ia32_syscall) testl $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SECCOMP),threadinfo_flags(%r10) jnz ia32_tracesys ia32_do_syscall: - cmpl $(IA32_NR_syscalls),%eax - jae ia32_badsys + cmpl $(IA32_NR_syscalls-1),%eax + ja ia32_badsys IA32_ARG_FIXUP call *ia32_sys_call_table(,%rax,8) # xxx: rip relative ia32_sysret: @@ -685,12 +687,11 @@ ia32_sys_call_table: .quad sys_readlinkat /* 305 */ .quad sys_fchmodat .quad sys_faccessat - .quad sys_ni_syscall /* pselect6 for now */ - .quad sys_ni_syscall /* ppoll for now */ + .quad quiet_ni_syscall /* pselect6 for now */ + .quad quiet_ni_syscall /* ppoll for now */ .quad sys_unshare /* 310 */ .quad compat_sys_set_robust_list .quad compat_sys_get_robust_list + .quad sys_splice + .quad sys_sync_file_range ia32_syscall_end: - .rept IA32_NR_syscalls-(ia32_syscall_end-ia32_sys_call_table)/8 - .quad ni_syscall - .endr diff --git a/arch/x86_64/ia32/vsyscall-sigreturn.S b/arch/x86_64/ia32/vsyscall-sigreturn.S index d90321fe9bba..1384367cdbe1 100644 --- a/arch/x86_64/ia32/vsyscall-sigreturn.S +++ b/arch/x86_64/ia32/vsyscall-sigreturn.S @@ -32,9 +32,28 @@ __kernel_rt_sigreturn: .size __kernel_rt_sigreturn,.-.LSTART_rt_sigreturn .section .eh_frame,"a",@progbits +.LSTARTFRAMES: + .long .LENDCIES-.LSTARTCIES +.LSTARTCIES: + .long 0 /* CIE ID */ + .byte 1 /* Version number */ + .string "zRS" /* NUL-terminated augmentation string */ + .uleb128 1 /* Code alignment factor */ + .sleb128 -4 /* Data alignment factor */ + .byte 8 /* Return address register column */ + .uleb128 1 /* Augmentation value length */ + .byte 0x1b /* DW_EH_PE_pcrel|DW_EH_PE_sdata4. */ + .byte 0x0c /* DW_CFA_def_cfa */ + .uleb128 4 + .uleb128 4 + .byte 0x88 /* DW_CFA_offset, column 0x8 */ + .uleb128 1 + .align 4 +.LENDCIES: + .long .LENDFDE2-.LSTARTFDE2 /* Length FDE */ .LSTARTFDE2: - .long .LSTARTFDE2-.LSTARTFRAME /* CIE pointer */ + .long .LSTARTFDE2-.LSTARTFRAMES /* CIE pointer */ /* HACK: The dwarf2 unwind routines will subtract 1 from the return address to get an address in the middle of the presumed call instruction. Since we didn't get here via @@ -97,7 +116,7 @@ __kernel_rt_sigreturn: .long .LENDFDE3-.LSTARTFDE3 /* Length FDE */ .LSTARTFDE3: - .long .LSTARTFDE3-.LSTARTFRAME /* CIE pointer */ + .long .LSTARTFDE3-.LSTARTFRAMES /* CIE pointer */ /* HACK: See above wrt unwind library assumptions. */ .long .LSTART_rt_sigreturn-1-. /* PC-relative start address */ .long .LEND_rt_sigreturn-.LSTART_rt_sigreturn+1 diff --git a/arch/x86_64/kernel/aperture.c b/arch/x86_64/kernel/aperture.c index fffd6b0a2fab..70b9d21ed675 100644 --- a/arch/x86_64/kernel/aperture.c +++ b/arch/x86_64/kernel/aperture.c @@ -80,7 +80,7 @@ static int __init aperture_valid(char *name, u64 aper_base, u32 aper_size) printk("Aperture from %s beyond 4GB. Ignoring.\n",name); return 0; } - if (e820_mapped(aper_base, aper_base + aper_size, E820_RAM)) { + if (e820_any_mapped(aper_base, aper_base + aper_size, E820_RAM)) { printk("Aperture from %s pointing to e820 RAM. Ignoring.\n",name); return 0; } diff --git a/arch/x86_64/kernel/apic.c b/arch/x86_64/kernel/apic.c index d54620147e8e..100a30c40044 100644 --- a/arch/x86_64/kernel/apic.c +++ b/arch/x86_64/kernel/apic.c @@ -615,7 +615,7 @@ static int __init apic_set_verbosity(char *str) printk(KERN_WARNING "APIC Verbosity level %s not recognised" " use apic=verbose or apic=debug", str); - return 0; + return 1; } __setup("apic=", apic_set_verbosity); @@ -1137,35 +1137,35 @@ int __init APIC_init_uniprocessor (void) static __init int setup_disableapic(char *str) { disable_apic = 1; - return 0; + return 1; } static __init int setup_nolapic(char *str) { disable_apic = 1; - return 0; + return 1; } static __init int setup_noapictimer(char *str) { if (str[0] != ' ' && str[0] != 0) - return -1; + return 0; disable_apic_timer = 1; - return 0; + return 1; } static __init int setup_apicmaintimer(char *str) { apic_runs_main_timer = 1; nohpet = 1; - return 0; + return 1; } __setup("apicmaintimer", setup_apicmaintimer); static __init int setup_noapicmaintimer(char *str) { apic_runs_main_timer = -1; - return 0; + return 1; } __setup("noapicmaintimer", setup_noapicmaintimer); diff --git a/arch/x86_64/kernel/e820.c b/arch/x86_64/kernel/e820.c index 293cd71a266a..62776c07cff1 100644 --- a/arch/x86_64/kernel/e820.c +++ b/arch/x86_64/kernel/e820.c @@ -80,7 +80,12 @@ static inline int bad_addr(unsigned long *addrp, unsigned long size) return 0; } -int __init e820_mapped(unsigned long start, unsigned long end, unsigned type) +/* + * This function checks if any part of the range is mapped + * with type. + */ +int __meminit +e820_any_mapped(unsigned long start, unsigned long end, unsigned type) { int i; for (i = 0; i < e820.nr_map; i++) { @@ -94,6 +99,35 @@ int __init e820_mapped(unsigned long start, unsigned long end, unsigned type) return 0; } +/* + * This function checks if the entire range is mapped with type. + * + * Note: this function only works correct if the e820 table is sorted and + * not-overlapping, which is the case + */ +int __init e820_all_mapped(unsigned long start, unsigned long end, unsigned type) +{ + int i; + for (i = 0; i < e820.nr_map; i++) { + struct e820entry *ei = &e820.map[i]; + if (type && ei->type != type) + continue; + /* is the region (part) in overlap with the current region ?*/ + if (ei->addr >= end || ei->addr + ei->size <= start) + continue; + + /* if the region is at the beginning of we move + * start to the end of the region since it's ok until there + */ + if (ei->addr <= start) + start = ei->addr + ei->size; + /* if start is now at or beyond end, we're done, full coverage */ + if (start >= end) + return 1; /* we're done */ + } + return 0; +} + /* * Find a free area in a specific range. */ diff --git a/arch/x86_64/kernel/early_printk.c b/arch/x86_64/kernel/early_printk.c index 13af920b6594..b93ef5b51980 100644 --- a/arch/x86_64/kernel/early_printk.c +++ b/arch/x86_64/kernel/early_printk.c @@ -221,7 +221,7 @@ int __init setup_early_printk(char *opt) char buf[256]; if (early_console_initialized) - return -1; + return 1; strlcpy(buf,opt,sizeof(buf)); space = strchr(buf, ' '); diff --git a/arch/x86_64/kernel/entry.S b/arch/x86_64/kernel/entry.S index 8538bfea30e6..c946e4fe67a7 100644 --- a/arch/x86_64/kernel/entry.S +++ b/arch/x86_64/kernel/entry.S @@ -180,6 +180,10 @@ rff_trace: * * XXX if we had a free scratch register we could save the RSP into the stack frame * and report it properly in ps. Unfortunately we haven't. + * + * When user can change the frames always force IRET. That is because + * it deals with uncanonical addresses better. SYSRET has trouble + * with them due to bugs in both AMD and Intel CPUs. */ ENTRY(system_call) @@ -254,7 +258,10 @@ sysret_signal: xorl %esi,%esi # oldset -> arg2 call ptregscall_common 1: movl $_TIF_NEED_RESCHED,%edi - jmp sysret_check + /* Use IRET because user could have changed frame. This + works because ptregscall_common has called FIXUP_TOP_OF_STACK. */ + cli + jmp int_with_check badsys: movq $-ENOSYS,RAX-ARGOFFSET(%rsp) @@ -280,7 +287,8 @@ tracesys: call syscall_trace_leave RESTORE_TOP_OF_STACK %rbx RESTORE_REST - jmp ret_from_sys_call + /* Use IRET because user could have changed frame */ + jmp int_ret_from_sys_call CFI_ENDPROC /* @@ -408,25 +416,9 @@ ENTRY(stub_execve) CFI_ADJUST_CFA_OFFSET -8 CFI_REGISTER rip, r11 SAVE_REST - movq %r11, %r15 - CFI_REGISTER rip, r15 FIXUP_TOP_OF_STACK %r11 call sys_execve - GET_THREAD_INFO(%rcx) - bt $TIF_IA32,threadinfo_flags(%rcx) - CFI_REMEMBER_STATE - jc exec_32bit RESTORE_TOP_OF_STACK %r11 - movq %r15, %r11 - CFI_REGISTER rip, r11 - RESTORE_REST - pushq %r11 - CFI_ADJUST_CFA_OFFSET 8 - CFI_REL_OFFSET rip, 0 - ret - -exec_32bit: - CFI_RESTORE_STATE movq %rax,RAX(%rsp) RESTORE_REST jmp int_ret_from_sys_call diff --git a/arch/x86_64/kernel/mce.c b/arch/x86_64/kernel/mce.c index 04282ef9fbd4..6f0790e8b6d3 100644 --- a/arch/x86_64/kernel/mce.c +++ b/arch/x86_64/kernel/mce.c @@ -29,6 +29,8 @@ #define MISC_MCELOG_MINOR 227 #define NR_BANKS 6 +atomic_t mce_entry; + static int mce_dont_init; /* 0: always panic, 1: panic if deadlock possible, 2: try to avoid panic, @@ -172,10 +174,12 @@ void do_machine_check(struct pt_regs * regs, long error_code) int i; int panicm_found = 0; + atomic_inc(&mce_entry); + if (regs) notify_die(DIE_NMI, "machine check", regs, error_code, 18, SIGKILL); if (!banks) - return; + goto out2; memset(&m, 0, sizeof(struct mce)); m.cpu = safe_smp_processor_id(); @@ -266,6 +270,8 @@ void do_machine_check(struct pt_regs * regs, long error_code) out: /* Last thing done in the machine check exception to clear state. */ wrmsrl(MSR_IA32_MCG_STATUS, 0); + out2: + atomic_dec(&mce_entry); } /* @@ -501,7 +507,7 @@ static struct miscdevice mce_log_device = { static int __init mcheck_disable(char *str) { mce_dont_init = 1; - return 0; + return 1; } /* mce=off disables machine check. Note you can reenable it later @@ -521,7 +527,7 @@ static int __init mcheck_enable(char *str) get_option(&str, &tolerant); else printk("mce= argument %s ignored. Please use /sys", str); - return 0; + return 1; } __setup("nomce", mcheck_disable); diff --git a/arch/x86_64/kernel/nmi.c b/arch/x86_64/kernel/nmi.c index d9e4067faf05..4e6357fe0ec3 100644 --- a/arch/x86_64/kernel/nmi.c +++ b/arch/x86_64/kernel/nmi.c @@ -34,6 +34,7 @@ #include #include #include +#include /* * lapic_nmi_owner tracks the ownership of the lapic NMI hardware: @@ -480,6 +481,12 @@ void __kprobes nmi_watchdog_tick(struct pt_regs * regs, unsigned reason) __get_cpu_var(nmi_touch) = 0; touched = 1; } +#ifdef CONFIG_X86_MCE + /* Could check oops_in_progress here too, but it's safer + not too */ + if (atomic_read(&mce_entry) > 0) + touched = 1; +#endif if (!touched && __get_cpu_var(last_irq_sum) == sum) { /* * Ayiee, looks like this CPU is stuck ... diff --git a/arch/x86_64/kernel/pci-dma.c b/arch/x86_64/kernel/pci-dma.c index 03c9eeedb0f3..af035ede70cd 100644 --- a/arch/x86_64/kernel/pci-dma.c +++ b/arch/x86_64/kernel/pci-dma.c @@ -48,9 +48,11 @@ dma_alloc_pages(struct device *dev, gfp_t gfp, unsigned order) { struct page *page; int node; +#ifdef CONFIG_PCI if (dev->bus == &pci_bus_type) node = pcibus_to_node(to_pci_dev(dev)->bus); else +#endif node = numa_node_id(); page = alloc_pages_node(node, gfp, order); return page ? page_address(page) : NULL; diff --git a/arch/x86_64/kernel/pmtimer.c b/arch/x86_64/kernel/pmtimer.c index ee5ee4891f3d..b0444a415bd6 100644 --- a/arch/x86_64/kernel/pmtimer.c +++ b/arch/x86_64/kernel/pmtimer.c @@ -121,7 +121,7 @@ unsigned int do_gettimeoffset_pm(void) static int __init nopmtimer_setup(char *s) { pmtmr_ioport = 0; - return 0; + return 1; } __setup("nopmtimer", nopmtimer_setup); diff --git a/arch/x86_64/kernel/process.c b/arch/x86_64/kernel/process.c index 70dd8e5c6889..1c44b53cb15b 100644 --- a/arch/x86_64/kernel/process.c +++ b/arch/x86_64/kernel/process.c @@ -781,10 +781,16 @@ long do_arch_prctl(struct task_struct *task, int code, unsigned long addr) } case ARCH_GET_GS: { unsigned long base; + unsigned gsindex; if (task->thread.gsindex == GS_TLS_SEL) base = read_32bit_tls(task, GS_TLS); - else if (doit) - rdmsrl(MSR_KERNEL_GS_BASE, base); + else if (doit) { + asm("movl %%gs,%0" : "=r" (gsindex)); + if (gsindex) + rdmsrl(MSR_KERNEL_GS_BASE, base); + else + base = task->thread.gs; + } else base = task->thread.gs; ret = put_user(base, (unsigned long __user *)addr); diff --git a/arch/x86_64/kernel/ptrace.c b/arch/x86_64/kernel/ptrace.c index d44b2c1e63a6..da8e7903d817 100644 --- a/arch/x86_64/kernel/ptrace.c +++ b/arch/x86_64/kernel/ptrace.c @@ -274,11 +274,6 @@ static int putreg(struct task_struct *child, return -EIO; value &= 0xffff; break; - case offsetof(struct user_regs_struct, rip): - /* Check if the new RIP address is canonical */ - if (value >= TASK_SIZE_OF(child)) - return -EIO; - break; } put_stack_long(child, regno - sizeof(struct pt_regs), value); return 0; diff --git a/arch/x86_64/kernel/setup.c b/arch/x86_64/kernel/setup.c index d1f3e9272c05..c50b06765a80 100644 --- a/arch/x86_64/kernel/setup.c +++ b/arch/x86_64/kernel/setup.c @@ -353,8 +353,10 @@ static __init void parse_cmdline_early (char ** cmdline_p) if (fullarg(from, "enable_timer_pin_1")) disable_timer_pin_1 = -1; - if (fullarg(from, "nolapic") || fullarg(from, "disableapic")) + if (fullarg(from, "nolapic") || fullarg(from, "disableapic")) { + clear_bit(X86_FEATURE_APIC, boot_cpu_data.x86_capability); disable_apic = 1; + } if (fullarg(from, "noapic")) skip_ioapic_setup = 1; @@ -540,7 +542,7 @@ void __init alternative_instructions(void) static int __init noreplacement_setup(char *s) { no_replacement = 1; - return 0; + return 1; } __setup("noreplacement", noreplacement_setup); diff --git a/arch/x86_64/kernel/setup64.c b/arch/x86_64/kernel/setup64.c index eabdb63fec31..8a691fa6d393 100644 --- a/arch/x86_64/kernel/setup64.c +++ b/arch/x86_64/kernel/setup64.c @@ -55,7 +55,7 @@ int __init nonx_setup(char *str) do_not_nx = 1; __supported_pte_mask &= ~_PAGE_NX; } - return 0; + return 1; } __setup("noexec=", nonx_setup); /* parsed early actually */ @@ -74,7 +74,7 @@ static int __init nonx32_setup(char *str) force_personality32 &= ~READ_IMPLIES_EXEC; else if (!strcmp(str, "off")) force_personality32 |= READ_IMPLIES_EXEC; - return 0; + return 1; } __setup("noexec32=", nonx32_setup); diff --git a/arch/x86_64/kernel/smpboot.c b/arch/x86_64/kernel/smpboot.c index ea48fa638070..71a7222cf9ce 100644 --- a/arch/x86_64/kernel/smpboot.c +++ b/arch/x86_64/kernel/smpboot.c @@ -353,7 +353,7 @@ static void __cpuinit tsc_sync_wait(void) static __init int notscsync_setup(char *s) { notscsync = 1; - return 0; + return 1; } __setup("notscsync", notscsync_setup); diff --git a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c index 473b514b66e4..7392570f975d 100644 --- a/arch/x86_64/kernel/time.c +++ b/arch/x86_64/kernel/time.c @@ -726,7 +726,7 @@ static __init int late_hpet_init(void) unsigned int ntimer; if (!vxtime.hpet_address) - return -1; + return 0; memset(&hd, 0, sizeof (hd)); @@ -917,6 +917,8 @@ void __init time_init(void) vxtime.hpet_address = 0; if (hpet_use_timer) { + /* set tick_nsec to use the proper rate for HPET */ + tick_nsec = TICK_NSEC_HPET; cpu_khz = hpet_calibrate_tsc(); timename = "HPET"; #ifdef CONFIG_X86_PM_TIMER @@ -1306,7 +1308,7 @@ irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs) static int __init nohpet_setup(char *s) { nohpet = 1; - return 0; + return 1; } __setup("nohpet", nohpet_setup); @@ -1314,7 +1316,7 @@ __setup("nohpet", nohpet_setup); int __init notsc_setup(char *s) { notsc = 1; - return 0; + return 1; } __setup("notsc", notsc_setup); diff --git a/arch/x86_64/kernel/traps.c b/arch/x86_64/kernel/traps.c index edaa9fe654dc..6bda322d3caf 100644 --- a/arch/x86_64/kernel/traps.c +++ b/arch/x86_64/kernel/traps.c @@ -973,14 +973,14 @@ void __init trap_init(void) static int __init oops_dummy(char *s) { panic_on_oops = 1; - return -1; + return 1; } __setup("oops=", oops_dummy); static int __init kstack_setup(char *s) { kstack_depth_to_print = simple_strtoul(s,NULL,0); - return 0; + return 1; } __setup("kstack=", kstack_setup); diff --git a/arch/x86_64/kernel/vmlinux.lds.S b/arch/x86_64/kernel/vmlinux.lds.S index 39ff0708f803..b81f473c4a19 100644 --- a/arch/x86_64/kernel/vmlinux.lds.S +++ b/arch/x86_64/kernel/vmlinux.lds.S @@ -65,7 +65,7 @@ SECTIONS .data.cacheline_aligned : AT(ADDR(.data.cacheline_aligned) - LOAD_OFFSET) { *(.data.cacheline_aligned) } - . = ALIGN(CONFIG_X86_L1_CACHE_BYTES); + . = ALIGN(CONFIG_X86_INTERNODE_CACHE_BYTES); .data.read_mostly : AT(ADDR(.data.read_mostly) - LOAD_OFFSET) { *(.data.read_mostly) } diff --git a/arch/x86_64/kernel/x8664_ksyms.c b/arch/x86_64/kernel/x8664_ksyms.c index d96a9348e5a2..1def21c9f7cd 100644 --- a/arch/x86_64/kernel/x8664_ksyms.c +++ b/arch/x86_64/kernel/x8664_ksyms.c @@ -102,8 +102,6 @@ EXPORT_SYMBOL(cpu_callout_map); EXPORT_SYMBOL(screen_info); #endif -EXPORT_SYMBOL(get_wchan); - EXPORT_SYMBOL(rtc_lock); EXPORT_SYMBOL_GPL(set_nmi_callback); @@ -114,7 +112,6 @@ EXPORT_SYMBOL_GPL(unset_nmi_callback); #undef memcpy #undef memset #undef memmove -#undef strlen extern void * memset(void *,int,__kernel_size_t); extern size_t strlen(const char *); @@ -123,8 +120,6 @@ extern void * memcpy(void *,const void *,__kernel_size_t); extern void * __memcpy(void *,const void *,__kernel_size_t); EXPORT_SYMBOL(memset); -EXPORT_SYMBOL(strlen); -EXPORT_SYMBOL(strpbrk); EXPORT_SYMBOL(memmove); EXPORT_SYMBOL(memcpy); EXPORT_SYMBOL(__memcpy); diff --git a/arch/x86_64/mm/fault.c b/arch/x86_64/mm/fault.c index 316c53de47bd..55250593d8c9 100644 --- a/arch/x86_64/mm/fault.c +++ b/arch/x86_64/mm/fault.c @@ -623,6 +623,6 @@ void vmalloc_sync_all(void) static int __init enable_pagefaulttrace(char *str) { page_fault_trace = 1; - return 0; + return 1; } __setup("pagefaulttrace", enable_pagefaulttrace); diff --git a/arch/x86_64/mm/init.c b/arch/x86_64/mm/init.c index e5f7f1c34462..4ba34e95d835 100644 --- a/arch/x86_64/mm/init.c +++ b/arch/x86_64/mm/init.c @@ -305,7 +305,7 @@ static void __meminit phys_pud_init(pud_t *pud, unsigned long address, unsigned if (paddr >= end) break; - if (!after_bootmem && !e820_mapped(paddr, paddr+PUD_SIZE, 0)) { + if (!after_bootmem && !e820_any_mapped(paddr, paddr+PUD_SIZE, 0)) { set_pud(pud, __pud(0)); continue; } @@ -507,9 +507,8 @@ void __init clear_kernel_mapping(unsigned long address, unsigned long size) /* * Memory hotplug specific functions - * These are only for non-NUMA machines right now. */ -#ifdef CONFIG_MEMORY_HOTPLUG +#if defined(CONFIG_ACPI_HOTPLUG_MEMORY) || defined(CONFIG_ACPI_HOTPLUG_MEMORY_MODULE) void online_page(struct page *page) { @@ -520,6 +519,38 @@ void online_page(struct page *page) num_physpages++; } +#ifndef CONFIG_MEMORY_HOTPLUG +/* + * Memory Hotadd without sparsemem. The mem_maps have been allocated in advance, + * just online the pages. + */ +int __add_pages(struct zone *z, unsigned long start_pfn, unsigned long nr_pages) +{ + int err = -EIO; + unsigned long pfn; + unsigned long total = 0, mem = 0; + for (pfn = start_pfn; pfn < start_pfn + nr_pages; pfn++) { + if (pfn_valid(pfn)) { + online_page(pfn_to_page(pfn)); + err = 0; + mem++; + } + total++; + } + if (!err) { + z->spanned_pages += total; + z->present_pages += mem; + z->zone_pgdat->node_spanned_pages += total; + z->zone_pgdat->node_present_pages += mem; + } + return err; +} +#endif + +/* + * Memory is added always to NORMAL zone. This means you will never get + * additional DMA/DMA32 memory. + */ int add_memory(u64 start, u64 size) { struct pglist_data *pgdat = NODE_DATA(0); diff --git a/arch/x86_64/mm/numa.c b/arch/x86_64/mm/numa.c index 4be82d6e2b48..cc02573a3271 100644 --- a/arch/x86_64/mm/numa.c +++ b/arch/x86_64/mm/numa.c @@ -100,11 +100,30 @@ int early_pfn_to_nid(unsigned long pfn) } #endif +static void * __init +early_node_mem(int nodeid, unsigned long start, unsigned long end, + unsigned long size) +{ + unsigned long mem = find_e820_area(start, end, size); + void *ptr; + if (mem != -1L) + return __va(mem); + ptr = __alloc_bootmem_nopanic(size, + SMP_CACHE_BYTES, __pa(MAX_DMA_ADDRESS)); + if (ptr == 0) { + printk(KERN_ERR "Cannot find %lu bytes in node %d\n", + size, nodeid); + return NULL; + } + return ptr; +} + /* Initialize bootmem allocator for a node */ void __init setup_node_bootmem(int nodeid, unsigned long start, unsigned long end) { unsigned long start_pfn, end_pfn, bootmap_pages, bootmap_size, bootmap_start; unsigned long nodedata_phys; + void *bootmap; const int pgdat_size = round_up(sizeof(pg_data_t), PAGE_SIZE); start = round_up(start, ZONE_ALIGN); @@ -114,13 +133,11 @@ void __init setup_node_bootmem(int nodeid, unsigned long start, unsigned long en start_pfn = start >> PAGE_SHIFT; end_pfn = end >> PAGE_SHIFT; - nodedata_phys = find_e820_area(start, end, pgdat_size); - if (nodedata_phys == -1L) - panic("Cannot find memory pgdat in node %d\n", nodeid); + node_data[nodeid] = early_node_mem(nodeid, start, end, pgdat_size); + if (node_data[nodeid] == NULL) + return; + nodedata_phys = __pa(node_data[nodeid]); - Dprintk("nodedata_phys %lx\n", nodedata_phys); - - node_data[nodeid] = phys_to_virt(nodedata_phys); memset(NODE_DATA(nodeid), 0, sizeof(pg_data_t)); NODE_DATA(nodeid)->bdata = &plat_node_bdata[nodeid]; NODE_DATA(nodeid)->node_start_pfn = start_pfn; @@ -129,9 +146,15 @@ void __init setup_node_bootmem(int nodeid, unsigned long start, unsigned long en /* Find a place for the bootmem map */ bootmap_pages = bootmem_bootmap_pages(end_pfn - start_pfn); bootmap_start = round_up(nodedata_phys + pgdat_size, PAGE_SIZE); - bootmap_start = find_e820_area(bootmap_start, end, bootmap_pages<= end) + free_bootmem((unsigned long)node_data[nodeid],pgdat_size); + node_data[nodeid] = NULL; + return; + } + bootmap_start = __pa(bootmap); Dprintk("bootmap start %lu pages %lu\n", bootmap_start, bootmap_pages); bootmap_size = init_bootmem_node(NODE_DATA(nodeid), @@ -142,6 +165,9 @@ void __init setup_node_bootmem(int nodeid, unsigned long start, unsigned long en reserve_bootmem_node(NODE_DATA(nodeid), nodedata_phys, pgdat_size); reserve_bootmem_node(NODE_DATA(nodeid), bootmap_start, bootmap_pages<> PAGE_SHIFT; + unsigned long e_pfn = end >> PAGE_SHIFT; + int changed = 0; + struct bootnode *nd = &nodes_add[node]; + + /* I had some trouble with strange memory hotadd regions breaking + the boot. Be very strict here and reject anything unexpected. + If you want working memory hotadd write correct SRATs. + + The node size check is a basic sanity check to guard against + mistakes */ + if ((signed long)(end - start) < NODE_MIN_SIZE) { + printk(KERN_ERR "SRAT: Hotplug area too small\n"); + return -1; + } + + /* This check might be a bit too strict, but I'm keeping it for now. */ + if (e820_hole_size(s_pfn, e_pfn) != e_pfn - s_pfn) { + printk(KERN_ERR "SRAT: Hotplug area has existing memory\n"); + return -1; + } + + if (!hotadd_enough_memory(&nodes_add[node])) { + printk(KERN_ERR "SRAT: Hotplug area too large\n"); + return -1; + } + + /* Looks good */ + + found_add_area = 1; + if (nd->start == nd->end) { + nd->start = start; + nd->end = end; + changed = 1; + } else { + if (nd->start == end) { + nd->start = start; + changed = 1; + } + if (nd->end == start) { + nd->end = end; + changed = 1; + } + if (!changed) + printk(KERN_ERR "SRAT: Hotplug zone not continuous. Partly ignored\n"); + } + + if ((nd->end >> PAGE_SHIFT) > end_pfn) + end_pfn = nd->end >> PAGE_SHIFT; + + if (changed) + printk(KERN_INFO "SRAT: hot plug zone found %Lx - %Lx\n", nd->start, nd->end); + return 0; +} +#endif + /* Callback for parsing of the Proximity Domain <-> Memory Area mappings */ void __init acpi_numa_memory_affinity_init(struct acpi_table_memory_affinity *ma) { - struct bootnode *nd; + struct bootnode *nd, oldnode; unsigned long start, end; int node, pxm; int i; @@ -172,6 +292,8 @@ acpi_numa_memory_affinity_init(struct acpi_table_memory_affinity *ma) } if (ma->flags.enabled == 0) return; + if (ma->flags.hot_pluggable && hotadd_percent == 0) + return; start = ma->base_addr_lo | ((u64)ma->base_addr_hi << 32); end = start + (ma->length_lo | ((u64)ma->length_hi << 32)); pxm = ma->proximity_domain; @@ -181,10 +303,6 @@ acpi_numa_memory_affinity_init(struct acpi_table_memory_affinity *ma) bad_srat(); return; } - /* It is fine to add this area to the nodes data it will be used later*/ - if (ma->flags.hot_pluggable == 1) - printk(KERN_INFO "SRAT: hot plug zone found %lx - %lx \n", - start, end); i = conflicting_nodes(start, end); if (i == node) { printk(KERN_WARNING @@ -199,6 +317,7 @@ acpi_numa_memory_affinity_init(struct acpi_table_memory_affinity *ma) return; } nd = &nodes[node]; + oldnode = *nd; if (!node_test_and_set(node, nodes_parsed)) { nd->start = start; nd->end = end; @@ -208,8 +327,19 @@ acpi_numa_memory_affinity_init(struct acpi_table_memory_affinity *ma) if (nd->end < end) nd->end = end; } + printk(KERN_INFO "SRAT: Node %u PXM %u %Lx-%Lx\n", node, pxm, nd->start, nd->end); + +#ifdef RESERVE_HOTADD + if (ma->flags.hot_pluggable && reserve_hotadd(node, start, end) < 0) { + /* Ignore hotadd region. Undo damage */ + printk(KERN_NOTICE "SRAT: Hotplug region ignored\n"); + *nd = oldnode; + if ((nd->start | nd->end) == 0) + node_clear(node, nodes_parsed); + } +#endif } /* Sanity check to catch more bad SRATs (they are amazingly common). @@ -225,6 +355,9 @@ static int nodes_cover_memory(void) unsigned long e = nodes[i].end >> PAGE_SHIFT; pxmram += e - s; pxmram -= e820_hole_size(s, e); + pxmram -= nodes_add[i].end - nodes_add[i].start; + if ((long)pxmram < 0) + pxmram = 0; } e820ram = end_pfn - e820_hole_size(0, end_pfn); @@ -258,7 +391,7 @@ int __init acpi_scan_nodes(unsigned long start, unsigned long end) /* First clean up the node list */ for (i = 0; i < MAX_NUMNODES; i++) { - cutoff_node(i, start, end); + cutoff_node(i, start, end); if ((nodes[i].end - nodes[i].start) < NODE_MIN_SIZE) unparse_node(i); } @@ -282,6 +415,12 @@ int __init acpi_scan_nodes(unsigned long start, unsigned long end) /* Finally register nodes */ for_each_node_mask(i, nodes_parsed) setup_node_bootmem(i, nodes[i].start, nodes[i].end); + /* Try again in case setup_node_bootmem missed one due + to missing bootmem */ + for_each_node_mask(i, nodes_parsed) + if (!node_online(i)) + setup_node_bootmem(i, nodes[i].start, nodes[i].end); + for (i = 0; i < NR_CPUS; i++) { if (cpu_to_node[i] == NUMA_NO_NODE) continue; @@ -303,6 +442,25 @@ static int node_to_pxm(int n) return 0; } +void __init srat_reserve_add_area(int nodeid) +{ + if (found_add_area && nodes_add[nodeid].end) { + u64 total_mb; + + printk(KERN_INFO "SRAT: Reserving hot-add memory space " + "for node %d at %Lx-%Lx\n", + nodeid, nodes_add[nodeid].start, nodes_add[nodeid].end); + total_mb = (nodes_add[nodeid].end - nodes_add[nodeid].start) + >> PAGE_SHIFT; + total_mb *= sizeof(struct page); + total_mb >>= 20; + printk(KERN_INFO "SRAT: This will cost you %Lu MB of " + "pre-allocated memory.\n", (unsigned long long)total_mb); + reserve_bootmem_node(NODE_DATA(nodeid), nodes_add[nodeid].start, + nodes_add[nodeid].end - nodes_add[nodeid].start); + } +} + int __node_distance(int a, int b) { int index; diff --git a/arch/x86_64/pci/mmconfig.c b/arch/x86_64/pci/mmconfig.c index e616500207e4..a2060e4d5de6 100644 --- a/arch/x86_64/pci/mmconfig.c +++ b/arch/x86_64/pci/mmconfig.c @@ -9,11 +9,16 @@ #include #include #include +#include + #include "pci.h" #define MMCONFIG_APER_SIZE (256*1024*1024) +/* Verify the first 16 busses. We assume that systems with more busses + get MCFG right. */ +#define MAX_CHECK_BUS 16 -static DECLARE_BITMAP(fallback_slots, 32); +static DECLARE_BITMAP(fallback_slots, 32*MAX_CHECK_BUS); /* Static virtual mapping of the MMCONFIG aperture */ struct mmcfg_virt { @@ -55,7 +60,8 @@ static char __iomem *get_virt(unsigned int seg, unsigned bus) static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn) { char __iomem *addr; - if (seg == 0 && bus == 0 && test_bit(PCI_SLOT(devfn), fallback_slots)) + if (seg == 0 && bus < MAX_CHECK_BUS && + test_bit(32*bus + PCI_SLOT(devfn), fallback_slots)) return NULL; addr = get_virt(seg, bus); if (!addr) @@ -69,8 +75,10 @@ static int pci_mmcfg_read(unsigned int seg, unsigned int bus, char __iomem *addr; /* Why do we have this when nobody checks it. How about a BUG()!? -AK */ - if (unlikely(!value || (bus > 255) || (devfn > 255) || (reg > 4095))) + if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) { + *value = -1; return -EINVAL; + } addr = pci_dev_base(seg, bus, devfn); if (!addr) @@ -129,21 +137,26 @@ static struct pci_raw_ops pci_mmcfg = { Normally this can be expressed in the MCFG by not listing them and assigning suitable _SEGs, but this isn't implemented in some BIOS. Instead try to discover all devices on bus 0 that are unreachable using MM - and fallback for them. - We only do this for bus 0/seg 0 */ + and fallback for them. */ static __init void unreachable_devices(void) { - int i; - for (i = 0; i < 32; i++) { - u32 val1; - char __iomem *addr; + int i, k; + /* Use the max bus number from ACPI here? */ + for (k = 0; k < MAX_CHECK_BUS; k++) { + for (i = 0; i < 32; i++) { + u32 val1; + char __iomem *addr; - pci_conf1_read(0, 0, PCI_DEVFN(i,0), 0, 4, &val1); - if (val1 == 0xffffffff) - continue; - addr = pci_dev_base(0, 0, PCI_DEVFN(i, 0)); - if (addr == NULL|| readl(addr) != val1) { - set_bit(i, fallback_slots); + pci_conf1_read(0, k, PCI_DEVFN(i,0), 0, 4, &val1); + if (val1 == 0xffffffff) + continue; + addr = pci_dev_base(0, k, PCI_DEVFN(i, 0)); + if (addr == NULL|| readl(addr) != val1) { + set_bit(i + 32*k, fallback_slots); + printk(KERN_NOTICE + "PCI: No mmconfig possible on device %x:%x\n", + k, i); + } } } } @@ -161,6 +174,14 @@ void __init pci_mmcfg_init(void) (pci_mmcfg_config[0].base_address == 0)) return; + if (!e820_all_mapped(pci_mmcfg_config[0].base_address, + pci_mmcfg_config[0].base_address + MMCONFIG_APER_SIZE, + E820_RESERVED)) { + printk(KERN_ERR "PCI: BIOS Bug: MCFG area is not E820-reserved\n"); + printk(KERN_ERR "PCI: Not using MMCONFIG.\n"); + return; + } + /* RED-PEN i386 doesn't do _nocache right now */ pci_mmcfg_virt = kmalloc(sizeof(*pci_mmcfg_virt) * pci_mmcfg_config_num, GFP_KERNEL); if (pci_mmcfg_virt == NULL) { diff --git a/arch/xtensa/kernel/xtensa_ksyms.c b/arch/xtensa/kernel/xtensa_ksyms.c index efae56a51475..a15b6e3e72c8 100644 --- a/arch/xtensa/kernel/xtensa_ksyms.c +++ b/arch/xtensa/kernel/xtensa_ksyms.c @@ -48,7 +48,6 @@ EXPORT_SYMBOL(memchr); EXPORT_SYMBOL(strcat); EXPORT_SYMBOL(strchr); EXPORT_SYMBOL(strlen); -EXPORT_SYMBOL(strpbrk); EXPORT_SYMBOL(strncat); EXPORT_SYMBOL(strnlen); EXPORT_SYMBOL(strrchr); @@ -113,8 +112,6 @@ EXPORT_SYMBOL(__xtensa_copy_user); // FIXME EXPORT_SYMBOL(screen_info); #endif -EXPORT_SYMBOL(get_wchan); - EXPORT_SYMBOL(outsb); EXPORT_SYMBOL(outsw); EXPORT_SYMBOL(outsl); diff --git a/block/Kconfig b/block/Kconfig index 5536839886ff..b6f5f0a79655 100644 --- a/block/Kconfig +++ b/block/Kconfig @@ -27,10 +27,10 @@ config BLK_DEV_IO_TRACE config LSF bool "Support for Large Single Files" depends on X86 || (MIPS && 32BIT) || PPC32 || ARCH_S390_31 || SUPERH || UML - default n help - When CONFIG_LBD is disabled, say Y here if you want to - handle large file(bigger than 2TB), otherwise say N. - When CONFIG_LBD is enabled, Y is set automatically. + Say Y here if you want to be able to handle very large files (bigger + than 2TB), otherwise say N. + + If unsure, say Y. source block/Kconfig.iosched diff --git a/block/elevator.c b/block/elevator.c index 56c2ed06a9e2..0d6be03d929e 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -145,7 +145,7 @@ static int __init elevator_setup(char *str) strcpy(chosen_elevator, "anticipatory"); else strncpy(chosen_elevator, str, sizeof(chosen_elevator) - 1); - return 0; + return 1; } __setup("elevator=", elevator_setup); diff --git a/block/genhd.c b/block/genhd.c index db4c60c802d6..5a8d3bf02f17 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -17,8 +17,6 @@ #include #include -#define MAX_PROBE_HASH 255 /* random */ - static struct subsystem block_subsys; static DEFINE_MUTEX(block_subsys_lock); @@ -31,108 +29,29 @@ static struct blk_major_name { struct blk_major_name *next; int major; char name[16]; -} *major_names[MAX_PROBE_HASH]; +} *major_names[BLKDEV_MAJOR_HASH_SIZE]; /* index in the above - for now: assume no multimajor ranges */ static inline int major_to_index(int major) { - return major % MAX_PROBE_HASH; + return major % BLKDEV_MAJOR_HASH_SIZE; } -struct blkdev_info { - int index; - struct blk_major_name *bd; -}; +#ifdef CONFIG_PROC_FS -/* - * iterate over a list of blkdev_info structures. allows - * the major_names array to be iterated over from outside this file - * must be called with the block_subsys_lock held - */ -void *get_next_blkdev(void *dev) +void blkdev_show(struct seq_file *f, off_t offset) { - struct blkdev_info *info; + struct blk_major_name *dp; - if (dev == NULL) { - info = kmalloc(sizeof(*info), GFP_KERNEL); - if (!info) - goto out; - info->index=0; - info->bd = major_names[info->index]; - if (info->bd) - goto out; - } else { - info = dev; - } - - while (info->index < ARRAY_SIZE(major_names)) { - if (info->bd) - info->bd = info->bd->next; - if (info->bd) - goto out; - /* - * No devices on this chain, move to the next - */ - info->index++; - info->bd = (info->index < ARRAY_SIZE(major_names)) ? - major_names[info->index] : NULL; - if (info->bd) - goto out; - } - -out: - return info; -} - -void *acquire_blkdev_list(void) -{ - mutex_lock(&block_subsys_lock); - return get_next_blkdev(NULL); -} - -void release_blkdev_list(void *dev) -{ - mutex_unlock(&block_subsys_lock); - kfree(dev); -} - - -/* - * Count the number of records in the blkdev_list. - * must be called with the block_subsys_lock held - */ -int count_blkdev_list(void) -{ - struct blk_major_name *n; - int i, count; - - count = 0; - - for (i = 0; i < ARRAY_SIZE(major_names); i++) { - for (n = major_names[i]; n; n = n->next) - count++; + if (offset < BLKDEV_MAJOR_HASH_SIZE) { + mutex_lock(&block_subsys_lock); + for (dp = major_names[offset]; dp; dp = dp->next) + seq_printf(f, "%3d %s\n", dp->major, dp->name); + mutex_unlock(&block_subsys_lock); } - - return count; -} - -/* - * extract the major and name values from a blkdev_info struct - * passed in as a void to *dev. Must be called with - * block_subsys_lock held - */ -int get_blkdev_info(void *dev, int *major, char **name) -{ - struct blkdev_info *info = dev; - - if (info->bd == NULL) - return 1; - - *major = info->bd->major; - *name = info->bd->name; - return 0; } +#endif /* CONFIG_PROC_FS */ int register_blkdev(unsigned int major, const char *name) { diff --git a/block/ll_rw_blk.c b/block/ll_rw_blk.c index 5b26af8597f3..e112d1a5dab6 100644 --- a/block/ll_rw_blk.c +++ b/block/ll_rw_blk.c @@ -1740,7 +1740,7 @@ EXPORT_SYMBOL(blk_run_queue); /** * blk_cleanup_queue: - release a &request_queue_t when it is no longer needed - * @q: the request queue to be released + * @kobj: the kobj belonging of the request queue to be released * * Description: * blk_cleanup_queue is the pair to blk_init_queue() or diff --git a/drivers/Kconfig b/drivers/Kconfig index 9f5c0da57c90..5c91d6afb117 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -64,6 +64,8 @@ source "drivers/usb/Kconfig" source "drivers/mmc/Kconfig" +source "drivers/leds/Kconfig" + source "drivers/infiniband/Kconfig" source "drivers/sn/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 424955274e60..447d8e68887a 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -25,9 +25,6 @@ obj-$(CONFIG_CONNECTOR) += connector/ obj-$(CONFIG_FB_I810) += video/i810/ obj-$(CONFIG_FB_INTEL) += video/intelfb/ -# we also need input/serio early so serio bus is initialized by the time -# serial drivers start registering their serio ports -obj-$(CONFIG_SERIO) += input/serio/ obj-y += serial/ obj-$(CONFIG_PARPORT) += parport/ obj-y += base/ block/ misc/ mfd/ net/ media/ @@ -53,6 +50,7 @@ obj-$(CONFIG_TC) += tc/ obj-$(CONFIG_USB) += usb/ obj-$(CONFIG_PCI) += usb/ obj-$(CONFIG_USB_GADGET) += usb/gadget/ +obj-$(CONFIG_SERIO) += input/serio/ obj-$(CONFIG_GAMEPORT) += input/gameport/ obj-$(CONFIG_INPUT) += input/ obj-$(CONFIG_I2O) += message/ @@ -69,7 +67,9 @@ obj-$(CONFIG_MCA) += mca/ obj-$(CONFIG_EISA) += eisa/ obj-$(CONFIG_CPU_FREQ) += cpufreq/ obj-$(CONFIG_MMC) += mmc/ +obj-$(CONFIG_NEW_LEDS) += leds/ obj-$(CONFIG_INFINIBAND) += infiniband/ +obj-$(CONFIG_IPATH_CORE) += infiniband/ obj-$(CONFIG_SGI_SN) += sn/ obj-y += firmware/ obj-$(CONFIG_CRYPTO) += crypto/ diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 5cb96300eb0f..c24652d31bf9 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -329,7 +329,7 @@ config ACPI_CONTAINER config ACPI_HOTPLUG_MEMORY tristate "Memory Hotplug" depends on ACPI - depends on MEMORY_HOTPLUG + depends on MEMORY_HOTPLUG || X86_64 default n help This driver adds supports for ACPI Memory Hotplug. This driver diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 79b09d76c180..eee0864ba300 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1572,7 +1572,7 @@ static void __exit acpi_ec_exit(void) static int __init acpi_fake_ecdt_setup(char *str) { acpi_fake_ecdt_enabled = 1; - return 0; + return 1; } __setup("acpi_fake_ecdt", acpi_fake_ecdt_setup); @@ -1591,7 +1591,7 @@ static int __init acpi_ec_set_intr_mode(char *str) acpi_ec_driver.ops.add = acpi_ec_poll_add; } printk(KERN_INFO PREFIX "EC %s mode.\n", intr ? "interrupt" : "polling"); - return 0; + return 1; } __setup("ec_intr=", acpi_ec_set_intr_mode); diff --git a/drivers/base/node.c b/drivers/base/node.c index 16c513aa4d48..c80c3aeed004 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -106,7 +106,7 @@ static ssize_t node_read_numastat(struct sys_device * dev, char * buf) other_node = 0; for (i = 0; i < MAX_NR_ZONES; i++) { struct zone *z = &pg->node_zones[i]; - for (cpu = 0; cpu < NR_CPUS; cpu++) { + for_each_online_cpu(cpu) { struct per_cpu_pageset *ps = zone_pcp(z,cpu); numa_hit += ps->numa_hit; numa_miss += ps->numa_miss; diff --git a/drivers/block/amiflop.c b/drivers/block/amiflop.c index b6e290956214..2a8af685926f 100644 --- a/drivers/block/amiflop.c +++ b/drivers/block/amiflop.c @@ -1850,6 +1850,7 @@ static int __init amiga_floppy_setup (char *str) return 0; printk (KERN_INFO "amiflop: Setting default df0 to %x\n", n); fd_def_df0 = n; + return 1; } __setup("floppy=", amiga_floppy_setup); diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index 9888bc151755..473a13b22b29 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -65,7 +65,7 @@ MODULE_LICENSE("GPL"); typedef struct bluecard_info_t { - dev_link_t link; + struct pcmcia_device *p_dev; dev_node_t node; struct hci_dev *hdev; @@ -85,8 +85,8 @@ typedef struct bluecard_info_t { } bluecard_info_t; -static void bluecard_config(dev_link_t *link); -static void bluecard_release(dev_link_t *link); +static int bluecard_config(struct pcmcia_device *link); +static void bluecard_release(struct pcmcia_device *link); static void bluecard_detach(struct pcmcia_device *p_dev); @@ -162,7 +162,7 @@ static void bluecard_detach(struct pcmcia_device *p_dev); static void bluecard_activity_led_timeout(u_long arg) { bluecard_info_t *info = (bluecard_info_t *)arg; - unsigned int iobase = info->link.io.BasePort1; + unsigned int iobase = info->p_dev->io.BasePort1; if (!test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) return; @@ -179,7 +179,7 @@ static void bluecard_activity_led_timeout(u_long arg) static void bluecard_enable_activity_led(bluecard_info_t *info) { - unsigned int iobase = info->link.io.BasePort1; + unsigned int iobase = info->p_dev->io.BasePort1; if (!test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) return; @@ -235,7 +235,7 @@ static void bluecard_write_wakeup(bluecard_info_t *info) } do { - register unsigned int iobase = info->link.io.BasePort1; + register unsigned int iobase = info->p_dev->io.BasePort1; register unsigned int offset; register unsigned char command; register unsigned long ready_bit; @@ -244,7 +244,7 @@ static void bluecard_write_wakeup(bluecard_info_t *info) clear_bit(XMIT_WAKEUP, &(info->tx_state)); - if (!(info->link.state & DEV_PRESENT)) + if (!pcmcia_dev_present(info->p_dev)) return; if (test_bit(XMIT_BUFFER_NUMBER, &(info->tx_state))) { @@ -382,7 +382,7 @@ static void bluecard_receive(bluecard_info_t *info, unsigned int offset) return; } - iobase = info->link.io.BasePort1; + iobase = info->p_dev->io.BasePort1; if (test_bit(XMIT_SENDING_READY, &(info->tx_state))) bluecard_enable_activity_led(info); @@ -512,7 +512,7 @@ static irqreturn_t bluecard_interrupt(int irq, void *dev_inst, struct pt_regs *r if (!test_bit(CARD_READY, &(info->hw_state))) return IRQ_HANDLED; - iobase = info->link.io.BasePort1; + iobase = info->p_dev->io.BasePort1; spin_lock(&(info->lock)); @@ -626,7 +626,7 @@ static int bluecard_hci_flush(struct hci_dev *hdev) static int bluecard_hci_open(struct hci_dev *hdev) { bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data); - unsigned int iobase = info->link.io.BasePort1; + unsigned int iobase = info->p_dev->io.BasePort1; if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE); @@ -646,7 +646,7 @@ static int bluecard_hci_open(struct hci_dev *hdev) static int bluecard_hci_close(struct hci_dev *hdev) { bluecard_info_t *info = (bluecard_info_t *)(hdev->driver_data); - unsigned int iobase = info->link.io.BasePort1; + unsigned int iobase = info->p_dev->io.BasePort1; if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags))) return 0; @@ -713,7 +713,7 @@ static int bluecard_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned l static int bluecard_open(bluecard_info_t *info) { - unsigned int iobase = info->link.io.BasePort1; + unsigned int iobase = info->p_dev->io.BasePort1; struct hci_dev *hdev; unsigned char id; @@ -831,7 +831,7 @@ static int bluecard_open(bluecard_info_t *info) static int bluecard_close(bluecard_info_t *info) { - unsigned int iobase = info->link.io.BasePort1; + unsigned int iobase = info->p_dev->io.BasePort1; struct hci_dev *hdev = info->hdev; if (!hdev) @@ -856,17 +856,16 @@ static int bluecard_close(bluecard_info_t *info) return 0; } -static int bluecard_attach(struct pcmcia_device *p_dev) +static int bluecard_probe(struct pcmcia_device *link) { bluecard_info_t *info; - dev_link_t *link; /* Create new info device */ info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; - link = &info->link; + info->p_dev = link; link->priv = info; link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; @@ -878,32 +877,22 @@ static int bluecard_attach(struct pcmcia_device *p_dev) link->irq.Instance = info; link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - bluecard_config(link); - - return 0; + return bluecard_config(link); } -static void bluecard_detach(struct pcmcia_device *p_dev) +static void bluecard_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); bluecard_info_t *info = link->priv; - if (link->state & DEV_CONFIG) - bluecard_release(link); - + bluecard_release(link); kfree(info); } -static int first_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse) +static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { int i; @@ -918,14 +907,12 @@ static int first_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse return pcmcia_parse_tuple(handle, tuple, parse); } -static void bluecard_config(dev_link_t *link) +static int bluecard_config(struct pcmcia_device *link) { - client_handle_t handle = link->handle; bluecard_info_t *info = link->priv; tuple_t tuple; u_short buf[256]; cisparse_t parse; - config_info_t config; int i, n, last_ret, last_fn; tuple.TupleData = (cisdata_t *)buf; @@ -935,7 +922,7 @@ static void bluecard_config(dev_link_t *link) /* Get configuration register information */ tuple.DesiredTuple = CISTPL_CONFIG; - last_ret = first_tuple(handle, &tuple, &parse); + last_ret = first_tuple(link, &tuple, &parse); if (last_ret != CS_SUCCESS) { last_fn = ParseTuple; goto cs_failed; @@ -943,36 +930,31 @@ static void bluecard_config(dev_link_t *link) link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; - /* Configure card */ - link->state |= DEV_CONFIG; - i = pcmcia_get_configuration_info(handle, &config); - link->conf.Vcc = config.Vcc; - link->conf.ConfigIndex = 0x20; link->io.NumPorts1 = 64; link->io.IOAddrLines = 6; for (n = 0; n < 0x400; n += 0x40) { link->io.BasePort1 = n ^ 0x300; - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) break; } if (i != CS_SUCCESS) { - cs_error(link->handle, RequestIO, i); + cs_error(link, RequestIO, i); goto failed; } - i = pcmcia_request_irq(link->handle, &link->irq); + i = pcmcia_request_irq(link, &link->irq); if (i != CS_SUCCESS) { - cs_error(link->handle, RequestIRQ, i); + cs_error(link, RequestIRQ, i); link->irq.AssignedIRQ = 0; } - i = pcmcia_request_configuration(link->handle, &link->conf); + i = pcmcia_request_configuration(link, &link->conf); if (i != CS_SUCCESS) { - cs_error(link->handle, RequestConfiguration, i); + cs_error(link, RequestConfiguration, i); goto failed; } @@ -980,57 +962,28 @@ static void bluecard_config(dev_link_t *link) goto failed; strcpy(info->node.dev_name, info->hdev->name); - link->dev = &info->node; - link->state &= ~DEV_CONFIG_PENDING; + link->dev_node = &info->node; - return; + return 0; cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); failed: bluecard_release(link); + return -ENODEV; } -static void bluecard_release(dev_link_t *link) +static void bluecard_release(struct pcmcia_device *link) { bluecard_info_t *info = link->priv; - if (link->state & DEV_PRESENT) - bluecard_close(info); + bluecard_close(info); del_timer(&(info->timer)); - link->dev = NULL; - - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - - link->state &= ~DEV_CONFIG; -} - -static int bluecard_suspend(struct pcmcia_device *dev) -{ - dev_link_t *link = dev_to_instance(dev); - - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) - pcmcia_release_configuration(link->handle); - - return 0; -} - -static int bluecard_resume(struct pcmcia_device *dev) -{ - dev_link_t *link = dev_to_instance(dev); - - link->state &= ~DEV_SUSPEND; - if (DEV_OK(link)) - pcmcia_request_configuration(link->handle, &link->conf); - - return 0; + pcmcia_disable_device(link); } static struct pcmcia_device_id bluecard_ids[] = { @@ -1046,11 +999,9 @@ static struct pcmcia_driver bluecard_driver = { .drv = { .name = "bluecard_cs", }, - .probe = bluecard_attach, + .probe = bluecard_probe, .remove = bluecard_detach, .id_table = bluecard_ids, - .suspend = bluecard_suspend, - .resume = bluecard_resume, }; static int __init init_bluecard_cs(void) diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index 7e21b1ff27c4..b94ac2f9f7ba 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -72,7 +72,7 @@ MODULE_LICENSE("GPL"); typedef struct bt3c_info_t { - dev_link_t link; + struct pcmcia_device *p_dev; dev_node_t node; struct hci_dev *hdev; @@ -88,8 +88,8 @@ typedef struct bt3c_info_t { } bt3c_info_t; -static void bt3c_config(dev_link_t *link); -static void bt3c_release(dev_link_t *link); +static int bt3c_config(struct pcmcia_device *link); +static void bt3c_release(struct pcmcia_device *link); static void bt3c_detach(struct pcmcia_device *p_dev); @@ -191,11 +191,11 @@ static void bt3c_write_wakeup(bt3c_info_t *info) return; do { - register unsigned int iobase = info->link.io.BasePort1; + register unsigned int iobase = info->p_dev->io.BasePort1; register struct sk_buff *skb; register int len; - if (!(info->link.state & DEV_PRESENT)) + if (!pcmcia_dev_present(info->p_dev)) break; @@ -229,7 +229,7 @@ static void bt3c_receive(bt3c_info_t *info) return; } - iobase = info->link.io.BasePort1; + iobase = info->p_dev->io.BasePort1; avail = bt3c_read(iobase, 0x7006); //printk("bt3c_cs: receiving %d bytes\n", avail); @@ -350,7 +350,7 @@ static irqreturn_t bt3c_interrupt(int irq, void *dev_inst, struct pt_regs *regs) return IRQ_NONE; } - iobase = info->link.io.BasePort1; + iobase = info->p_dev->io.BasePort1; spin_lock(&(info->lock)); @@ -481,7 +481,7 @@ static int bt3c_load_firmware(bt3c_info_t *info, unsigned char *firmware, int co unsigned int iobase, size, addr, fcs, tmp; int i, err = 0; - iobase = info->link.io.BasePort1; + iobase = info->p_dev->io.BasePort1; /* Reset */ bt3c_io_write(iobase, 0x8040, 0x0404); @@ -562,7 +562,6 @@ static int bt3c_open(bt3c_info_t *info) { const struct firmware *firmware; struct hci_dev *hdev; - client_handle_t handle; int err; spin_lock_init(&(info->lock)); @@ -594,10 +593,8 @@ static int bt3c_open(bt3c_info_t *info) hdev->owner = THIS_MODULE; - handle = info->link.handle; - /* Load firmware */ - err = request_firmware(&firmware, "BT3CPCC.bin", &handle_to_dev(handle)); + err = request_firmware(&firmware, "BT3CPCC.bin", &info->p_dev->dev); if (err < 0) { BT_ERR("Firmware request failed"); goto error; @@ -648,17 +645,16 @@ static int bt3c_close(bt3c_info_t *info) return 0; } -static int bt3c_attach(struct pcmcia_device *p_dev) +static int bt3c_probe(struct pcmcia_device *link) { bt3c_info_t *info; - dev_link_t *link; /* Create new info device */ info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; - link = &info->link; + info->p_dev = link; link->priv = info; link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; @@ -670,31 +666,21 @@ static int bt3c_attach(struct pcmcia_device *p_dev) link->irq.Instance = info; link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - bt3c_config(link); - - return 0; + return bt3c_config(link); } -static void bt3c_detach(struct pcmcia_device *p_dev) +static void bt3c_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); bt3c_info_t *info = link->priv; - if (link->state & DEV_CONFIG) - bt3c_release(link); - + bt3c_release(link); kfree(info); } -static int get_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse) +static int get_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { int i; @@ -705,30 +691,28 @@ static int get_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse) return pcmcia_parse_tuple(handle, tuple, parse); } -static int first_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse) +static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { if (pcmcia_get_first_tuple(handle, tuple) != CS_SUCCESS) return CS_NO_MORE_ITEMS; return get_tuple(handle, tuple, parse); } -static int next_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse) +static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { if (pcmcia_get_next_tuple(handle, tuple) != CS_SUCCESS) return CS_NO_MORE_ITEMS; return get_tuple(handle, tuple, parse); } -static void bt3c_config(dev_link_t *link) +static int bt3c_config(struct pcmcia_device *link) { static kio_addr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; - client_handle_t handle = link->handle; bt3c_info_t *info = link->priv; tuple_t tuple; u_short buf[256]; cisparse_t parse; cistpl_cftable_entry_t *cf = &parse.cftable_entry; - config_info_t config; int i, j, try, last_ret, last_fn; tuple.TupleData = (cisdata_t *)buf; @@ -738,7 +722,7 @@ static void bt3c_config(dev_link_t *link) /* Get configuration register information */ tuple.DesiredTuple = CISTPL_CONFIG; - last_ret = first_tuple(handle, &tuple, &parse); + last_ret = first_tuple(link, &tuple, &parse); if (last_ret != CS_SUCCESS) { last_fn = ParseTuple; goto cs_failed; @@ -746,11 +730,6 @@ static void bt3c_config(dev_link_t *link) link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; - /* Configure card */ - link->state |= DEV_CONFIG; - i = pcmcia_get_configuration_info(handle, &config); - link->conf.Vcc = config.Vcc; - /* First pass: look for a config entry that looks normal. */ tuple.TupleData = (cisdata_t *)buf; tuple.TupleOffset = 0; @@ -759,59 +738,59 @@ static void bt3c_config(dev_link_t *link) tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; /* Two tries: without IO aliases, then with aliases */ for (try = 0; try < 2; try++) { - i = first_tuple(handle, &tuple, &parse); + i = first_tuple(link, &tuple, &parse); while (i != CS_NO_MORE_ITEMS) { if (i != CS_SUCCESS) goto next_entry; if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM)) - link->conf.Vpp1 = link->conf.Vpp2 = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000; + link->conf.Vpp = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000; if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && (cf->io.win[0].base != 0)) { link->conf.ConfigIndex = cf->index; link->io.BasePort1 = cf->io.win[0].base; link->io.IOAddrLines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK; - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) goto found_port; } next_entry: - i = next_tuple(handle, &tuple, &parse); + i = next_tuple(link, &tuple, &parse); } } /* Second pass: try to find an entry that isn't picky about its base address, then try to grab any standard serial port address, and finally try to get any free port. */ - i = first_tuple(handle, &tuple, &parse); + i = first_tuple(link, &tuple, &parse); while (i != CS_NO_MORE_ITEMS) { if ((i == CS_SUCCESS) && (cf->io.nwin > 0) && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) { link->conf.ConfigIndex = cf->index; for (j = 0; j < 5; j++) { link->io.BasePort1 = base[j]; link->io.IOAddrLines = base[j] ? 16 : 3; - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) goto found_port; } } - i = next_tuple(handle, &tuple, &parse); + i = next_tuple(link, &tuple, &parse); } found_port: if (i != CS_SUCCESS) { BT_ERR("No usable port range found"); - cs_error(link->handle, RequestIO, i); + cs_error(link, RequestIO, i); goto failed; } - i = pcmcia_request_irq(link->handle, &link->irq); + i = pcmcia_request_irq(link, &link->irq); if (i != CS_SUCCESS) { - cs_error(link->handle, RequestIRQ, i); + cs_error(link, RequestIRQ, i); link->irq.AssignedIRQ = 0; } - i = pcmcia_request_configuration(link->handle, &link->conf); + i = pcmcia_request_configuration(link, &link->conf); if (i != CS_SUCCESS) { - cs_error(link->handle, RequestConfiguration, i); + cs_error(link, RequestConfiguration, i); goto failed; } @@ -819,55 +798,26 @@ found_port: goto failed; strcpy(info->node.dev_name, info->hdev->name); - link->dev = &info->node; - link->state &= ~DEV_CONFIG_PENDING; + link->dev_node = &info->node; - return; + return 0; cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); failed: bt3c_release(link); + return -ENODEV; } -static void bt3c_release(dev_link_t *link) +static void bt3c_release(struct pcmcia_device *link) { bt3c_info_t *info = link->priv; - if (link->state & DEV_PRESENT) - bt3c_close(info); + bt3c_close(info); - link->dev = NULL; - - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - - link->state &= ~DEV_CONFIG; -} - -static int bt3c_suspend(struct pcmcia_device *dev) -{ - dev_link_t *link = dev_to_instance(dev); - - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) - pcmcia_release_configuration(link->handle); - - return 0; -} - -static int bt3c_resume(struct pcmcia_device *dev) -{ - dev_link_t *link = dev_to_instance(dev); - - link->state &= ~DEV_SUSPEND; - if (DEV_OK(link)) - pcmcia_request_configuration(link->handle, &link->conf); - - return 0; + pcmcia_disable_device(link); } @@ -882,11 +832,9 @@ static struct pcmcia_driver bt3c_driver = { .drv = { .name = "bt3c_cs", }, - .probe = bt3c_attach, + .probe = bt3c_probe, .remove = bt3c_detach, .id_table = bt3c_ids, - .suspend = bt3c_suspend, - .resume = bt3c_resume, }; static int __init init_bt3c_cs(void) diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index 7b4bff4cfa2d..9ce4c93467e5 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -68,7 +68,7 @@ MODULE_LICENSE("GPL"); typedef struct btuart_info_t { - dev_link_t link; + struct pcmcia_device *p_dev; dev_node_t node; struct hci_dev *hdev; @@ -84,8 +84,8 @@ typedef struct btuart_info_t { } btuart_info_t; -static void btuart_config(dev_link_t *link); -static void btuart_release(dev_link_t *link); +static int btuart_config(struct pcmcia_device *link); +static void btuart_release(struct pcmcia_device *link); static void btuart_detach(struct pcmcia_device *p_dev); @@ -146,13 +146,13 @@ static void btuart_write_wakeup(btuart_info_t *info) } do { - register unsigned int iobase = info->link.io.BasePort1; + register unsigned int iobase = info->p_dev->io.BasePort1; register struct sk_buff *skb; register int len; clear_bit(XMIT_WAKEUP, &(info->tx_state)); - if (!(info->link.state & DEV_PRESENT)) + if (!pcmcia_dev_present(info->p_dev)) return; if (!(skb = skb_dequeue(&(info->txq)))) @@ -187,7 +187,7 @@ static void btuart_receive(btuart_info_t *info) return; } - iobase = info->link.io.BasePort1; + iobase = info->p_dev->io.BasePort1; do { info->hdev->stat.byte_rx++; @@ -301,7 +301,7 @@ static irqreturn_t btuart_interrupt(int irq, void *dev_inst, struct pt_regs *reg return IRQ_NONE; } - iobase = info->link.io.BasePort1; + iobase = info->p_dev->io.BasePort1; spin_lock(&(info->lock)); @@ -357,7 +357,7 @@ static void btuart_change_speed(btuart_info_t *info, unsigned int speed) return; } - iobase = info->link.io.BasePort1; + iobase = info->p_dev->io.BasePort1; spin_lock_irqsave(&(info->lock), flags); @@ -481,7 +481,7 @@ static int btuart_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned lon static int btuart_open(btuart_info_t *info) { unsigned long flags; - unsigned int iobase = info->link.io.BasePort1; + unsigned int iobase = info->p_dev->io.BasePort1; struct hci_dev *hdev; spin_lock_init(&(info->lock)); @@ -550,7 +550,7 @@ static int btuart_open(btuart_info_t *info) static int btuart_close(btuart_info_t *info) { unsigned long flags; - unsigned int iobase = info->link.io.BasePort1; + unsigned int iobase = info->p_dev->io.BasePort1; struct hci_dev *hdev = info->hdev; if (!hdev) @@ -576,17 +576,16 @@ static int btuart_close(btuart_info_t *info) return 0; } -static int btuart_attach(struct pcmcia_device *p_dev) +static int btuart_probe(struct pcmcia_device *link) { btuart_info_t *info; - dev_link_t *link; /* Create new info device */ info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; - link = &info->link; + info->p_dev = link; link->priv = info; link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; @@ -598,31 +597,21 @@ static int btuart_attach(struct pcmcia_device *p_dev) link->irq.Instance = info; link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - btuart_config(link); - - return 0; + return btuart_config(link); } -static void btuart_detach(struct pcmcia_device *p_dev) +static void btuart_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); btuart_info_t *info = link->priv; - if (link->state & DEV_CONFIG) - btuart_release(link); - + btuart_release(link); kfree(info); } -static int get_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse) +static int get_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { int i; @@ -633,30 +622,28 @@ static int get_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse) return pcmcia_parse_tuple(handle, tuple, parse); } -static int first_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse) +static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { if (pcmcia_get_first_tuple(handle, tuple) != CS_SUCCESS) return CS_NO_MORE_ITEMS; return get_tuple(handle, tuple, parse); } -static int next_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse) +static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { if (pcmcia_get_next_tuple(handle, tuple) != CS_SUCCESS) return CS_NO_MORE_ITEMS; return get_tuple(handle, tuple, parse); } -static void btuart_config(dev_link_t *link) +static int btuart_config(struct pcmcia_device *link) { static kio_addr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; - client_handle_t handle = link->handle; btuart_info_t *info = link->priv; tuple_t tuple; u_short buf[256]; cisparse_t parse; cistpl_cftable_entry_t *cf = &parse.cftable_entry; - config_info_t config; int i, j, try, last_ret, last_fn; tuple.TupleData = (cisdata_t *)buf; @@ -666,7 +653,7 @@ static void btuart_config(dev_link_t *link) /* Get configuration register information */ tuple.DesiredTuple = CISTPL_CONFIG; - last_ret = first_tuple(handle, &tuple, &parse); + last_ret = first_tuple(link, &tuple, &parse); if (last_ret != CS_SUCCESS) { last_fn = ParseTuple; goto cs_failed; @@ -674,11 +661,6 @@ static void btuart_config(dev_link_t *link) link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; - /* Configure card */ - link->state |= DEV_CONFIG; - i = pcmcia_get_configuration_info(handle, &config); - link->conf.Vcc = config.Vcc; - /* First pass: look for a config entry that looks normal. */ tuple.TupleData = (cisdata_t *) buf; tuple.TupleOffset = 0; @@ -687,29 +669,29 @@ static void btuart_config(dev_link_t *link) tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; /* Two tries: without IO aliases, then with aliases */ for (try = 0; try < 2; try++) { - i = first_tuple(handle, &tuple, &parse); + i = first_tuple(link, &tuple, &parse); while (i != CS_NO_MORE_ITEMS) { if (i != CS_SUCCESS) goto next_entry; if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM)) - link->conf.Vpp1 = link->conf.Vpp2 = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000; + link->conf.Vpp = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000; if ((cf->io.nwin > 0) && (cf->io.win[0].len == 8) && (cf->io.win[0].base != 0)) { link->conf.ConfigIndex = cf->index; link->io.BasePort1 = cf->io.win[0].base; link->io.IOAddrLines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK; - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) goto found_port; } next_entry: - i = next_tuple(handle, &tuple, &parse); + i = next_tuple(link, &tuple, &parse); } } /* Second pass: try to find an entry that isn't picky about its base address, then try to grab any standard serial port address, and finally try to get any free port. */ - i = first_tuple(handle, &tuple, &parse); + i = first_tuple(link, &tuple, &parse); while (i != CS_NO_MORE_ITEMS) { if ((i == CS_SUCCESS) && (cf->io.nwin > 0) && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) { @@ -717,30 +699,30 @@ next_entry: for (j = 0; j < 5; j++) { link->io.BasePort1 = base[j]; link->io.IOAddrLines = base[j] ? 16 : 3; - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) goto found_port; } } - i = next_tuple(handle, &tuple, &parse); + i = next_tuple(link, &tuple, &parse); } found_port: if (i != CS_SUCCESS) { BT_ERR("No usable port range found"); - cs_error(link->handle, RequestIO, i); + cs_error(link, RequestIO, i); goto failed; } - i = pcmcia_request_irq(link->handle, &link->irq); + i = pcmcia_request_irq(link, &link->irq); if (i != CS_SUCCESS) { - cs_error(link->handle, RequestIRQ, i); + cs_error(link, RequestIRQ, i); link->irq.AssignedIRQ = 0; } - i = pcmcia_request_configuration(link->handle, &link->conf); + i = pcmcia_request_configuration(link, &link->conf); if (i != CS_SUCCESS) { - cs_error(link->handle, RequestConfiguration, i); + cs_error(link, RequestConfiguration, i); goto failed; } @@ -748,58 +730,28 @@ found_port: goto failed; strcpy(info->node.dev_name, info->hdev->name); - link->dev = &info->node; - link->state &= ~DEV_CONFIG_PENDING; + link->dev_node = &info->node; - return; + return 0; cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); failed: btuart_release(link); + return -ENODEV; } -static void btuart_release(dev_link_t *link) +static void btuart_release(struct pcmcia_device *link) { btuart_info_t *info = link->priv; - if (link->state & DEV_PRESENT) - btuart_close(info); + btuart_close(info); - link->dev = NULL; - - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - - link->state &= ~DEV_CONFIG; + pcmcia_disable_device(link); } -static int btuart_suspend(struct pcmcia_device *dev) -{ - dev_link_t *link = dev_to_instance(dev); - - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) - pcmcia_release_configuration(link->handle); - - return 0; -} - -static int btuart_resume(struct pcmcia_device *dev) -{ - dev_link_t *link = dev_to_instance(dev); - - link->state &= ~DEV_SUSPEND; - if (DEV_OK(link)) - pcmcia_request_configuration(link->handle, &link->conf); - - return 0; -} - - static struct pcmcia_device_id btuart_ids[] = { /* don't use this driver. Use serial_cs + hci_uart instead */ PCMCIA_DEVICE_NULL @@ -811,11 +763,9 @@ static struct pcmcia_driver btuart_driver = { .drv = { .name = "btuart_cs", }, - .probe = btuart_attach, + .probe = btuart_probe, .remove = btuart_detach, .id_table = btuart_ids, - .suspend = btuart_suspend, - .resume = btuart_resume, }; static int __init init_btuart_cs(void) diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index 0449bc45ae5e..a71a240611e0 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -68,7 +68,7 @@ MODULE_LICENSE("GPL"); typedef struct dtl1_info_t { - dev_link_t link; + struct pcmcia_device *p_dev; dev_node_t node; struct hci_dev *hdev; @@ -87,8 +87,8 @@ typedef struct dtl1_info_t { } dtl1_info_t; -static void dtl1_config(dev_link_t *link); -static void dtl1_release(dev_link_t *link); +static int dtl1_config(struct pcmcia_device *link); +static void dtl1_release(struct pcmcia_device *link); static void dtl1_detach(struct pcmcia_device *p_dev); @@ -153,13 +153,13 @@ static void dtl1_write_wakeup(dtl1_info_t *info) } do { - register unsigned int iobase = info->link.io.BasePort1; + register unsigned int iobase = info->p_dev->io.BasePort1; register struct sk_buff *skb; register int len; clear_bit(XMIT_WAKEUP, &(info->tx_state)); - if (!(info->link.state & DEV_PRESENT)) + if (!pcmcia_dev_present(info->p_dev)) return; if (!(skb = skb_dequeue(&(info->txq)))) @@ -218,7 +218,7 @@ static void dtl1_receive(dtl1_info_t *info) return; } - iobase = info->link.io.BasePort1; + iobase = info->p_dev->io.BasePort1; do { info->hdev->stat.byte_rx++; @@ -305,7 +305,7 @@ static irqreturn_t dtl1_interrupt(int irq, void *dev_inst, struct pt_regs *regs) return IRQ_NONE; } - iobase = info->link.io.BasePort1; + iobase = info->p_dev->io.BasePort1; spin_lock(&(info->lock)); @@ -458,7 +458,7 @@ static int dtl1_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long static int dtl1_open(dtl1_info_t *info) { unsigned long flags; - unsigned int iobase = info->link.io.BasePort1; + unsigned int iobase = info->p_dev->io.BasePort1; struct hci_dev *hdev; spin_lock_init(&(info->lock)); @@ -504,7 +504,7 @@ static int dtl1_open(dtl1_info_t *info) outb(UART_LCR_WLEN8, iobase + UART_LCR); /* Reset DLAB */ outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR); - info->ri_latch = inb(info->link.io.BasePort1 + UART_MSR) & UART_MSR_RI; + info->ri_latch = inb(info->p_dev->io.BasePort1 + UART_MSR) & UART_MSR_RI; /* Turn on interrupts */ outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER); @@ -529,7 +529,7 @@ static int dtl1_open(dtl1_info_t *info) static int dtl1_close(dtl1_info_t *info) { unsigned long flags; - unsigned int iobase = info->link.io.BasePort1; + unsigned int iobase = info->p_dev->io.BasePort1; struct hci_dev *hdev = info->hdev; if (!hdev) @@ -555,17 +555,16 @@ static int dtl1_close(dtl1_info_t *info) return 0; } -static int dtl1_attach(struct pcmcia_device *p_dev) +static int dtl1_probe(struct pcmcia_device *link) { dtl1_info_t *info; - dev_link_t *link; /* Create new info device */ info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; - link = &info->link; + info->p_dev = link; link->priv = info; link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; @@ -577,31 +576,22 @@ static int dtl1_attach(struct pcmcia_device *p_dev) link->irq.Instance = info; link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - dtl1_config(link); - - return 0; + return dtl1_config(link); } -static void dtl1_detach(struct pcmcia_device *p_dev) +static void dtl1_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); dtl1_info_t *info = link->priv; - if (link->state & DEV_CONFIG) - dtl1_release(link); + dtl1_release(link); kfree(info); } -static int get_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse) +static int get_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { int i; @@ -612,29 +602,27 @@ static int get_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse) return pcmcia_parse_tuple(handle, tuple, parse); } -static int first_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse) +static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { if (pcmcia_get_first_tuple(handle, tuple) != CS_SUCCESS) return CS_NO_MORE_ITEMS; return get_tuple(handle, tuple, parse); } -static int next_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse) +static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { if (pcmcia_get_next_tuple(handle, tuple) != CS_SUCCESS) return CS_NO_MORE_ITEMS; return get_tuple(handle, tuple, parse); } -static void dtl1_config(dev_link_t *link) +static int dtl1_config(struct pcmcia_device *link) { - client_handle_t handle = link->handle; dtl1_info_t *info = link->priv; tuple_t tuple; u_short buf[256]; cisparse_t parse; cistpl_cftable_entry_t *cf = &parse.cftable_entry; - config_info_t config; int i, last_ret, last_fn; tuple.TupleData = (cisdata_t *)buf; @@ -644,7 +632,7 @@ static void dtl1_config(dev_link_t *link) /* Get configuration register information */ tuple.DesiredTuple = CISTPL_CONFIG; - last_ret = first_tuple(handle, &tuple, &parse); + last_ret = first_tuple(link, &tuple, &parse); if (last_ret != CS_SUCCESS) { last_fn = ParseTuple; goto cs_failed; @@ -652,11 +640,6 @@ static void dtl1_config(dev_link_t *link) link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; - /* Configure card */ - link->state |= DEV_CONFIG; - i = pcmcia_get_configuration_info(handle, &config); - link->conf.Vcc = config.Vcc; - tuple.TupleData = (cisdata_t *)buf; tuple.TupleOffset = 0; tuple.TupleDataMax = 255; @@ -665,34 +648,34 @@ static void dtl1_config(dev_link_t *link) /* Look for a generic full-sized window */ link->io.NumPorts1 = 8; - i = first_tuple(handle, &tuple, &parse); + i = first_tuple(link, &tuple, &parse); while (i != CS_NO_MORE_ITEMS) { if ((i == CS_SUCCESS) && (cf->io.nwin == 1) && (cf->io.win[0].len > 8)) { link->conf.ConfigIndex = cf->index; link->io.BasePort1 = cf->io.win[0].base; link->io.NumPorts1 = cf->io.win[0].len; /*yo */ link->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK; - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) break; } - i = next_tuple(handle, &tuple, &parse); + i = next_tuple(link, &tuple, &parse); } if (i != CS_SUCCESS) { - cs_error(link->handle, RequestIO, i); + cs_error(link, RequestIO, i); goto failed; } - i = pcmcia_request_irq(link->handle, &link->irq); + i = pcmcia_request_irq(link, &link->irq); if (i != CS_SUCCESS) { - cs_error(link->handle, RequestIRQ, i); + cs_error(link, RequestIRQ, i); link->irq.AssignedIRQ = 0; } - i = pcmcia_request_configuration(link->handle, &link->conf); + i = pcmcia_request_configuration(link, &link->conf); if (i != CS_SUCCESS) { - cs_error(link->handle, RequestConfiguration, i); + cs_error(link, RequestConfiguration, i); goto failed; } @@ -700,55 +683,26 @@ static void dtl1_config(dev_link_t *link) goto failed; strcpy(info->node.dev_name, info->hdev->name); - link->dev = &info->node; - link->state &= ~DEV_CONFIG_PENDING; + link->dev_node = &info->node; - return; + return 0; cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); failed: dtl1_release(link); + return -ENODEV; } -static void dtl1_release(dev_link_t *link) +static void dtl1_release(struct pcmcia_device *link) { dtl1_info_t *info = link->priv; - if (link->state & DEV_PRESENT) - dtl1_close(info); + dtl1_close(info); - link->dev = NULL; - - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - - link->state &= ~DEV_CONFIG; -} - -static int dtl1_suspend(struct pcmcia_device *dev) -{ - dev_link_t *link = dev_to_instance(dev); - - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) - pcmcia_release_configuration(link->handle); - - return 0; -} - -static int dtl1_resume(struct pcmcia_device *dev) -{ - dev_link_t *link = dev_to_instance(dev); - - link->state &= ~DEV_SUSPEND; - if (DEV_OK(link)) - pcmcia_request_configuration(link->handle, &link->conf); - - return 0; + pcmcia_disable_device(link); } @@ -765,11 +719,9 @@ static struct pcmcia_driver dtl1_driver = { .drv = { .name = "dtl1_cs", }, - .probe = dtl1_attach, + .probe = dtl1_probe, .remove = dtl1_detach, .id_table = dtl1_ids, - .suspend = dtl1_suspend, - .resume = dtl1_resume, }; static int __init init_dtl1_cs(void) diff --git a/drivers/cdrom/aztcd.c b/drivers/cdrom/aztcd.c index ce4a1ce59d6a..ec004897b634 100644 --- a/drivers/cdrom/aztcd.c +++ b/drivers/cdrom/aztcd.c @@ -1763,7 +1763,7 @@ static int __init aztcd_init(void) release_region(azt_port, 4); } } - if ((azt_port_auto[i] == 0) || (i == 16)) { + if ((i == 16) || (azt_port_auto[i] == 0)) { printk(KERN_INFO "aztcd: no AZTECH CD-ROM drive found\n"); return -EIO; } diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 889cad07774e..402296670d3a 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -805,10 +805,6 @@ config S3C2410_RTC Samsung S3C2410. This can provide periodic interrupt rates from 1Hz to 64Hz for user programs, and wakeup from Alarm. -config RTC_VR41XX - tristate "NEC VR4100 series Real Time Clock Support" - depends on CPU_VR41XX - config COBALT_LCD bool "Support for Cobalt LCD" depends on MIPS_COBALT diff --git a/drivers/char/Makefile b/drivers/char/Makefile index a73cb4956928..f5b01c6d498e 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -67,7 +67,6 @@ obj-$(CONFIG_SGI_DS1286) += ds1286.o obj-$(CONFIG_SGI_IP27_RTC) += ip27-rtc.o obj-$(CONFIG_DS1302) += ds1302.o obj-$(CONFIG_S3C2410_RTC) += s3c2410-rtc.o -obj-$(CONFIG_RTC_VR41XX) += vr41xx_rtc.o ifeq ($(CONFIG_GENERIC_NVRAM),y) obj-$(CONFIG_NVRAM) += generic_nvram.o else diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c index 927a5bbe112c..a370e7a0bad5 100644 --- a/drivers/char/applicom.c +++ b/drivers/char/applicom.c @@ -142,7 +142,7 @@ static int ac_register_board(unsigned long physloc, void __iomem *loc, if (!boardno) boardno = readb(loc + NUMCARD_OWNER_TO_PC); - if (!boardno && boardno > MAX_BOARD) { + if (!boardno || boardno > MAX_BOARD) { printk(KERN_WARNING "Board #%d (at 0x%lx) is out of range (1 <= x <= %d).\n", boardno, physloc, MAX_BOARD); return 0; diff --git a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h index 107df9fdba4e..edc72a6348a7 100644 --- a/drivers/char/drm/drmP.h +++ b/drivers/char/drm/drmP.h @@ -357,6 +357,12 @@ typedef struct drm_freelist { spinlock_t lock; } drm_freelist_t; +typedef struct drm_dma_handle { + dma_addr_t busaddr; + void *vaddr; + size_t size; +} drm_dma_handle_t; + /** * Buffer entry. There is one of this for each buffer size order. */ @@ -366,7 +372,7 @@ typedef struct drm_buf_entry { drm_buf_t *buflist; /**< buffer list */ int seg_count; int page_order; - unsigned long *seglist; + drm_dma_handle_t **seglist; drm_freelist_t freelist; } drm_buf_entry_t; @@ -483,12 +489,6 @@ typedef struct drm_sigdata { drm_hw_lock_t *lock; } drm_sigdata_t; -typedef struct drm_dma_handle { - dma_addr_t busaddr; - void *vaddr; - size_t size; -} drm_dma_handle_t; - /** * Mappings list */ @@ -813,8 +813,6 @@ extern void drm_mem_init(void); extern int drm_mem_info(char *buf, char **start, off_t offset, int request, int *eof, void *data); extern void *drm_realloc(void *oldpt, size_t oldsize, size_t size, int area); -extern unsigned long drm_alloc_pages(int order, int area); -extern void drm_free_pages(unsigned long address, int order, int area); extern void *drm_ioremap(unsigned long offset, unsigned long size, drm_device_t * dev); extern void *drm_ioremap_nocache(unsigned long offset, unsigned long size, diff --git a/drivers/char/drm/drm_bufs.c b/drivers/char/drm/drm_bufs.c index e2637b4d51de..8a9cf12e6183 100644 --- a/drivers/char/drm/drm_bufs.c +++ b/drivers/char/drm/drm_bufs.c @@ -474,8 +474,7 @@ static void drm_cleanup_buf_error(drm_device_t * dev, drm_buf_entry_t * entry) if (entry->seg_count) { for (i = 0; i < entry->seg_count; i++) { if (entry->seglist[i]) { - drm_free_pages(entry->seglist[i], - entry->page_order, DRM_MEM_DMA); + drm_pci_free(dev, entry->seglist[i]); } } drm_free(entry->seglist, @@ -678,7 +677,7 @@ int drm_addbufs_pci(drm_device_t * dev, drm_buf_desc_t * request) int total; int page_order; drm_buf_entry_t *entry; - unsigned long page; + drm_dma_handle_t *dmah; drm_buf_t *buf; int alignment; unsigned long offset; @@ -781,8 +780,10 @@ int drm_addbufs_pci(drm_device_t * dev, drm_buf_desc_t * request) page_count = 0; while (entry->buf_count < count) { - page = drm_alloc_pages(page_order, DRM_MEM_DMA); - if (!page) { + + dmah = drm_pci_alloc(dev, PAGE_SIZE << page_order, 0x1000, 0xfffffffful); + + if (!dmah) { /* Set count correctly so we free the proper amount. */ entry->buf_count = count; entry->seg_count = count; @@ -794,13 +795,13 @@ int drm_addbufs_pci(drm_device_t * dev, drm_buf_desc_t * request) atomic_dec(&dev->buf_alloc); return -ENOMEM; } - entry->seglist[entry->seg_count++] = page; + entry->seglist[entry->seg_count++] = dmah; for (i = 0; i < (1 << page_order); i++) { DRM_DEBUG("page %d @ 0x%08lx\n", dma->page_count + page_count, - page + PAGE_SIZE * i); + (unsigned long)dmah->vaddr + PAGE_SIZE * i); temp_pagelist[dma->page_count + page_count++] - = page + PAGE_SIZE * i; + = (unsigned long)dmah->vaddr + PAGE_SIZE * i; } for (offset = 0; offset + size <= total && entry->buf_count < count; @@ -811,7 +812,8 @@ int drm_addbufs_pci(drm_device_t * dev, drm_buf_desc_t * request) buf->order = order; buf->used = 0; buf->offset = (dma->byte_count + byte_count + offset); - buf->address = (void *)(page + offset); + buf->address = (void *)(dmah->vaddr + offset); + buf->bus_address = dmah->busaddr + offset; buf->next = NULL; buf->waiting = 0; buf->pending = 0; diff --git a/drivers/char/drm/drm_dma.c b/drivers/char/drm/drm_dma.c index 2afab95ca036..892db7096986 100644 --- a/drivers/char/drm/drm_dma.c +++ b/drivers/char/drm/drm_dma.c @@ -85,9 +85,7 @@ void drm_dma_takedown(drm_device_t * dev) dma->bufs[i].seg_count); for (j = 0; j < dma->bufs[i].seg_count; j++) { if (dma->bufs[i].seglist[j]) { - drm_free_pages(dma->bufs[i].seglist[j], - dma->bufs[i].page_order, - DRM_MEM_DMA); + drm_pci_free(dev, dma->bufs[i].seglist[j]); } } drm_free(dma->bufs[i].seglist, diff --git a/drivers/char/drm/drm_memory.c b/drivers/char/drm/drm_memory.c index 8074771e348f..dddf8de66143 100644 --- a/drivers/char/drm/drm_memory.c +++ b/drivers/char/drm/drm_memory.c @@ -79,65 +79,6 @@ void *drm_realloc(void *oldpt, size_t oldsize, size_t size, int area) return pt; } -/** - * Allocate pages. - * - * \param order size order. - * \param area memory area. (Not used.) - * \return page address on success, or zero on failure. - * - * Allocate and reserve free pages. - */ -unsigned long drm_alloc_pages(int order, int area) -{ - unsigned long address; - unsigned long bytes = PAGE_SIZE << order; - unsigned long addr; - unsigned int sz; - - address = __get_free_pages(GFP_KERNEL|__GFP_COMP, order); - if (!address) - return 0; - - /* Zero */ - memset((void *)address, 0, bytes); - - /* Reserve */ - for (addr = address, sz = bytes; - sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) { - SetPageReserved(virt_to_page(addr)); - } - - return address; -} - -/** - * Free pages. - * - * \param address address of the pages to free. - * \param order size order. - * \param area memory area. (Not used.) - * - * Unreserve and free pages allocated by alloc_pages(). - */ -void drm_free_pages(unsigned long address, int order, int area) -{ - unsigned long bytes = PAGE_SIZE << order; - unsigned long addr; - unsigned int sz; - - if (!address) - return; - - /* Unreserve */ - for (addr = address, sz = bytes; - sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) { - ClearPageReserved(virt_to_page(addr)); - } - - free_pages(address, order); -} - #if __OS_HAS_AGP /** Wrapper around agp_allocate_memory() */ DRM_AGP_MEM *drm_alloc_agp(drm_device_t * dev, int pages, u32 type) diff --git a/drivers/char/drm/drm_memory_debug.h b/drivers/char/drm/drm_memory_debug.h index e84605fc54af..7868341817da 100644 --- a/drivers/char/drm/drm_memory_debug.h +++ b/drivers/char/drm/drm_memory_debug.h @@ -206,76 +206,6 @@ void drm_free (void *pt, size_t size, int area) { } } -unsigned long drm_alloc_pages (int order, int area) { - unsigned long address; - unsigned long bytes = PAGE_SIZE << order; - unsigned long addr; - unsigned int sz; - - spin_lock(&drm_mem_lock); - if ((drm_ram_used >> PAGE_SHIFT) - > (DRM_RAM_PERCENT * drm_ram_available) / 100) { - spin_unlock(&drm_mem_lock); - return 0; - } - spin_unlock(&drm_mem_lock); - - address = __get_free_pages(GFP_KERNEL|__GFP_COMP, order); - if (!address) { - spin_lock(&drm_mem_lock); - ++drm_mem_stats[area].fail_count; - spin_unlock(&drm_mem_lock); - return 0; - } - spin_lock(&drm_mem_lock); - ++drm_mem_stats[area].succeed_count; - drm_mem_stats[area].bytes_allocated += bytes; - drm_ram_used += bytes; - spin_unlock(&drm_mem_lock); - - /* Zero outside the lock */ - memset((void *)address, 0, bytes); - - /* Reserve */ - for (addr = address, sz = bytes; - sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) { - SetPageReserved(virt_to_page(addr)); - } - - return address; -} - -void drm_free_pages (unsigned long address, int order, int area) { - unsigned long bytes = PAGE_SIZE << order; - int alloc_count; - int free_count; - unsigned long addr; - unsigned int sz; - - if (!address) { - DRM_MEM_ERROR(area, "Attempt to free address 0\n"); - } else { - /* Unreserve */ - for (addr = address, sz = bytes; - sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) { - ClearPageReserved(virt_to_page(addr)); - } - free_pages(address, order); - } - - spin_lock(&drm_mem_lock); - free_count = ++drm_mem_stats[area].free_count; - alloc_count = drm_mem_stats[area].succeed_count; - drm_mem_stats[area].bytes_freed += bytes; - drm_ram_used -= bytes; - spin_unlock(&drm_mem_lock); - if (free_count > alloc_count) { - DRM_MEM_ERROR(area, - "Excess frees: %d frees, %d allocs\n", - free_count, alloc_count); - } -} - void *drm_ioremap (unsigned long offset, unsigned long size, drm_device_t * dev) { void *pt; diff --git a/drivers/char/drm/drm_pci.c b/drivers/char/drm/drm_pci.c index 1fd7ff164817..b28ca9cea8a2 100644 --- a/drivers/char/drm/drm_pci.c +++ b/drivers/char/drm/drm_pci.c @@ -50,6 +50,10 @@ drm_dma_handle_t *drm_pci_alloc(drm_device_t * dev, size_t size, size_t align, dma_addr_t maxaddr) { drm_dma_handle_t *dmah; +#if 1 + unsigned long addr; + size_t sz; +#endif #ifdef DRM_DEBUG_MEMORY int area = DRM_MEM_DMA; @@ -79,7 +83,7 @@ drm_dma_handle_t *drm_pci_alloc(drm_device_t * dev, size_t size, size_t align, return NULL; dmah->size = size; - dmah->vaddr = pci_alloc_consistent(dev->pdev, size, &dmah->busaddr); + dmah->vaddr = dma_alloc_coherent(&dev->pdev->dev, size, &dmah->busaddr, GFP_KERNEL | __GFP_COMP); #ifdef DRM_DEBUG_MEMORY if (dmah->vaddr == NULL) { @@ -104,18 +108,29 @@ drm_dma_handle_t *drm_pci_alloc(drm_device_t * dev, size_t size, size_t align, memset(dmah->vaddr, 0, size); + /* XXX - Is virt_to_page() legal for consistent mem? */ + /* Reserve */ + for (addr = (unsigned long)dmah->vaddr, sz = size; + sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) { + SetPageReserved(virt_to_page(addr)); + } + return dmah; } EXPORT_SYMBOL(drm_pci_alloc); /** - * \brief Free a PCI consistent memory block with freeing its descriptor. + * \brief Free a PCI consistent memory block without freeing its descriptor. * * This function is for internal use in the Linux-specific DRM core code. */ void __drm_pci_free(drm_device_t * dev, drm_dma_handle_t * dmah) { +#if 1 + unsigned long addr; + size_t sz; +#endif #ifdef DRM_DEBUG_MEMORY int area = DRM_MEM_DMA; int alloc_count; @@ -127,8 +142,14 @@ void __drm_pci_free(drm_device_t * dev, drm_dma_handle_t * dmah) DRM_MEM_ERROR(area, "Attempt to free address 0\n"); #endif } else { - pci_free_consistent(dev->pdev, dmah->size, dmah->vaddr, - dmah->busaddr); + /* XXX - Is virt_to_page() legal for consistent mem? */ + /* Unreserve */ + for (addr = (unsigned long)dmah->vaddr, sz = dmah->size; + sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) { + ClearPageReserved(virt_to_page(addr)); + } + dma_free_coherent(&dev->pdev->dev, dmah->size, dmah->vaddr, + dmah->busaddr); } #ifdef DRM_DEBUG_MEMORY diff --git a/drivers/char/drm/drm_pciids.h b/drivers/char/drm/drm_pciids.h index 2c17e88a8847..b1bb3c7b568d 100644 --- a/drivers/char/drm/drm_pciids.h +++ b/drivers/char/drm/drm_pciids.h @@ -3,49 +3,69 @@ Please contact dri-devel@lists.sf.net to add new cards to this list */ #define radeon_PCI_IDS \ - {0x1002, 0x3150, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350},\ + {0x1002, 0x3150, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY}, \ + {0x1002, 0x3152, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x3154, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x3E50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x3E54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \ {0x1002, 0x4136, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|CHIP_IS_IGP}, \ {0x1002, 0x4137, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|CHIP_IS_IGP}, \ {0x1002, 0x4144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \ {0x1002, 0x4145, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \ {0x1002, 0x4146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \ {0x1002, 0x4147, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \ + {0x1002, 0x4148, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \ + {0x1002, 0x4149, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \ + {0x1002, 0x414A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \ + {0x1002, 0x414B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \ {0x1002, 0x4150, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \ {0x1002, 0x4151, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \ {0x1002, 0x4152, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \ {0x1002, 0x4153, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \ {0x1002, 0x4154, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \ + {0x1002, 0x4155, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \ {0x1002, 0x4156, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \ - {0x1002, 0x4237, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS250|CHIP_IS_IGP}, \ + {0x1002, 0x4237, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|CHIP_IS_IGP}, \ {0x1002, 0x4242, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ {0x1002, 0x4243, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ {0x1002, 0x4336, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|CHIP_IS_IGP|CHIP_IS_MOBILITY}, \ {0x1002, 0x4337, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|CHIP_IS_IGP|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4437, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS250|CHIP_IS_IGP|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4964, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R250}, \ - {0x1002, 0x4965, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R250}, \ - {0x1002, 0x4966, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R250}, \ - {0x1002, 0x4967, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R250}, \ - {0x1002, 0x4A49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420}, \ - {0x1002, 0x4A4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420}, \ + {0x1002, 0x4437, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|CHIP_IS_IGP|CHIP_IS_MOBILITY}, \ + {0x1002, 0x4966, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250}, \ + {0x1002, 0x4967, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250}, \ + {0x1002, 0x4A48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x4A49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x4A4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x4A4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x4A4C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x4A4D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x4A4E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x4A4F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x4A50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x4A54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x4B49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x4B4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x4B4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x4B4C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ {0x1002, 0x4C57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200|CHIP_IS_MOBILITY}, \ {0x1002, 0x4C58, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200|CHIP_IS_MOBILITY}, \ {0x1002, 0x4C59, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|CHIP_IS_MOBILITY}, \ {0x1002, 0x4C5A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4C64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R250|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4C65, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R250|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4C66, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R250|CHIP_IS_MOBILITY}, \ - {0x1002, 0x4C67, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R250|CHIP_IS_MOBILITY}, \ + {0x1002, 0x4C64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|CHIP_IS_MOBILITY}, \ + {0x1002, 0x4C66, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|CHIP_IS_MOBILITY}, \ + {0x1002, 0x4C67, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|CHIP_IS_MOBILITY}, \ {0x1002, 0x4E44, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \ {0x1002, 0x4E45, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \ - {0x1002, 0x4E46, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \ + {0x1002, 0x4E46, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \ {0x1002, 0x4E47, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \ {0x1002, 0x4E48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \ {0x1002, 0x4E49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \ - {0x1002, 0x4E4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \ + {0x1002, 0x4E4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \ {0x1002, 0x4E4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \ {0x1002, 0x4E50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \ {0x1002, 0x4E51, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \ + {0x1002, 0x4E52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \ + {0x1002, 0x4E53, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \ {0x1002, 0x4E54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \ {0x1002, 0x4E56, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \ {0x1002, 0x5144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|CHIP_SINGLE_CRTC}, \ @@ -53,44 +73,66 @@ {0x1002, 0x5146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|CHIP_SINGLE_CRTC}, \ {0x1002, 0x5147, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|CHIP_SINGLE_CRTC}, \ {0x1002, 0x5148, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ - {0x1002, 0x5149, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ - {0x1002, 0x514A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ - {0x1002, 0x514B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ {0x1002, 0x514C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ {0x1002, 0x514D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ - {0x1002, 0x514E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ - {0x1002, 0x514F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ {0x1002, 0x5157, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200}, \ {0x1002, 0x5158, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200}, \ {0x1002, 0x5159, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \ {0x1002, 0x515A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \ {0x1002, 0x515E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \ - {0x1002, 0x5168, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ - {0x1002, 0x5169, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ - {0x1002, 0x516A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ - {0x1002, 0x516B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ - {0x1002, 0x516C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \ - {0x1002, 0x5460, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \ - {0x1002, 0x554F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \ + {0x1002, 0x5460, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY}, \ + {0x1002, 0x5462, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY}, \ + {0x1002, 0x5464, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY}, \ + {0x1002, 0x5548, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5549, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x554A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x554B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x554C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x554D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x554E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x554F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5551, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5552, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5554, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x564A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x564B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x564F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5652, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5653, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ {0x1002, 0x5834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|CHIP_IS_IGP}, \ {0x1002, 0x5835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|CHIP_IS_IGP|CHIP_IS_MOBILITY}, \ - {0x1002, 0x5836, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|CHIP_IS_IGP}, \ - {0x1002, 0x5837, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|CHIP_IS_IGP}, \ {0x1002, 0x5960, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ {0x1002, 0x5961, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ {0x1002, 0x5962, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ - {0x1002, 0x5963, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ {0x1002, 0x5964, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ - {0x1002, 0x5968, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ + {0x1002, 0x5965, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ {0x1002, 0x5969, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \ - {0x1002, 0x596A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ - {0x1002, 0x596B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ + {0x1002, 0x5b60, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5b62, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5b63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5b64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5b65, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \ {0x1002, 0x5c61, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|CHIP_IS_MOBILITY}, \ - {0x1002, 0x5c62, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ {0x1002, 0x5c63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|CHIP_IS_MOBILITY}, \ - {0x1002, 0x5c64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \ - {0x1002, 0x5d4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \ - {0x1002, 0x5e4b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420}, \ + {0x1002, 0x5d48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5d49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5d4a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5d4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5d4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5d4e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5d4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5d50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5d52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5d57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5e48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5e4a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5e4b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5e4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5e4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x5e4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x7834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|CHIP_IS_IGP|CHIP_NEW_MEMMAP}, \ + {0x1002, 0x7835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|CHIP_IS_IGP|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \ {0, 0, 0} #define r128_PCI_IDS \ diff --git a/drivers/char/drm/i915_dma.c b/drivers/char/drm/i915_dma.c index 1ff4c7ca0bff..9f4b8ce4c05e 100644 --- a/drivers/char/drm/i915_dma.c +++ b/drivers/char/drm/i915_dma.c @@ -495,8 +495,6 @@ static int i915_dispatch_batchbuffer(drm_device_t * dev, } } - dev_priv->sarea_priv->last_enqueue = dev_priv->counter++; - i915_emit_breadcrumb(dev); return 0; diff --git a/drivers/char/drm/i915_irq.c b/drivers/char/drm/i915_irq.c index d3879ac9970f..a752afd86ab8 100644 --- a/drivers/char/drm/i915_irq.c +++ b/drivers/char/drm/i915_irq.c @@ -53,6 +53,8 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) I915_WRITE16(I915REG_INT_IDENTITY_R, temp); + dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); + if (temp & USER_INT_FLAG) DRM_WAKEUP(&dev_priv->irq_queue); diff --git a/drivers/char/drm/r300_cmdbuf.c b/drivers/char/drm/r300_cmdbuf.c index c08fa5076f05..b108c7f913b2 100644 --- a/drivers/char/drm/r300_cmdbuf.c +++ b/drivers/char/drm/r300_cmdbuf.c @@ -214,13 +214,13 @@ void r300_init_reg_flags(void) ADD_RANGE(0x4F54, 1); ADD_RANGE(R300_TX_FILTER_0, 16); - ADD_RANGE(R300_TX_UNK1_0, 16); + ADD_RANGE(R300_TX_FILTER1_0, 16); ADD_RANGE(R300_TX_SIZE_0, 16); ADD_RANGE(R300_TX_FORMAT_0, 16); ADD_RANGE(R300_TX_PITCH_0, 16); /* Texture offset is dangerous and needs more checking */ ADD_RANGE_MARK(R300_TX_OFFSET_0, 16, MARK_CHECK_OFFSET); - ADD_RANGE(R300_TX_UNK4_0, 16); + ADD_RANGE(R300_TX_CHROMA_KEY_0, 16); ADD_RANGE(R300_TX_BORDER_COLOR_0, 16); /* Sporadic registers used as primitives are emitted */ @@ -242,8 +242,10 @@ static __inline__ int r300_check_range(unsigned reg, int count) return 0; } - /* we expect offsets passed to the framebuffer to be either within video memory or - within AGP space */ +/* + * we expect offsets passed to the framebuffer to be either within video + * memory or within AGP space + */ static __inline__ int r300_check_offset(drm_radeon_private_t *dev_priv, u32 offset) { @@ -251,11 +253,11 @@ static __inline__ int r300_check_offset(drm_radeon_private_t *dev_priv, but this value is not being kept. This code is correct for now (does the same thing as the code that sets MC_FB_LOCATION) in radeon_cp.c */ - if ((offset >= dev_priv->fb_location) && - (offset < dev_priv->gart_vm_start)) + if (offset >= dev_priv->fb_location && + offset < (dev_priv->fb_location + dev_priv->fb_size)) return 0; - if ((offset >= dev_priv->gart_vm_start) && - (offset < dev_priv->gart_vm_start + dev_priv->gart_size)) + if (offset >= dev_priv->gart_vm_start && + offset < (dev_priv->gart_vm_start + dev_priv->gart_size)) return 0; return 1; } @@ -490,6 +492,7 @@ static __inline__ int r300_emit_3d_load_vbpntr(drm_radeon_private_t *dev_priv, return 0; } + static __inline__ int r300_emit_bitblt_multi(drm_radeon_private_t *dev_priv, drm_radeon_kcmd_buffer_t *cmdbuf) { @@ -701,6 +704,64 @@ static void r300_discard_buffer(drm_device_t * dev, drm_buf_t * buf) buf->used = 0; } +static int r300_scratch(drm_radeon_private_t *dev_priv, + drm_radeon_kcmd_buffer_t *cmdbuf, + drm_r300_cmd_header_t header) +{ + u32 *ref_age_base; + u32 i, buf_idx, h_pending; + RING_LOCALS; + + if (cmdbuf->bufsz < + (sizeof(u64) + header.scratch.n_bufs * sizeof(buf_idx))) { + return DRM_ERR(EINVAL); + } + + if (header.scratch.reg >= 5) { + return DRM_ERR(EINVAL); + } + + dev_priv->scratch_ages[header.scratch.reg]++; + + ref_age_base = *(u32 **)cmdbuf->buf; + + cmdbuf->buf += sizeof(u64); + cmdbuf->bufsz -= sizeof(u64); + + for (i=0; i < header.scratch.n_bufs; i++) { + buf_idx = *(u32 *)cmdbuf->buf; + buf_idx *= 2; /* 8 bytes per buf */ + + if (DRM_COPY_TO_USER(ref_age_base + buf_idx, &dev_priv->scratch_ages[header.scratch.reg], sizeof(u32))) { + return DRM_ERR(EINVAL); + } + + if (DRM_COPY_FROM_USER(&h_pending, ref_age_base + buf_idx + 1, sizeof(u32))) { + return DRM_ERR(EINVAL); + } + + if (h_pending == 0) { + return DRM_ERR(EINVAL); + } + + h_pending--; + + if (DRM_COPY_TO_USER(ref_age_base + buf_idx + 1, &h_pending, sizeof(u32))) { + return DRM_ERR(EINVAL); + } + + cmdbuf->buf += sizeof(buf_idx); + cmdbuf->bufsz -= sizeof(buf_idx); + } + + BEGIN_RING(2); + OUT_RING(CP_PACKET0(RADEON_SCRATCH_REG0 + header.scratch.reg * 4, 0)); + OUT_RING(dev_priv->scratch_ages[header.scratch.reg]); + ADVANCE_RING(); + + return 0; +} + /** * Parses and validates a user-supplied command buffer and emits appropriate * commands on the DMA ring buffer. @@ -838,6 +899,15 @@ int r300_do_cp_cmdbuf(drm_device_t *dev, } break; + case R300_CMD_SCRATCH: + DRM_DEBUG("R300_CMD_SCRATCH\n"); + ret = r300_scratch(dev_priv, cmdbuf, header); + if (ret) { + DRM_ERROR("r300_scratch failed\n"); + goto cleanup; + } + break; + default: DRM_ERROR("bad cmd_type %i at %p\n", header.header.cmd_type, diff --git a/drivers/char/drm/r300_reg.h b/drivers/char/drm/r300_reg.h index d1e19954406b..a881f96c983e 100644 --- a/drivers/char/drm/r300_reg.h +++ b/drivers/char/drm/r300_reg.h @@ -711,8 +711,22 @@ I am fairly certain that they are correct unless stated otherwise in comments. # define R300_TX_MAX_ANISO_16_TO_1 (8 << 21) # define R300_TX_MAX_ANISO_MASK (14 << 21) -#define R300_TX_UNK1_0 0x4440 +#define R300_TX_FILTER1_0 0x4440 +# define R300_CHROMA_KEY_MODE_DISABLE 0 +# define R300_CHROMA_KEY_FORCE 1 +# define R300_CHROMA_KEY_BLEND 2 +# define R300_MC_ROUND_NORMAL (0<<2) +# define R300_MC_ROUND_MPEG4 (1<<2) # define R300_LOD_BIAS_MASK 0x1fff +# define R300_EDGE_ANISO_EDGE_DIAG (0<<13) +# define R300_EDGE_ANISO_EDGE_ONLY (1<<13) +# define R300_MC_COORD_TRUNCATE_DISABLE (0<<14) +# define R300_MC_COORD_TRUNCATE_MPEG (1<<14) +# define R300_TX_TRI_PERF_0_8 (0<<15) +# define R300_TX_TRI_PERF_1_8 (1<<15) +# define R300_TX_TRI_PERF_1_4 (2<<15) +# define R300_TX_TRI_PERF_3_8 (3<<15) +# define R300_ANISO_THRESHOLD_MASK (7<<17) #define R300_TX_SIZE_0 0x4480 # define R300_TX_WIDTHMASK_SHIFT 0 @@ -722,6 +736,8 @@ I am fairly certain that they are correct unless stated otherwise in comments. # define R300_TX_UNK23 (1 << 23) # define R300_TX_SIZE_SHIFT 26 /* largest of width, height */ # define R300_TX_SIZE_MASK (15 << 26) +# define R300_TX_SIZE_PROJECTED (1<<30) +# define R300_TX_SIZE_TXPITCH_EN (1<<31) #define R300_TX_FORMAT_0 0x44C0 /* The interpretation of the format word by Wladimir van der Laan */ /* The X, Y, Z and W refer to the layout of the components. @@ -750,7 +766,8 @@ I am fairly certain that they are correct unless stated otherwise in comments. # define R300_TX_FORMAT_B8G8_B8G8 0x14 /* no swizzle */ # define R300_TX_FORMAT_G8R8_G8B8 0x15 /* no swizzle */ /* 0x16 - some 16 bit green format.. ?? */ -# define R300_TX_FORMAT_UNK25 (1 << 25) /* no swizzle */ +# define R300_TX_FORMAT_UNK25 (1 << 25) /* no swizzle */ +# define R300_TX_FORMAT_CUBIC_MAP (1 << 26) /* gap */ /* Floating point formats */ @@ -800,18 +817,20 @@ I am fairly certain that they are correct unless stated otherwise in comments. # define R300_TX_FORMAT_YUV_MODE 0x00800000 -#define R300_TX_PITCH_0 0x4500 +#define R300_TX_PITCH_0 0x4500 /* obvious missing in gap */ #define R300_TX_OFFSET_0 0x4540 /* BEGIN: Guess from R200 */ # define R300_TXO_ENDIAN_NO_SWAP (0 << 0) # define R300_TXO_ENDIAN_BYTE_SWAP (1 << 0) # define R300_TXO_ENDIAN_WORD_SWAP (2 << 0) # define R300_TXO_ENDIAN_HALFDW_SWAP (3 << 0) +# define R300_TXO_MACRO_TILE (1 << 2) +# define R300_TXO_MICRO_TILE (1 << 3) # define R300_TXO_OFFSET_MASK 0xffffffe0 # define R300_TXO_OFFSET_SHIFT 5 /* END */ -#define R300_TX_UNK4_0 0x4580 -#define R300_TX_BORDER_COLOR_0 0x45C0 //ff00ff00 == { 0, 1.0, 0, 1.0 } +#define R300_TX_CHROMA_KEY_0 0x4580 /* 32 bit chroma key */ +#define R300_TX_BORDER_COLOR_0 0x45C0 //ff00ff00 == { 0, 1.0, 0, 1.0 } /* END */ @@ -868,7 +887,9 @@ I am fairly certain that they are correct unless stated otherwise in comments. # define R300_PFS_NODE_TEX_OFFSET_MASK (31 << 12) # define R300_PFS_NODE_TEX_END_SHIFT 17 # define R300_PFS_NODE_TEX_END_MASK (31 << 17) -# define R300_PFS_NODE_LAST_NODE (1 << 22) +/*# define R300_PFS_NODE_LAST_NODE (1 << 22) */ +# define R300_PFS_NODE_OUTPUT_COLOR (1 << 22) +# define R300_PFS_NODE_OUTPUT_DEPTH (1 << 23) /* TEX // As far as I can tell, texture instructions cannot write into output @@ -887,6 +908,7 @@ I am fairly certain that they are correct unless stated otherwise in comments. */ # define R300_FPITX_OPCODE_SHIFT 15 # define R300_FPITX_OP_TEX 1 +# define R300_FPITX_OP_KIL 2 # define R300_FPITX_OP_TXP 3 # define R300_FPITX_OP_TXB 4 @@ -962,9 +984,11 @@ I am fairly certain that they are correct unless stated otherwise in comments. # define R300_FPI1_SRC2C_CONST (1 << 17) # define R300_FPI1_DSTC_SHIFT 18 # define R300_FPI1_DSTC_MASK (31 << 18) +# define R300_FPI1_DSTC_REG_MASK_SHIFT 23 # define R300_FPI1_DSTC_REG_X (1 << 23) # define R300_FPI1_DSTC_REG_Y (1 << 24) # define R300_FPI1_DSTC_REG_Z (1 << 25) +# define R300_FPI1_DSTC_OUTPUT_MASK_SHIFT 26 # define R300_FPI1_DSTC_OUTPUT_X (1 << 26) # define R300_FPI1_DSTC_OUTPUT_Y (1 << 27) # define R300_FPI1_DSTC_OUTPUT_Z (1 << 28) @@ -983,6 +1007,7 @@ I am fairly certain that they are correct unless stated otherwise in comments. # define R300_FPI3_DSTA_MASK (31 << 18) # define R300_FPI3_DSTA_REG (1 << 23) # define R300_FPI3_DSTA_OUTPUT (1 << 24) +# define R300_FPI3_DSTA_DEPTH (1 << 27) #define R300_PFS_INSTR0_0 0x48C0 # define R300_FPI0_ARGC_SRC0C_XYZ 0 @@ -1036,7 +1061,7 @@ I am fairly certain that they are correct unless stated otherwise in comments. # define R300_FPI0_OUTC_FRC (9 << 23) # define R300_FPI0_OUTC_REPL_ALPHA (10 << 23) # define R300_FPI0_OUTC_SAT (1 << 30) -# define R300_FPI0_UNKNOWN_31 (1 << 31) +# define R300_FPI0_INSERT_NOP (1 << 31) #define R300_PFS_INSTR2_0 0x49C0 # define R300_FPI2_ARGA_SRC0C_X 0 diff --git a/drivers/char/drm/radeon_cp.c b/drivers/char/drm/radeon_cp.c index 9bb8ae0c1c27..7f949c9c9691 100644 --- a/drivers/char/drm/radeon_cp.c +++ b/drivers/char/drm/radeon_cp.c @@ -1118,14 +1118,20 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev, { u32 ring_start, cur_read_ptr; u32 tmp; - - /* Initialize the memory controller */ - RADEON_WRITE(RADEON_MC_FB_LOCATION, - ((dev_priv->gart_vm_start - 1) & 0xffff0000) - | (dev_priv->fb_location >> 16)); + + /* Initialize the memory controller. With new memory map, the fb location + * is not changed, it should have been properly initialized already. Part + * of the problem is that the code below is bogus, assuming the GART is + * always appended to the fb which is not necessarily the case + */ + if (!dev_priv->new_memmap) + RADEON_WRITE(RADEON_MC_FB_LOCATION, + ((dev_priv->gart_vm_start - 1) & 0xffff0000) + | (dev_priv->fb_location >> 16)); #if __OS_HAS_AGP if (dev_priv->flags & CHIP_IS_AGP) { + RADEON_WRITE(RADEON_AGP_BASE, (unsigned int)dev->agp->base); RADEON_WRITE(RADEON_MC_AGP_LOCATION, (((dev_priv->gart_vm_start - 1 + dev_priv->gart_size) & 0xffff0000) | @@ -1153,8 +1159,6 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev, #if __OS_HAS_AGP if (dev_priv->flags & CHIP_IS_AGP) { - /* set RADEON_AGP_BASE here instead of relying on X from user space */ - RADEON_WRITE(RADEON_AGP_BASE, (unsigned int)dev->agp->base); RADEON_WRITE(RADEON_CP_RB_RPTR_ADDR, dev_priv->ring_rptr->offset - dev->agp->base + dev_priv->gart_vm_start); @@ -1174,6 +1178,17 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev, entry->handle + tmp_ofs); } + /* Set ring buffer size */ +#ifdef __BIG_ENDIAN + RADEON_WRITE(RADEON_CP_RB_CNTL, + dev_priv->ring.size_l2qw | RADEON_BUF_SWAP_32BIT); +#else + RADEON_WRITE(RADEON_CP_RB_CNTL, dev_priv->ring.size_l2qw); +#endif + + /* Start with assuming that writeback doesn't work */ + dev_priv->writeback_works = 0; + /* Initialize the scratch register pointer. This will cause * the scratch register values to be written out to memory * whenever they are updated. @@ -1190,7 +1205,38 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev, RADEON_WRITE(RADEON_SCRATCH_UMSK, 0x7); - /* Writeback doesn't seem to work everywhere, test it first */ + /* Turn on bus mastering */ + tmp = RADEON_READ(RADEON_BUS_CNTL) & ~RADEON_BUS_MASTER_DIS; + RADEON_WRITE(RADEON_BUS_CNTL, tmp); + + dev_priv->sarea_priv->last_frame = dev_priv->scratch[0] = 0; + RADEON_WRITE(RADEON_LAST_FRAME_REG, dev_priv->sarea_priv->last_frame); + + dev_priv->sarea_priv->last_dispatch = dev_priv->scratch[1] = 0; + RADEON_WRITE(RADEON_LAST_DISPATCH_REG, + dev_priv->sarea_priv->last_dispatch); + + dev_priv->sarea_priv->last_clear = dev_priv->scratch[2] = 0; + RADEON_WRITE(RADEON_LAST_CLEAR_REG, dev_priv->sarea_priv->last_clear); + + radeon_do_wait_for_idle(dev_priv); + + /* Sync everything up */ + RADEON_WRITE(RADEON_ISYNC_CNTL, + (RADEON_ISYNC_ANY2D_IDLE3D | + RADEON_ISYNC_ANY3D_IDLE2D | + RADEON_ISYNC_WAIT_IDLEGUI | + RADEON_ISYNC_CPSCRATCH_IDLEGUI)); + +} + +static void radeon_test_writeback(drm_radeon_private_t * dev_priv) +{ + u32 tmp; + + /* Writeback doesn't seem to work everywhere, test it here and possibly + * enable it if it appears to work + */ DRM_WRITE32(dev_priv->ring_rptr, RADEON_SCRATCHOFF(1), 0); RADEON_WRITE(RADEON_SCRATCH_REG1, 0xdeadbeef); @@ -1203,46 +1249,15 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev, if (tmp < dev_priv->usec_timeout) { dev_priv->writeback_works = 1; - DRM_DEBUG("writeback test succeeded, tmp=%d\n", tmp); + DRM_INFO("writeback test succeeded in %d usecs\n", tmp); } else { dev_priv->writeback_works = 0; - DRM_DEBUG("writeback test failed\n"); + DRM_INFO("writeback test failed\n"); } if (radeon_no_wb == 1) { dev_priv->writeback_works = 0; - DRM_DEBUG("writeback forced off\n"); + DRM_INFO("writeback forced off\n"); } - - dev_priv->sarea_priv->last_frame = dev_priv->scratch[0] = 0; - RADEON_WRITE(RADEON_LAST_FRAME_REG, dev_priv->sarea_priv->last_frame); - - dev_priv->sarea_priv->last_dispatch = dev_priv->scratch[1] = 0; - RADEON_WRITE(RADEON_LAST_DISPATCH_REG, - dev_priv->sarea_priv->last_dispatch); - - dev_priv->sarea_priv->last_clear = dev_priv->scratch[2] = 0; - RADEON_WRITE(RADEON_LAST_CLEAR_REG, dev_priv->sarea_priv->last_clear); - - /* Set ring buffer size */ -#ifdef __BIG_ENDIAN - RADEON_WRITE(RADEON_CP_RB_CNTL, - dev_priv->ring.size_l2qw | RADEON_BUF_SWAP_32BIT); -#else - RADEON_WRITE(RADEON_CP_RB_CNTL, dev_priv->ring.size_l2qw); -#endif - - radeon_do_wait_for_idle(dev_priv); - - /* Turn on bus mastering */ - tmp = RADEON_READ(RADEON_BUS_CNTL) & ~RADEON_BUS_MASTER_DIS; - RADEON_WRITE(RADEON_BUS_CNTL, tmp); - - /* Sync everything up */ - RADEON_WRITE(RADEON_ISYNC_CNTL, - (RADEON_ISYNC_ANY2D_IDLE3D | - RADEON_ISYNC_ANY3D_IDLE2D | - RADEON_ISYNC_WAIT_IDLEGUI | - RADEON_ISYNC_CPSCRATCH_IDLEGUI)); } /* Enable or disable PCI-E GART on the chip */ @@ -1317,6 +1332,14 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init) DRM_DEBUG("\n"); + /* if we require new memory map but we don't have it fail */ + if ((dev_priv->flags & CHIP_NEW_MEMMAP) && !dev_priv->new_memmap) + { + DRM_ERROR("Cannot initialise DRM on this card\nThis card requires a new X.org DDX\n"); + radeon_do_cleanup_cp(dev); + return DRM_ERR(EINVAL); + } + if (init->is_pci && (dev_priv->flags & CHIP_IS_AGP)) { DRM_DEBUG("Forcing AGP card to PCI mode\n"); @@ -1496,6 +1519,9 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init) dev_priv->fb_location = (RADEON_READ(RADEON_MC_FB_LOCATION) & 0xffff) << 16; + dev_priv->fb_size = + ((RADEON_READ(RADEON_MC_FB_LOCATION) & 0xffff0000u) + 0x10000) + - dev_priv->fb_location; dev_priv->front_pitch_offset = (((dev_priv->front_pitch / 64) << 22) | ((dev_priv->front_offset @@ -1510,8 +1536,46 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init) + dev_priv->fb_location) >> 10)); dev_priv->gart_size = init->gart_size; - dev_priv->gart_vm_start = dev_priv->fb_location - + RADEON_READ(RADEON_CONFIG_APER_SIZE); + + /* New let's set the memory map ... */ + if (dev_priv->new_memmap) { + u32 base = 0; + + DRM_INFO("Setting GART location based on new memory map\n"); + + /* If using AGP, try to locate the AGP aperture at the same + * location in the card and on the bus, though we have to + * align it down. + */ +#if __OS_HAS_AGP + if (dev_priv->flags & CHIP_IS_AGP) { + base = dev->agp->base; + /* Check if valid */ + if ((base + dev_priv->gart_size) > dev_priv->fb_location && + base < (dev_priv->fb_location + dev_priv->fb_size)) { + DRM_INFO("Can't use AGP base @0x%08lx, won't fit\n", + dev->agp->base); + base = 0; + } + } +#endif + /* If not or if AGP is at 0 (Macs), try to put it elsewhere */ + if (base == 0) { + base = dev_priv->fb_location + dev_priv->fb_size; + if (((base + dev_priv->gart_size) & 0xfffffffful) + < base) + base = dev_priv->fb_location + - dev_priv->gart_size; + } + dev_priv->gart_vm_start = base & 0xffc00000u; + if (dev_priv->gart_vm_start != base) + DRM_INFO("GART aligned down from 0x%08x to 0x%08x\n", + base, dev_priv->gart_vm_start); + } else { + DRM_INFO("Setting GART location based on old memory map\n"); + dev_priv->gart_vm_start = dev_priv->fb_location + + RADEON_READ(RADEON_CONFIG_APER_SIZE); + } #if __OS_HAS_AGP if (dev_priv->flags & CHIP_IS_AGP) @@ -1596,6 +1660,7 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init) dev_priv->last_buf = 0; radeon_do_engine_reset(dev); + radeon_test_writeback(dev_priv); return 0; } diff --git a/drivers/char/drm/radeon_drm.h b/drivers/char/drm/radeon_drm.h index 9c177a6b2a4c..c8e279e89c2e 100644 --- a/drivers/char/drm/radeon_drm.h +++ b/drivers/char/drm/radeon_drm.h @@ -222,6 +222,7 @@ typedef union { # define R300_WAIT_3D 0x2 # define R300_WAIT_2D_CLEAN 0x3 # define R300_WAIT_3D_CLEAN 0x4 +#define R300_CMD_SCRATCH 8 typedef union { unsigned int u; @@ -247,6 +248,9 @@ typedef union { struct { unsigned char cmd_type, flags, pad0, pad1; } wait; + struct { + unsigned char cmd_type, reg, n_bufs, flags; + } scratch; } drm_r300_cmd_header_t; #define RADEON_FRONT 0x1 @@ -697,6 +701,7 @@ typedef struct drm_radeon_setparam { #define RADEON_SETPARAM_FB_LOCATION 1 /* determined framebuffer location */ #define RADEON_SETPARAM_SWITCH_TILING 2 /* enable/disable color tiling */ #define RADEON_SETPARAM_PCIGART_LOCATION 3 /* PCI Gart Location */ +#define RADEON_SETPARAM_NEW_MEMMAP 4 /* Use new memory map */ /* 1.14: Clients can allocate/free a surface */ diff --git a/drivers/char/drm/radeon_drv.h b/drivers/char/drm/radeon_drv.h index 1f7d2ab8c4fc..78345cee8f8e 100644 --- a/drivers/char/drm/radeon_drv.h +++ b/drivers/char/drm/radeon_drv.h @@ -38,7 +38,7 @@ #define DRIVER_NAME "radeon" #define DRIVER_DESC "ATI Radeon" -#define DRIVER_DATE "20051229" +#define DRIVER_DATE "20060225" /* Interface history: * @@ -91,9 +91,11 @@ * 1.20- Add support for r300 texrect * 1.21- Add support for card type getparam * 1.22- Add support for texture cache flushes (R300_TX_CNTL) + * 1.23- Add new radeon memory map work from benh + * 1.24- Add general-purpose packet for manipulating scratch registers (r300) */ #define DRIVER_MAJOR 1 -#define DRIVER_MINOR 22 +#define DRIVER_MINOR 24 #define DRIVER_PATCHLEVEL 0 /* @@ -101,20 +103,21 @@ */ enum radeon_family { CHIP_R100, - CHIP_RS100, CHIP_RV100, + CHIP_RS100, CHIP_RV200, - CHIP_R200, CHIP_RS200, - CHIP_R250, - CHIP_RS250, + CHIP_R200, CHIP_RV250, + CHIP_RS300, CHIP_RV280, CHIP_R300, - CHIP_RS300, CHIP_R350, CHIP_RV350, + CHIP_RV380, CHIP_R420, + CHIP_RV410, + CHIP_RS400, CHIP_LAST, }; @@ -136,9 +139,11 @@ enum radeon_chip_flags { CHIP_IS_AGP = 0x00080000UL, CHIP_HAS_HIERZ = 0x00100000UL, CHIP_IS_PCIE = 0x00200000UL, + CHIP_NEW_MEMMAP = 0x00400000UL, }; -#define GET_RING_HEAD(dev_priv) DRM_READ32( (dev_priv)->ring_rptr, 0 ) +#define GET_RING_HEAD(dev_priv) (dev_priv->writeback_works ? \ + DRM_READ32( (dev_priv)->ring_rptr, 0 ) : RADEON_READ(RADEON_CP_RB_RPTR)) #define SET_RING_HEAD(dev_priv,val) DRM_WRITE32( (dev_priv)->ring_rptr, 0, (val) ) typedef struct drm_radeon_freelist { @@ -199,6 +204,8 @@ typedef struct drm_radeon_private { drm_radeon_sarea_t *sarea_priv; u32 fb_location; + u32 fb_size; + int new_memmap; int gart_size; u32 gart_vm_start; @@ -272,6 +279,8 @@ typedef struct drm_radeon_private { unsigned long pcigart_offset; drm_ati_pcigart_info gart_info; + u32 scratch_ages[5]; + /* starting from here on, data is preserved accross an open */ uint32_t flags; /* see radeon_chip_flags */ } drm_radeon_private_t; diff --git a/drivers/char/drm/radeon_state.c b/drivers/char/drm/radeon_state.c index 7bc27516d425..c5b8f774a599 100644 --- a/drivers/char/drm/radeon_state.c +++ b/drivers/char/drm/radeon_state.c @@ -45,22 +45,53 @@ static __inline__ int radeon_check_and_fixup_offset(drm_radeon_private_t * u32 off = *offset; struct drm_radeon_driver_file_fields *radeon_priv; - if (off >= dev_priv->fb_location && - off < (dev_priv->gart_vm_start + dev_priv->gart_size)) + /* Hrm ... the story of the offset ... So this function converts + * the various ideas of what userland clients might have for an + * offset in the card address space into an offset into the card + * address space :) So with a sane client, it should just keep + * the value intact and just do some boundary checking. However, + * not all clients are sane. Some older clients pass us 0 based + * offsets relative to the start of the framebuffer and some may + * assume the AGP aperture it appended to the framebuffer, so we + * try to detect those cases and fix them up. + * + * Note: It might be a good idea here to make sure the offset lands + * in some "allowed" area to protect things like the PCIE GART... + */ + + /* First, the best case, the offset already lands in either the + * framebuffer or the GART mapped space + */ + if ((off >= dev_priv->fb_location && + off < (dev_priv->fb_location + dev_priv->fb_size)) || + (off >= dev_priv->gart_vm_start && + off < (dev_priv->gart_vm_start + dev_priv->gart_size))) return 0; - radeon_priv = filp_priv->driver_priv; - off += radeon_priv->radeon_fb_delta; + /* Ok, that didn't happen... now check if we have a zero based + * offset that fits in the framebuffer + gart space, apply the + * magic offset we get from SETPARAM or calculated from fb_location + */ + if (off < (dev_priv->fb_size + dev_priv->gart_size)) { + radeon_priv = filp_priv->driver_priv; + off += radeon_priv->radeon_fb_delta; + } - DRM_DEBUG("offset fixed up to 0x%x\n", off); + /* Finally, assume we aimed at a GART offset if beyond the fb */ + if (off > (dev_priv->fb_location + dev_priv->fb_size)) + off = off - (dev_priv->fb_location + dev_priv->fb_size) + + dev_priv->gart_vm_start; - if (off < dev_priv->fb_location || - off >= (dev_priv->gart_vm_start + dev_priv->gart_size)) - return DRM_ERR(EINVAL); - - *offset = off; - - return 0; + /* Now recheck and fail if out of bounds */ + if ((off >= dev_priv->fb_location && + off < (dev_priv->fb_location + dev_priv->fb_size)) || + (off >= dev_priv->gart_vm_start && + off < (dev_priv->gart_vm_start + dev_priv->gart_size))) { + DRM_DEBUG("offset fixed up to 0x%x\n", off); + *offset = off; + return 0; + } + return DRM_ERR(EINVAL); } static __inline__ int radeon_check_and_fixup_packets(drm_radeon_private_t * @@ -1939,11 +1970,6 @@ static int radeon_surface_alloc(DRM_IOCTL_ARGS) drm_radeon_private_t *dev_priv = dev->dev_private; drm_radeon_surface_alloc_t alloc; - if (!dev_priv) { - DRM_ERROR("%s called with no initialization\n", __FUNCTION__); - return DRM_ERR(EINVAL); - } - DRM_COPY_FROM_USER_IOCTL(alloc, (drm_radeon_surface_alloc_t __user *) data, sizeof(alloc)); @@ -1960,12 +1986,7 @@ static int radeon_surface_free(DRM_IOCTL_ARGS) drm_radeon_private_t *dev_priv = dev->dev_private; drm_radeon_surface_free_t memfree; - if (!dev_priv) { - DRM_ERROR("%s called with no initialization\n", __FUNCTION__); - return DRM_ERR(EINVAL); - } - - DRM_COPY_FROM_USER_IOCTL(memfree, (drm_radeon_mem_free_t __user *) data, + DRM_COPY_FROM_USER_IOCTL(memfree, (drm_radeon_surface_free_t __user *) data, sizeof(memfree)); if (free_surface(filp, dev_priv, memfree.address)) @@ -2100,11 +2121,6 @@ static int radeon_cp_vertex(DRM_IOCTL_ARGS) LOCK_TEST_WITH_RETURN(dev, filp); - if (!dev_priv) { - DRM_ERROR("%s called with no initialization\n", __FUNCTION__); - return DRM_ERR(EINVAL); - } - DRM_GET_PRIV_WITH_RETURN(filp_priv, filp); DRM_COPY_FROM_USER_IOCTL(vertex, (drm_radeon_vertex_t __user *) data, @@ -2189,11 +2205,6 @@ static int radeon_cp_indices(DRM_IOCTL_ARGS) LOCK_TEST_WITH_RETURN(dev, filp); - if (!dev_priv) { - DRM_ERROR("%s called with no initialization\n", __FUNCTION__); - return DRM_ERR(EINVAL); - } - DRM_GET_PRIV_WITH_RETURN(filp_priv, filp); DRM_COPY_FROM_USER_IOCTL(elts, (drm_radeon_indices_t __user *) data, @@ -2340,11 +2351,6 @@ static int radeon_cp_indirect(DRM_IOCTL_ARGS) LOCK_TEST_WITH_RETURN(dev, filp); - if (!dev_priv) { - DRM_ERROR("%s called with no initialization\n", __FUNCTION__); - return DRM_ERR(EINVAL); - } - DRM_COPY_FROM_USER_IOCTL(indirect, (drm_radeon_indirect_t __user *) data, sizeof(indirect)); @@ -2417,11 +2423,6 @@ static int radeon_cp_vertex2(DRM_IOCTL_ARGS) LOCK_TEST_WITH_RETURN(dev, filp); - if (!dev_priv) { - DRM_ERROR("%s called with no initialization\n", __FUNCTION__); - return DRM_ERR(EINVAL); - } - DRM_GET_PRIV_WITH_RETURN(filp_priv, filp); DRM_COPY_FROM_USER_IOCTL(vertex, (drm_radeon_vertex2_t __user *) data, @@ -2738,11 +2739,6 @@ static int radeon_cp_cmdbuf(DRM_IOCTL_ARGS) LOCK_TEST_WITH_RETURN(dev, filp); - if (!dev_priv) { - DRM_ERROR("%s called with no initialization\n", __FUNCTION__); - return DRM_ERR(EINVAL); - } - DRM_GET_PRIV_WITH_RETURN(filp_priv, filp); DRM_COPY_FROM_USER_IOCTL(cmdbuf, @@ -2897,11 +2893,6 @@ static int radeon_cp_getparam(DRM_IOCTL_ARGS) drm_radeon_getparam_t param; int value; - if (!dev_priv) { - DRM_ERROR("%s called with no initialization\n", __FUNCTION__); - return DRM_ERR(EINVAL); - } - DRM_COPY_FROM_USER_IOCTL(param, (drm_radeon_getparam_t __user *) data, sizeof(param)); @@ -2981,11 +2972,6 @@ static int radeon_cp_setparam(DRM_IOCTL_ARGS) drm_radeon_setparam_t sp; struct drm_radeon_driver_file_fields *radeon_priv; - if (!dev_priv) { - DRM_ERROR("%s called with no initialization\n", __FUNCTION__); - return DRM_ERR(EINVAL); - } - DRM_GET_PRIV_WITH_RETURN(filp_priv, filp); DRM_COPY_FROM_USER_IOCTL(sp, (drm_radeon_setparam_t __user *) data, @@ -3012,6 +2998,9 @@ static int radeon_cp_setparam(DRM_IOCTL_ARGS) case RADEON_SETPARAM_PCIGART_LOCATION: dev_priv->pcigart_offset = sp.value; break; + case RADEON_SETPARAM_NEW_MEMMAP: + dev_priv->new_memmap = sp.value; + break; default: DRM_DEBUG("Invalid parameter %d\n", sp.param); return DRM_ERR(EINVAL); diff --git a/drivers/char/drm/sis_mm.c b/drivers/char/drm/sis_mm.c index 6774d2fe3452..5e9936bc307f 100644 --- a/drivers/char/drm/sis_mm.c +++ b/drivers/char/drm/sis_mm.c @@ -110,7 +110,7 @@ static int sis_fb_alloc(DRM_IOCTL_ARGS) DRM_COPY_TO_USER_IOCTL(argp, fb, sizeof(fb)); - DRM_DEBUG("alloc fb, size = %d, offset = %ld\n", fb.size, req.offset); + DRM_DEBUG("alloc fb, size = %d, offset = %d\n", fb.size, req.offset); return retval; } diff --git a/drivers/char/dtlk.c b/drivers/char/dtlk.c index a229915ce1b2..87dcaa237f07 100644 --- a/drivers/char/dtlk.c +++ b/drivers/char/dtlk.c @@ -490,7 +490,7 @@ for (i = 0; i < 10; i++) \ release_region(dtlk_portlist[i], DTLK_IO_EXTENT); } - printk(KERN_INFO "\nDoubleTalk PC - not found\n"); + printk(KERN_INFO "DoubleTalk PC - not found\n"); return -ENODEV; } diff --git a/drivers/char/hvcs.c b/drivers/char/hvcs.c index 327b00c3c45e..8d97b3911293 100644 --- a/drivers/char/hvcs.c +++ b/drivers/char/hvcs.c @@ -904,7 +904,7 @@ static int hvcs_enable_device(struct hvcs_struct *hvcsd, uint32_t unit_address, * It is possible the vty-server was removed after the irq was * requested but before we have time to enable interrupts. */ - if (vio_enable_interrupts(vdev) == H_Success) + if (vio_enable_interrupts(vdev) == H_SUCCESS) return 0; else { printk(KERN_ERR "HVCS: int enable failed for" diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c index 932feedda262..e1c95374984c 100644 --- a/drivers/char/ipmi/ipmi_devintf.c +++ b/drivers/char/ipmi/ipmi_devintf.c @@ -42,7 +42,7 @@ #include #include #include -#include +#include #include #include #include @@ -55,7 +55,7 @@ struct ipmi_file_private struct file *file; struct fasync_struct *fasync_queue; wait_queue_head_t wait; - struct semaphore recv_sem; + struct mutex recv_mutex; int default_retries; unsigned int default_retry_time_ms; }; @@ -141,7 +141,7 @@ static int ipmi_open(struct inode *inode, struct file *file) INIT_LIST_HEAD(&(priv->recv_msgs)); init_waitqueue_head(&priv->wait); priv->fasync_queue = NULL; - sema_init(&(priv->recv_sem), 1); + mutex_init(&priv->recv_mutex); /* Use the low-level defaults. */ priv->default_retries = -1; @@ -285,15 +285,15 @@ static int ipmi_ioctl(struct inode *inode, break; } - /* We claim a semaphore because we don't want two + /* We claim a mutex because we don't want two users getting something from the queue at a time. Since we have to release the spinlock before we can copy the data to the user, it's possible another user will grab something from the queue, too. Then the messages might get out of order if something fails and the message gets put back onto the - queue. This semaphore prevents that problem. */ - down(&(priv->recv_sem)); + queue. This mutex prevents that problem. */ + mutex_lock(&priv->recv_mutex); /* Grab the message off the list. */ spin_lock_irqsave(&(priv->recv_msg_lock), flags); @@ -352,7 +352,7 @@ static int ipmi_ioctl(struct inode *inode, goto recv_putback_on_err; } - up(&(priv->recv_sem)); + mutex_unlock(&priv->recv_mutex); ipmi_free_recv_msg(msg); break; @@ -362,11 +362,11 @@ static int ipmi_ioctl(struct inode *inode, spin_lock_irqsave(&(priv->recv_msg_lock), flags); list_add(entry, &(priv->recv_msgs)); spin_unlock_irqrestore(&(priv->recv_msg_lock), flags); - up(&(priv->recv_sem)); + mutex_unlock(&priv->recv_mutex); break; recv_err: - up(&(priv->recv_sem)); + mutex_unlock(&priv->recv_mutex); break; } diff --git a/drivers/char/ipmi/ipmi_kcs_sm.c b/drivers/char/ipmi/ipmi_kcs_sm.c index da1554194d3d..2062675f9e99 100644 --- a/drivers/char/ipmi/ipmi_kcs_sm.c +++ b/drivers/char/ipmi/ipmi_kcs_sm.c @@ -227,7 +227,7 @@ static inline int check_ibf(struct si_sm_data *kcs, unsigned char status, static inline int check_obf(struct si_sm_data *kcs, unsigned char status, long time) { - if (! GET_STATUS_OBF(status)) { + if (!GET_STATUS_OBF(status)) { kcs->obf_timeout -= time; if (kcs->obf_timeout < 0) { start_error_recovery(kcs, "OBF not ready in time"); @@ -407,7 +407,7 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time) } if (state == KCS_READ_STATE) { - if (! check_obf(kcs, status, time)) + if (!check_obf(kcs, status, time)) return SI_SM_CALL_WITH_DELAY; read_next_byte(kcs); } else { @@ -447,7 +447,7 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time) "Not in read state for error2"); break; } - if (! check_obf(kcs, status, time)) + if (!check_obf(kcs, status, time)) return SI_SM_CALL_WITH_DELAY; clear_obf(kcs, status); @@ -462,7 +462,7 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time) break; } - if (! check_obf(kcs, status, time)) + if (!check_obf(kcs, status, time)) return SI_SM_CALL_WITH_DELAY; clear_obf(kcs, status); diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 40eb005b9d77..9f2f8fdec69a 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -234,7 +235,7 @@ struct ipmi_smi /* The list of command receivers that are registered for commands on this interface. */ - struct semaphore cmd_rcvrs_lock; + struct mutex cmd_rcvrs_mutex; struct list_head cmd_rcvrs; /* Events that were queues because no one was there to receive @@ -387,10 +388,10 @@ static void clean_up_interface_data(ipmi_smi_t intf) /* Wholesale remove all the entries from the list in the * interface and wait for RCU to know that none are in use. */ - down(&intf->cmd_rcvrs_lock); + mutex_lock(&intf->cmd_rcvrs_mutex); list_add_rcu(&list, &intf->cmd_rcvrs); list_del_rcu(&intf->cmd_rcvrs); - up(&intf->cmd_rcvrs_lock); + mutex_unlock(&intf->cmd_rcvrs_mutex); synchronize_rcu(); list_for_each_entry_safe(rcvr, rcvr2, &list, link) @@ -557,7 +558,7 @@ unsigned int ipmi_addr_length(int addr_type) static void deliver_response(struct ipmi_recv_msg *msg) { - if (! msg->user) { + if (!msg->user) { ipmi_smi_t intf = msg->user_msg_data; unsigned long flags; @@ -598,11 +599,11 @@ static int intf_next_seq(ipmi_smi_t intf, (i+1)%IPMI_IPMB_NUM_SEQ != intf->curr_seq; i = (i+1)%IPMI_IPMB_NUM_SEQ) { - if (! intf->seq_table[i].inuse) + if (!intf->seq_table[i].inuse) break; } - if (! intf->seq_table[i].inuse) { + if (!intf->seq_table[i].inuse) { intf->seq_table[i].recv_msg = recv_msg; /* Start with the maximum timeout, when the send response @@ -763,7 +764,7 @@ int ipmi_create_user(unsigned int if_num, } new_user = kmalloc(sizeof(*new_user), GFP_KERNEL); - if (! new_user) + if (!new_user) return -ENOMEM; spin_lock_irqsave(&interfaces_lock, flags); @@ -819,14 +820,13 @@ static void free_user(struct kref *ref) int ipmi_destroy_user(ipmi_user_t user) { - int rv = -ENODEV; ipmi_smi_t intf = user->intf; int i; unsigned long flags; struct cmd_rcvr *rcvr; struct cmd_rcvr *rcvrs = NULL; - user->valid = 1; + user->valid = 0; /* Remove the user from the interface's sequence table. */ spin_lock_irqsave(&intf->seq_lock, flags); @@ -847,7 +847,7 @@ int ipmi_destroy_user(ipmi_user_t user) * since other things may be using it till we do * synchronize_rcu()) then free everything in that list. */ - down(&intf->cmd_rcvrs_lock); + mutex_lock(&intf->cmd_rcvrs_mutex); list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) { if (rcvr->user == user) { list_del_rcu(&rcvr->link); @@ -855,7 +855,7 @@ int ipmi_destroy_user(ipmi_user_t user) rcvrs = rcvr; } } - up(&intf->cmd_rcvrs_lock); + mutex_unlock(&intf->cmd_rcvrs_mutex); synchronize_rcu(); while (rcvrs) { rcvr = rcvrs; @@ -871,7 +871,7 @@ int ipmi_destroy_user(ipmi_user_t user) kref_put(&user->refcount, free_user); - return rv; + return 0; } void ipmi_get_version(ipmi_user_t user, @@ -936,10 +936,12 @@ int ipmi_set_gets_events(ipmi_user_t user, int val) if (val) { /* Deliver any queued events. */ - list_for_each_entry_safe(msg, msg2, &intf->waiting_events, link) { + list_for_each_entry_safe(msg, msg2, &intf->waiting_events, + link) { list_del(&msg->link); list_add_tail(&msg->link, &msgs); } + intf->waiting_events_count = 0; } /* Hold the events lock while doing this to preserve order. */ @@ -978,13 +980,13 @@ int ipmi_register_for_cmd(ipmi_user_t user, rcvr = kmalloc(sizeof(*rcvr), GFP_KERNEL); - if (! rcvr) + if (!rcvr) return -ENOMEM; rcvr->cmd = cmd; rcvr->netfn = netfn; rcvr->user = user; - down(&intf->cmd_rcvrs_lock); + mutex_lock(&intf->cmd_rcvrs_mutex); /* Make sure the command/netfn is not already registered. */ entry = find_cmd_rcvr(intf, netfn, cmd); if (entry) { @@ -995,7 +997,7 @@ int ipmi_register_for_cmd(ipmi_user_t user, list_add_rcu(&rcvr->link, &intf->cmd_rcvrs); out_unlock: - up(&intf->cmd_rcvrs_lock); + mutex_unlock(&intf->cmd_rcvrs_mutex); if (rv) kfree(rcvr); @@ -1009,17 +1011,17 @@ int ipmi_unregister_for_cmd(ipmi_user_t user, ipmi_smi_t intf = user->intf; struct cmd_rcvr *rcvr; - down(&intf->cmd_rcvrs_lock); + mutex_lock(&intf->cmd_rcvrs_mutex); /* Make sure the command/netfn is not already registered. */ rcvr = find_cmd_rcvr(intf, netfn, cmd); if ((rcvr) && (rcvr->user == user)) { list_del_rcu(&rcvr->link); - up(&intf->cmd_rcvrs_lock); + mutex_unlock(&intf->cmd_rcvrs_mutex); synchronize_rcu(); kfree(rcvr); return 0; } else { - up(&intf->cmd_rcvrs_lock); + mutex_unlock(&intf->cmd_rcvrs_mutex); return -ENOENT; } } @@ -1514,7 +1516,7 @@ int ipmi_request_settime(ipmi_user_t user, unsigned char saddr, lun; int rv; - if (! user) + if (!user) return -EINVAL; rv = check_addr(user->intf, addr, &saddr, &lun); if (rv) @@ -1545,7 +1547,7 @@ int ipmi_request_supply_msgs(ipmi_user_t user, unsigned char saddr, lun; int rv; - if (! user) + if (!user) return -EINVAL; rv = check_addr(user->intf, addr, &saddr, &lun); if (rv) @@ -1570,7 +1572,7 @@ static int ipmb_file_read_proc(char *page, char **start, off_t off, char *out = (char *) page; ipmi_smi_t intf = data; int i; - int rv= 0; + int rv = 0; for (i = 0; i < IPMI_MAX_CHANNELS; i++) rv += sprintf(out+rv, "%x ", intf->channels[i].address); @@ -1989,7 +1991,7 @@ static int ipmi_bmc_register(ipmi_smi_t intf) } else { bmc->dev = platform_device_alloc("ipmi_bmc", bmc->id.device_id); - if (! bmc->dev) { + if (!bmc->dev) { printk(KERN_ERR "ipmi_msghandler:" " Unable to allocate platform device\n"); @@ -2305,8 +2307,7 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, void *send_info, struct ipmi_device_id *device_id, struct device *si_dev, - unsigned char slave_addr, - ipmi_smi_t *new_intf) + unsigned char slave_addr) { int i, j; int rv; @@ -2366,7 +2367,7 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, spin_lock_init(&intf->events_lock); INIT_LIST_HEAD(&intf->waiting_events); intf->waiting_events_count = 0; - init_MUTEX(&intf->cmd_rcvrs_lock); + mutex_init(&intf->cmd_rcvrs_mutex); INIT_LIST_HEAD(&intf->cmd_rcvrs); init_waitqueue_head(&intf->waitq); @@ -2388,9 +2389,9 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, if (rv) goto out; - /* FIXME - this is an ugly kludge, this sets the intf for the - caller before sending any messages with it. */ - *new_intf = intf; + rv = handlers->start_processing(send_info, intf); + if (rv) + goto out; get_guid(intf); @@ -2622,7 +2623,7 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, spin_unlock_irqrestore(&intf->counter_lock, flags); recv_msg = ipmi_alloc_recv_msg(); - if (! recv_msg) { + if (!recv_msg) { /* We couldn't allocate memory for the message, so requeue it for handling later. */ @@ -2777,7 +2778,7 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf, spin_unlock_irqrestore(&intf->counter_lock, flags); recv_msg = ipmi_alloc_recv_msg(); - if (! recv_msg) { + if (!recv_msg) { /* We couldn't allocate memory for the message, so requeue it for handling later. */ @@ -2869,13 +2870,14 @@ static int handle_read_event_rsp(ipmi_smi_t intf, events. */ rcu_read_lock(); list_for_each_entry_rcu(user, &intf->users, link) { - if (! user->gets_events) + if (!user->gets_events) continue; recv_msg = ipmi_alloc_recv_msg(); - if (! recv_msg) { + if (!recv_msg) { rcu_read_unlock(); - list_for_each_entry_safe(recv_msg, recv_msg2, &msgs, link) { + list_for_each_entry_safe(recv_msg, recv_msg2, &msgs, + link) { list_del(&recv_msg->link); ipmi_free_recv_msg(recv_msg); } @@ -2905,7 +2907,7 @@ static int handle_read_event_rsp(ipmi_smi_t intf, /* No one to receive the message, put it in queue if there's not already too many things in the queue. */ recv_msg = ipmi_alloc_recv_msg(); - if (! recv_msg) { + if (!recv_msg) { /* We couldn't allocate memory for the message, so requeue it for handling later. */ @@ -2915,6 +2917,7 @@ static int handle_read_event_rsp(ipmi_smi_t intf, copy_event_into_recv_msg(recv_msg, msg); list_add_tail(&(recv_msg->link), &(intf->waiting_events)); + intf->waiting_events_count++; } else { /* There's too many things in the queue, discard this message. */ @@ -3190,7 +3193,7 @@ void ipmi_smi_watchdog_pretimeout(ipmi_smi_t intf) rcu_read_lock(); list_for_each_entry_rcu(user, &intf->users, link) { - if (! user->handler->ipmi_watchdog_pretimeout) + if (!user->handler->ipmi_watchdog_pretimeout) continue; user->handler->ipmi_watchdog_pretimeout(user->handler_data); @@ -3278,7 +3281,7 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent, smi_msg = smi_from_recv_msg(intf, ent->recv_msg, slot, ent->seqid); - if (! smi_msg) + if (!smi_msg) return; spin_unlock_irqrestore(&intf->seq_lock, *flags); @@ -3314,8 +3317,9 @@ static void ipmi_timeout_handler(long timeout_period) /* See if any waiting messages need to be processed. */ spin_lock_irqsave(&intf->waiting_msgs_lock, flags); - list_for_each_entry_safe(smi_msg, smi_msg2, &intf->waiting_msgs, link) { - if (! handle_new_recv_msg(intf, smi_msg)) { + list_for_each_entry_safe(smi_msg, smi_msg2, + &intf->waiting_msgs, link) { + if (!handle_new_recv_msg(intf, smi_msg)) { list_del(&smi_msg->link); ipmi_free_smi_msg(smi_msg); } else { diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c index 786a2802ca34..d0b5c08e7b4e 100644 --- a/drivers/char/ipmi/ipmi_poweroff.c +++ b/drivers/char/ipmi/ipmi_poweroff.c @@ -346,7 +346,7 @@ static int ipmi_dell_chassis_detect (ipmi_user_t user) { const char ipmi_version_major = ipmi_version & 0xF; const char ipmi_version_minor = (ipmi_version >> 4) & 0xF; - const char mfr[3]=DELL_IANA_MFR_ID; + const char mfr[3] = DELL_IANA_MFR_ID; if (!memcmp(mfr, &mfg_id, sizeof(mfr)) && ipmi_version_major <= 1 && ipmi_version_minor < 5) diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 35fbd4d8ed4b..a86c0f29953e 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -803,7 +803,7 @@ static int ipmi_thread(void *data) set_user_nice(current, 19); while (!kthread_should_stop()) { spin_lock_irqsave(&(smi_info->si_lock), flags); - smi_result=smi_event_handler(smi_info, 0); + smi_result = smi_event_handler(smi_info, 0); spin_unlock_irqrestore(&(smi_info->si_lock), flags); if (smi_result == SI_SM_CALL_WITHOUT_DELAY) { /* do nothing */ @@ -972,10 +972,37 @@ static irqreturn_t si_bt_irq_handler(int irq, void *data, struct pt_regs *regs) return si_irq_handler(irq, data, regs); } +static int smi_start_processing(void *send_info, + ipmi_smi_t intf) +{ + struct smi_info *new_smi = send_info; + + new_smi->intf = intf; + + /* Set up the timer that drives the interface. */ + setup_timer(&new_smi->si_timer, smi_timeout, (long)new_smi); + new_smi->last_timeout_jiffies = jiffies; + mod_timer(&new_smi->si_timer, jiffies + SI_TIMEOUT_JIFFIES); + + if (new_smi->si_type != SI_BT) { + new_smi->thread = kthread_run(ipmi_thread, new_smi, + "kipmi%d", new_smi->intf_num); + if (IS_ERR(new_smi->thread)) { + printk(KERN_NOTICE "ipmi_si_intf: Could not start" + " kernel thread due to error %ld, only using" + " timers to drive the interface\n", + PTR_ERR(new_smi->thread)); + new_smi->thread = NULL; + } + } + + return 0; +} static struct ipmi_smi_handlers handlers = { .owner = THIS_MODULE, + .start_processing = smi_start_processing, .sender = sender, .request_events = request_events, .set_run_to_completion = set_run_to_completion, @@ -987,7 +1014,7 @@ static struct ipmi_smi_handlers handlers = #define SI_MAX_PARMS 4 static LIST_HEAD(smi_infos); -static DECLARE_MUTEX(smi_infos_lock); +static DEFINE_MUTEX(smi_infos_lock); static int smi_num; /* Used to sequence the SMIs */ #define DEFAULT_REGSPACING 1 @@ -2162,9 +2189,13 @@ static void setup_xaction_handlers(struct smi_info *smi_info) static inline void wait_for_timer_and_thread(struct smi_info *smi_info) { - if (smi_info->thread != NULL && smi_info->thread != ERR_PTR(-ENOMEM)) - kthread_stop(smi_info->thread); - del_timer_sync(&smi_info->si_timer); + if (smi_info->intf) { + /* The timer and thread are only running if the + interface has been started up and registered. */ + if (smi_info->thread != NULL) + kthread_stop(smi_info->thread); + del_timer_sync(&smi_info->si_timer); + } } static struct ipmi_default_vals @@ -2245,7 +2276,7 @@ static int try_smi_init(struct smi_info *new_smi) new_smi->slave_addr, new_smi->irq); } - down(&smi_infos_lock); + mutex_lock(&smi_infos_lock); if (!is_new_interface(new_smi)) { printk(KERN_WARNING "ipmi_si: duplicate interface\n"); rv = -EBUSY; @@ -2341,21 +2372,6 @@ static int try_smi_init(struct smi_info *new_smi) if (new_smi->irq) new_smi->si_state = SI_CLEARING_FLAGS_THEN_SET_IRQ; - /* The ipmi_register_smi() code does some operations to - determine the channel information, so we must be ready to - handle operations before it is called. This means we have - to stop the timer if we get an error after this point. */ - init_timer(&(new_smi->si_timer)); - new_smi->si_timer.data = (long) new_smi; - new_smi->si_timer.function = smi_timeout; - new_smi->last_timeout_jiffies = jiffies; - new_smi->si_timer.expires = jiffies + SI_TIMEOUT_JIFFIES; - - add_timer(&(new_smi->si_timer)); - if (new_smi->si_type != SI_BT) - new_smi->thread = kthread_run(ipmi_thread, new_smi, - "kipmi%d", new_smi->intf_num); - if (!new_smi->dev) { /* If we don't already have a device from something * else (like PCI), then register a new one. */ @@ -2365,7 +2381,7 @@ static int try_smi_init(struct smi_info *new_smi) printk(KERN_ERR "ipmi_si_intf:" " Unable to allocate platform device\n"); - goto out_err_stop_timer; + goto out_err; } new_smi->dev = &new_smi->pdev->dev; new_smi->dev->driver = &ipmi_driver; @@ -2377,7 +2393,7 @@ static int try_smi_init(struct smi_info *new_smi) " Unable to register system interface device:" " %d\n", rv); - goto out_err_stop_timer; + goto out_err; } new_smi->dev_registered = 1; } @@ -2386,8 +2402,7 @@ static int try_smi_init(struct smi_info *new_smi) new_smi, &new_smi->device_id, new_smi->dev, - new_smi->slave_addr, - &(new_smi->intf)); + new_smi->slave_addr); if (rv) { printk(KERN_ERR "ipmi_si: Unable to register device: error %d\n", @@ -2417,7 +2432,7 @@ static int try_smi_init(struct smi_info *new_smi) list_add_tail(&new_smi->link, &smi_infos); - up(&smi_infos_lock); + mutex_unlock(&smi_infos_lock); printk(" IPMI %s interface initialized\n",si_to_str[new_smi->si_type]); @@ -2454,7 +2469,7 @@ static int try_smi_init(struct smi_info *new_smi) kfree(new_smi); - up(&smi_infos_lock); + mutex_unlock(&smi_infos_lock); return rv; } @@ -2512,26 +2527,26 @@ static __devinit int init_ipmi_si(void) #endif if (si_trydefaults) { - down(&smi_infos_lock); + mutex_lock(&smi_infos_lock); if (list_empty(&smi_infos)) { /* No BMC was found, try defaults. */ - up(&smi_infos_lock); + mutex_unlock(&smi_infos_lock); default_find_bmc(); } else { - up(&smi_infos_lock); + mutex_unlock(&smi_infos_lock); } } - down(&smi_infos_lock); + mutex_lock(&smi_infos_lock); if (list_empty(&smi_infos)) { - up(&smi_infos_lock); + mutex_unlock(&smi_infos_lock); #ifdef CONFIG_PCI pci_unregister_driver(&ipmi_pci_driver); #endif printk("ipmi_si: Unable to find any System Interface(s)\n"); return -ENODEV; } else { - up(&smi_infos_lock); + mutex_unlock(&smi_infos_lock); return 0; } } @@ -2607,10 +2622,10 @@ static __exit void cleanup_ipmi_si(void) pci_unregister_driver(&ipmi_pci_driver); #endif - down(&smi_infos_lock); + mutex_lock(&smi_infos_lock); list_for_each_entry_safe(e, tmp_e, &smi_infos, link) cleanup_one_si(e); - up(&smi_infos_lock); + mutex_unlock(&smi_infos_lock); driver_unregister(&ipmi_driver); } diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 7ece9f3c8f70..2d11ddd99e55 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -303,21 +304,22 @@ static int ipmi_heartbeat(void); static void panic_halt_ipmi_heartbeat(void); -/* We use a semaphore to make sure that only one thing can send a set +/* We use a mutex to make sure that only one thing can send a set timeout at one time, because we only have one copy of the data. - The semaphore is claimed when the set_timeout is sent and freed + The mutex is claimed when the set_timeout is sent and freed when both messages are free. */ static atomic_t set_timeout_tofree = ATOMIC_INIT(0); -static DECLARE_MUTEX(set_timeout_lock); +static DEFINE_MUTEX(set_timeout_lock); +static DECLARE_COMPLETION(set_timeout_wait); static void set_timeout_free_smi(struct ipmi_smi_msg *msg) { if (atomic_dec_and_test(&set_timeout_tofree)) - up(&set_timeout_lock); + complete(&set_timeout_wait); } static void set_timeout_free_recv(struct ipmi_recv_msg *msg) { if (atomic_dec_and_test(&set_timeout_tofree)) - up(&set_timeout_lock); + complete(&set_timeout_wait); } static struct ipmi_smi_msg set_timeout_smi_msg = { @@ -399,7 +401,7 @@ static int ipmi_set_timeout(int do_heartbeat) /* We can only send one of these at a time. */ - down(&set_timeout_lock); + mutex_lock(&set_timeout_lock); atomic_set(&set_timeout_tofree, 2); @@ -407,16 +409,21 @@ static int ipmi_set_timeout(int do_heartbeat) &set_timeout_recv_msg, &send_heartbeat_now); if (rv) { - up(&set_timeout_lock); - } else { - if ((do_heartbeat == IPMI_SET_TIMEOUT_FORCE_HB) - || ((send_heartbeat_now) - && (do_heartbeat == IPMI_SET_TIMEOUT_HB_IF_NECESSARY))) - { - rv = ipmi_heartbeat(); - } + mutex_unlock(&set_timeout_lock); + goto out; } + wait_for_completion(&set_timeout_wait); + + if ((do_heartbeat == IPMI_SET_TIMEOUT_FORCE_HB) + || ((send_heartbeat_now) + && (do_heartbeat == IPMI_SET_TIMEOUT_HB_IF_NECESSARY))) + { + rv = ipmi_heartbeat(); + } + mutex_unlock(&set_timeout_lock); + +out: return rv; } @@ -458,17 +465,17 @@ static void panic_halt_ipmi_set_timeout(void) The semaphore is claimed when the set_timeout is sent and freed when both messages are free. */ static atomic_t heartbeat_tofree = ATOMIC_INIT(0); -static DECLARE_MUTEX(heartbeat_lock); -static DECLARE_MUTEX_LOCKED(heartbeat_wait_lock); +static DEFINE_MUTEX(heartbeat_lock); +static DECLARE_COMPLETION(heartbeat_wait); static void heartbeat_free_smi(struct ipmi_smi_msg *msg) { if (atomic_dec_and_test(&heartbeat_tofree)) - up(&heartbeat_wait_lock); + complete(&heartbeat_wait); } static void heartbeat_free_recv(struct ipmi_recv_msg *msg) { if (atomic_dec_and_test(&heartbeat_tofree)) - up(&heartbeat_wait_lock); + complete(&heartbeat_wait); } static struct ipmi_smi_msg heartbeat_smi_msg = { @@ -511,14 +518,14 @@ static int ipmi_heartbeat(void) return ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); } - down(&heartbeat_lock); + mutex_lock(&heartbeat_lock); atomic_set(&heartbeat_tofree, 2); /* Don't reset the timer if we have the timer turned off, that re-enables the watchdog. */ if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE) { - up(&heartbeat_lock); + mutex_unlock(&heartbeat_lock); return 0; } @@ -539,14 +546,14 @@ static int ipmi_heartbeat(void) &heartbeat_recv_msg, 1); if (rv) { - up(&heartbeat_lock); + mutex_unlock(&heartbeat_lock); printk(KERN_WARNING PFX "heartbeat failure: %d\n", rv); return rv; } /* Wait for the heartbeat to be sent. */ - down(&heartbeat_wait_lock); + wait_for_completion(&heartbeat_wait); if (heartbeat_recv_msg.msg.data[0] != 0) { /* Got an error in the heartbeat response. It was already @@ -555,7 +562,7 @@ static int ipmi_heartbeat(void) rv = -EINVAL; } - up(&heartbeat_lock); + mutex_unlock(&heartbeat_lock); return rv; } @@ -589,7 +596,7 @@ static void panic_halt_ipmi_heartbeat(void) 1); } -static struct watchdog_info ident= +static struct watchdog_info ident = { .options = 0, /* WDIOF_SETTIMEOUT, */ .firmware_version = 1, @@ -790,13 +797,13 @@ static int ipmi_fasync(int fd, struct file *file, int on) static int ipmi_close(struct inode *ino, struct file *filep) { - if (iminor(ino)==WATCHDOG_MINOR) - { + if (iminor(ino) == WATCHDOG_MINOR) { if (expect_close == 42) { ipmi_watchdog_state = WDOG_TIMEOUT_NONE; ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); } else { - printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); + printk(KERN_CRIT PFX + "Unexpected close, not stopping watchdog!\n"); ipmi_heartbeat(); } clear_bit(0, &ipmi_wdog_open); diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index e5247f85a446..ef20c1fc9c4c 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -706,7 +706,6 @@ static int stli_portcmdstats(stliport_t *portp); static int stli_clrportstats(stliport_t *portp, comstats_t __user *cp); static int stli_getportstruct(stliport_t __user *arg); static int stli_getbrdstruct(stlibrd_t __user *arg); -static void *stli_memalloc(int len); static stlibrd_t *stli_allocbrd(void); static void stli_ecpinit(stlibrd_t *brdp); @@ -997,17 +996,6 @@ static int stli_parsebrd(stlconf_t *confp, char **argp) /*****************************************************************************/ -/* - * Local driver kernel malloc routine. - */ - -static void *stli_memalloc(int len) -{ - return((void *) kmalloc(len, GFP_KERNEL)); -} - -/*****************************************************************************/ - static int stli_open(struct tty_struct *tty, struct file *filp) { stlibrd_t *brdp; @@ -3227,13 +3215,12 @@ static int stli_initports(stlibrd_t *brdp) #endif for (i = 0, panelnr = 0, panelport = 0; (i < brdp->nrports); i++) { - portp = (stliport_t *) stli_memalloc(sizeof(stliport_t)); - if (portp == (stliport_t *) NULL) { + portp = kzalloc(sizeof(stliport_t), GFP_KERNEL); + if (!portp) { printk("STALLION: failed to allocate port structure\n"); continue; } - memset(portp, 0, sizeof(stliport_t)); portp->magic = STLI_PORTMAGIC; portp->portnr = i; portp->brdnr = brdp->brdnr; @@ -4610,14 +4597,13 @@ static stlibrd_t *stli_allocbrd(void) { stlibrd_t *brdp; - brdp = (stlibrd_t *) stli_memalloc(sizeof(stlibrd_t)); - if (brdp == (stlibrd_t *) NULL) { + brdp = kzalloc(sizeof(stlibrd_t), GFP_KERNEL); + if (!brdp) { printk(KERN_ERR "STALLION: failed to allocate memory " "(size=%d)\n", sizeof(stlibrd_t)); - return((stlibrd_t *) NULL); + return NULL; } - memset(brdp, 0, sizeof(stlibrd_t)); brdp->magic = STLI_BOARDMAGIC; return(brdp); } @@ -5210,12 +5196,12 @@ int __init stli_init(void) /* * Allocate a temporary write buffer. */ - stli_tmpwritebuf = (char *) stli_memalloc(STLI_TXBUFSIZE); - if (stli_tmpwritebuf == (char *) NULL) + stli_tmpwritebuf = kmalloc(STLI_TXBUFSIZE, GFP_KERNEL); + if (!stli_tmpwritebuf) printk(KERN_ERR "STALLION: failed to allocate memory " "(size=%d)\n", STLI_TXBUFSIZE); - stli_txcookbuf = stli_memalloc(STLI_TXBUFSIZE); - if (stli_txcookbuf == (char *) NULL) + stli_txcookbuf = kmalloc(STLI_TXBUFSIZE, GFP_KERNEL); + if (!stli_txcookbuf) printk(KERN_ERR "STALLION: failed to allocate memory " "(size=%d)\n", STLI_TXBUFSIZE); diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index 8b603b2d1c42..935670a3cd98 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -74,7 +74,7 @@ void compute_shiftstate(void); k_self, k_fn, k_spec, k_pad,\ k_dead, k_cons, k_cur, k_shift,\ k_meta, k_ascii, k_lock, k_lowercase,\ - k_slock, k_dead2, k_ignore, k_ignore + k_slock, k_dead2, k_brl, k_ignore typedef void (k_handler_fn)(struct vc_data *vc, unsigned char value, char up_flag, struct pt_regs *regs); @@ -100,7 +100,7 @@ static fn_handler_fn *fn_handler[] = { FN_HANDLERS }; const int max_vals[] = { 255, ARRAY_SIZE(func_table) - 1, ARRAY_SIZE(fn_handler) - 1, NR_PAD - 1, NR_DEAD - 1, 255, 3, NR_SHIFT - 1, 255, NR_ASCII - 1, NR_LOCK - 1, - 255, NR_LOCK - 1, 255 + 255, NR_LOCK - 1, 255, NR_BRL - 1 }; const int NR_TYPES = ARRAY_SIZE(max_vals); @@ -126,7 +126,7 @@ static unsigned long key_down[NBITS(KEY_MAX)]; /* keyboard key bitmap */ static unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */ static int dead_key_next; static int npadch = -1; /* -1 or number assembled on pad */ -static unsigned char diacr; +static unsigned int diacr; static char rep; /* flag telling character repeat */ static unsigned char ledstate = 0xff; /* undefined */ @@ -394,22 +394,30 @@ void compute_shiftstate(void) * Otherwise, conclude that DIACR was not combining after all, * queue it and return CH. */ -static unsigned char handle_diacr(struct vc_data *vc, unsigned char ch) +static unsigned int handle_diacr(struct vc_data *vc, unsigned int ch) { - int d = diacr; + unsigned int d = diacr; unsigned int i; diacr = 0; - for (i = 0; i < accent_table_size; i++) { - if (accent_table[i].diacr == d && accent_table[i].base == ch) - return accent_table[i].result; + if ((d & ~0xff) == BRL_UC_ROW) { + if ((ch & ~0xff) == BRL_UC_ROW) + return d | ch; + } else { + for (i = 0; i < accent_table_size; i++) + if (accent_table[i].diacr == d && accent_table[i].base == ch) + return accent_table[i].result; } - if (ch == ' ' || ch == d) + if (ch == ' ' || ch == (BRL_UC_ROW|0) || ch == d) return d; - put_queue(vc, d); + if (kbd->kbdmode == VC_UNICODE) + to_utf8(vc, d); + else if (d < 0x100) + put_queue(vc, d); + return ch; } @@ -419,7 +427,10 @@ static unsigned char handle_diacr(struct vc_data *vc, unsigned char ch) static void fn_enter(struct vc_data *vc, struct pt_regs *regs) { if (diacr) { - put_queue(vc, diacr); + if (kbd->kbdmode == VC_UNICODE) + to_utf8(vc, diacr); + else if (diacr < 0x100) + put_queue(vc, diacr); diacr = 0; } put_queue(vc, 13); @@ -615,7 +626,7 @@ static void k_lowercase(struct vc_data *vc, unsigned char value, char up_flag, s printk(KERN_ERR "keyboard.c: k_lowercase was called - impossible\n"); } -static void k_self(struct vc_data *vc, unsigned char value, char up_flag, struct pt_regs *regs) +static void k_unicode(struct vc_data *vc, unsigned int value, char up_flag, struct pt_regs *regs) { if (up_flag) return; /* no action, if this is a key release */ @@ -628,7 +639,10 @@ static void k_self(struct vc_data *vc, unsigned char value, char up_flag, struct diacr = value; return; } - put_queue(vc, value); + if (kbd->kbdmode == VC_UNICODE) + to_utf8(vc, value); + else if (value < 0x100) + put_queue(vc, value); } /* @@ -636,13 +650,23 @@ static void k_self(struct vc_data *vc, unsigned char value, char up_flag, struct * dead keys modifying the same character. Very useful * for Vietnamese. */ -static void k_dead2(struct vc_data *vc, unsigned char value, char up_flag, struct pt_regs *regs) +static void k_deadunicode(struct vc_data *vc, unsigned int value, char up_flag, struct pt_regs *regs) { if (up_flag) return; diacr = (diacr ? handle_diacr(vc, value) : value); } +static void k_self(struct vc_data *vc, unsigned char value, char up_flag, struct pt_regs *regs) +{ + k_unicode(vc, value, up_flag, regs); +} + +static void k_dead2(struct vc_data *vc, unsigned char value, char up_flag, struct pt_regs *regs) +{ + k_deadunicode(vc, value, up_flag, regs); +} + /* * Obsolete - for backwards compatibility only */ @@ -650,7 +674,7 @@ static void k_dead(struct vc_data *vc, unsigned char value, char up_flag, struct { static unsigned char ret_diacr[NR_DEAD] = {'`', '\'', '^', '~', '"', ',' }; value = ret_diacr[value]; - k_dead2(vc, value, up_flag, regs); + k_deadunicode(vc, value, up_flag, regs); } static void k_cons(struct vc_data *vc, unsigned char value, char up_flag, struct pt_regs *regs) @@ -835,6 +859,62 @@ static void k_slock(struct vc_data *vc, unsigned char value, char up_flag, struc } } +/* by default, 300ms interval for combination release */ +static long brl_timeout = 300; +MODULE_PARM_DESC(brl_timeout, "Braille keys release delay in ms (0 for combination on first release, < 0 for dead characters)"); +module_param(brl_timeout, long, 0644); +static void k_brl(struct vc_data *vc, unsigned char value, char up_flag, struct pt_regs *regs) +{ + static unsigned pressed,committing; + static unsigned long releasestart; + + if (kbd->kbdmode != VC_UNICODE) { + if (!up_flag) + printk("keyboard mode must be unicode for braille patterns\n"); + return; + } + + if (!value) { + k_unicode(vc, BRL_UC_ROW, up_flag, regs); + return; + } + + if (value > 8) + return; + + if (brl_timeout < 0) { + k_deadunicode(vc, BRL_UC_ROW | (1 << (value - 1)), up_flag, regs); + return; + } + + if (up_flag) { + if (brl_timeout) { + if (!committing || + jiffies - releasestart > (brl_timeout * HZ) / 1000) { + committing = pressed; + releasestart = jiffies; + } + pressed &= ~(1 << (value - 1)); + if (!pressed) { + if (committing) { + k_unicode(vc, BRL_UC_ROW | committing, 0, regs); + committing = 0; + } + } + } else { + if (committing) { + k_unicode(vc, BRL_UC_ROW | committing, 0, regs); + committing = 0; + } + pressed &= ~(1 << (value - 1)); + } + } else { + pressed |= 1 << (value - 1); + if (!brl_timeout) + committing = pressed; + } +} + /* * The leds display either (i) the status of NumLock, CapsLock, ScrollLock, * or (ii) whatever pattern of lights people want to show using KDSETLED, @@ -1125,9 +1205,13 @@ static void kbd_keycode(unsigned int keycode, int down, } if (keycode > NR_KEYS) - return; + if (keycode >= KEY_BRL_DOT1 && keycode <= KEY_BRL_DOT8) + keysym = K(KT_BRL, keycode - KEY_BRL_DOT1 + 1); + else + return; + else + keysym = key_map[keycode]; - keysym = key_map[keycode]; type = KTYP(keysym); if (type < 0xf0) { diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index 5fdf18515433..02114a0bd0d9 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -46,7 +46,7 @@ /* #define ATR_CSUM */ #ifdef PCMCIA_DEBUG -#define reader_to_dev(x) (&handle_to_dev(x->link.handle)) +#define reader_to_dev(x) (&handle_to_dev(x->p_dev->handle)) static int pc_debug = PCMCIA_DEBUG; module_param(pc_debug, int, 0600); #define DEBUGP(n, rdr, x, args...) do { \ @@ -67,7 +67,7 @@ static char *version = "cm4000_cs.c v2.4.0gm6 - All bugs added by Harald Welte"; #define T_100MSEC msecs_to_jiffies(100) #define T_500MSEC msecs_to_jiffies(500) -static void cm4000_release(dev_link_t *link); +static void cm4000_release(struct pcmcia_device *link); static int major; /* major number we get from the kernel */ @@ -106,7 +106,7 @@ static int major; /* major number we get from the kernel */ #define REG_STOPBITS(x) (x + 7) struct cm4000_dev { - dev_link_t link; /* pcmcia link */ + struct pcmcia_device *p_dev; dev_node_t node; /* OS node (major,minor) */ unsigned char atr[MAX_ATR]; @@ -149,14 +149,14 @@ struct cm4000_dev { #define ZERO_DEV(dev) \ memset(&dev->atr_csum,0, \ sizeof(struct cm4000_dev) - \ - /*link*/ sizeof(dev_link_t) - \ + /*link*/ sizeof(struct pcmcia_device) - \ /*node*/ sizeof(dev_node_t) - \ /*atr*/ MAX_ATR*sizeof(char) - \ /*rbuf*/ 512*sizeof(char) - \ /*sbuf*/ 512*sizeof(char) - \ /*queue*/ 4*sizeof(wait_queue_head_t)) -static dev_link_t *dev_table[CM4000_MAX_DEV]; +static struct pcmcia_device *dev_table[CM4000_MAX_DEV]; static struct class *cmm_class; /* This table doesn't use spaces after the comma between fields and thus @@ -454,7 +454,7 @@ static struct card_fixup card_fixups[] = { static void set_cardparameter(struct cm4000_dev *dev) { int i; - ioaddr_t iobase = dev->link.io.BasePort1; + ioaddr_t iobase = dev->p_dev->io.BasePort1; u_int8_t stopbits = 0x02; /* ISO default */ DEBUGP(3, dev, "-> set_cardparameter\n"); @@ -487,7 +487,7 @@ static int set_protocol(struct cm4000_dev *dev, struct ptsreq *ptsreq) unsigned short num_bytes_read; unsigned char pts_reply[4]; ssize_t rc; - ioaddr_t iobase = dev->link.io.BasePort1; + ioaddr_t iobase = dev->p_dev->io.BasePort1; rc = 0; @@ -699,7 +699,7 @@ static void terminate_monitor(struct cm4000_dev *dev) static void monitor_card(unsigned long p) { struct cm4000_dev *dev = (struct cm4000_dev *) p; - ioaddr_t iobase = dev->link.io.BasePort1; + ioaddr_t iobase = dev->p_dev->io.BasePort1; unsigned short s; struct ptsreq ptsreq; int i, atrc; @@ -962,7 +962,7 @@ static ssize_t cmm_read(struct file *filp, __user char *buf, size_t count, loff_t *ppos) { struct cm4000_dev *dev = filp->private_data; - ioaddr_t iobase = dev->link.io.BasePort1; + ioaddr_t iobase = dev->p_dev->io.BasePort1; ssize_t rc; int i, j, k; @@ -971,7 +971,7 @@ static ssize_t cmm_read(struct file *filp, __user char *buf, size_t count, if (count == 0) /* according to manpage */ return 0; - if ((dev->link.state & DEV_PRESENT) == 0 || /* socket removed */ + if (!pcmcia_dev_present(dev->p_dev) || /* device removed */ test_bit(IS_CMM_ABSENT, &dev->flags)) return -ENODEV; @@ -1083,7 +1083,7 @@ static ssize_t cmm_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { struct cm4000_dev *dev = (struct cm4000_dev *) filp->private_data; - ioaddr_t iobase = dev->link.io.BasePort1; + ioaddr_t iobase = dev->p_dev->io.BasePort1; unsigned short s; unsigned char tmp; unsigned char infolen; @@ -1108,7 +1108,7 @@ static ssize_t cmm_write(struct file *filp, const char __user *buf, sendT0 = dev->proto ? 0 : nr > 5 ? 0x08 : 0; - if ((dev->link.state & DEV_PRESENT) == 0 || /* socket removed */ + if (!pcmcia_dev_present(dev->p_dev) || /* device removed */ test_bit(IS_CMM_ABSENT, &dev->flags)) return -ENODEV; @@ -1440,8 +1440,8 @@ static int cmm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { struct cm4000_dev *dev = filp->private_data; - ioaddr_t iobase = dev->link.io.BasePort1; - dev_link_t *link; + ioaddr_t iobase = dev->p_dev->io.BasePort1; + struct pcmcia_device *link; int size; int rc; void __user *argp = (void __user *)arg; @@ -1458,7 +1458,7 @@ static int cmm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, iminor(inode), ioctl_names[_IOC_NR(cmd)]); link = dev_table[iminor(inode)]; - if (!(DEV_OK(link))) { + if (!pcmcia_dev_present(link)) { DEBUGP(4, dev, "DEV_OK false\n"); return -ENODEV; } @@ -1660,14 +1660,14 @@ static int cmm_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, static int cmm_open(struct inode *inode, struct file *filp) { struct cm4000_dev *dev; - dev_link_t *link; + struct pcmcia_device *link; int rc, minor = iminor(inode); if (minor >= CM4000_MAX_DEV) return -ENODEV; link = dev_table[minor]; - if (link == NULL || !(DEV_OK(link))) + if (link == NULL || !pcmcia_dev_present(link)) return -ENODEV; if (link->open) @@ -1709,7 +1709,7 @@ static int cmm_open(struct inode *inode, struct file *filp) static int cmm_close(struct inode *inode, struct file *filp) { struct cm4000_dev *dev; - dev_link_t *link; + struct pcmcia_device *link; int minor = iminor(inode); if (minor >= CM4000_MAX_DEV) @@ -1735,7 +1735,7 @@ static int cmm_close(struct inode *inode, struct file *filp) return 0; } -static void cmm_cm4000_release(dev_link_t * link) +static void cmm_cm4000_release(struct pcmcia_device * link) { struct cm4000_dev *dev = link->priv; @@ -1759,13 +1759,11 @@ static void cmm_cm4000_release(dev_link_t * link) /*==== Interface to PCMCIA Layer =======================================*/ -static void cm4000_config(dev_link_t * link, int devno) +static int cm4000_config(struct pcmcia_device * link, int devno) { - client_handle_t handle = link->handle; struct cm4000_dev *dev; tuple_t tuple; cisparse_t parse; - config_info_t conf; u_char buf[64]; int fail_fn, fail_rc; int rc; @@ -1777,41 +1775,34 @@ static void cm4000_config(dev_link_t * link, int devno) tuple.TupleDataMax = sizeof(buf); tuple.TupleOffset = 0; - if ((fail_rc = pcmcia_get_first_tuple(handle, &tuple)) != CS_SUCCESS) { + if ((fail_rc = pcmcia_get_first_tuple(link, &tuple)) != CS_SUCCESS) { fail_fn = GetFirstTuple; goto cs_failed; } - if ((fail_rc = pcmcia_get_tuple_data(handle, &tuple)) != CS_SUCCESS) { + if ((fail_rc = pcmcia_get_tuple_data(link, &tuple)) != CS_SUCCESS) { fail_fn = GetTupleData; goto cs_failed; } if ((fail_rc = - pcmcia_parse_tuple(handle, &tuple, &parse)) != CS_SUCCESS) { + pcmcia_parse_tuple(link, &tuple, &parse)) != CS_SUCCESS) { fail_fn = ParseTuple; goto cs_failed; } - if ((fail_rc = - pcmcia_get_configuration_info(handle, &conf)) != CS_SUCCESS) { - fail_fn = GetConfigurationInfo; - goto cs_failed; - } - link->state |= DEV_CONFIG; link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; - link->conf.Vcc = conf.Vcc; link->io.BasePort2 = 0; link->io.NumPorts2 = 0; link->io.Attributes2 = 0; tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - for (rc = pcmcia_get_first_tuple(handle, &tuple); - rc == CS_SUCCESS; rc = pcmcia_get_next_tuple(handle, &tuple)) { + for (rc = pcmcia_get_first_tuple(link, &tuple); + rc == CS_SUCCESS; rc = pcmcia_get_next_tuple(link, &tuple)) { - rc = pcmcia_get_tuple_data(handle, &tuple); + rc = pcmcia_get_tuple_data(link, &tuple); if (rc != CS_SUCCESS) continue; - rc = pcmcia_parse_tuple(handle, &tuple, &parse); + rc = pcmcia_parse_tuple(link, &tuple, &parse); if (rc != CS_SUCCESS) continue; @@ -1831,7 +1822,7 @@ static void cm4000_config(dev_link_t * link, int devno) link->io.IOAddrLines = parse.cftable_entry.io.flags & CISTPL_IO_LINES_MASK; - rc = pcmcia_request_io(handle, &link->io); + rc = pcmcia_request_io(link, &link->io); if (rc == CS_SUCCESS) break; /* we are done */ } @@ -1841,7 +1832,7 @@ static void cm4000_config(dev_link_t * link, int devno) link->conf.IntType = 00000002; if ((fail_rc = - pcmcia_request_configuration(handle, &link->conf)) != CS_SUCCESS) { + pcmcia_request_configuration(link, &link->conf)) != CS_SUCCESS) { fail_fn = RequestConfiguration; goto cs_release; } @@ -1851,63 +1842,48 @@ static void cm4000_config(dev_link_t * link, int devno) dev->node.major = major; dev->node.minor = devno; dev->node.next = NULL; - link->dev = &dev->node; - link->state &= ~DEV_CONFIG_PENDING; + link->dev_node = &dev->node; - return; + return 0; cs_failed: - cs_error(handle, fail_fn, fail_rc); + cs_error(link, fail_fn, fail_rc); cs_release: cm4000_release(link); - - link->state &= ~DEV_CONFIG_PENDING; + return -ENODEV; } -static int cm4000_suspend(struct pcmcia_device *p_dev) +static int cm4000_suspend(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct cm4000_dev *dev; dev = link->priv; - - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) - pcmcia_release_configuration(link->handle); stop_monitor(dev); return 0; } -static int cm4000_resume(struct pcmcia_device *p_dev) +static int cm4000_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct cm4000_dev *dev; dev = link->priv; - - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) - pcmcia_request_configuration(link->handle, &link->conf); - if (link->open) start_monitor(dev); return 0; } -static void cm4000_release(dev_link_t *link) +static void cm4000_release(struct pcmcia_device *link) { cmm_cm4000_release(link->priv); /* delay release until device closed */ - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); + pcmcia_disable_device(link); } -static int cm4000_attach(struct pcmcia_device *p_dev) +static int cm4000_probe(struct pcmcia_device *link) { struct cm4000_dev *dev; - dev_link_t *link; - int i; + int i, ret; for (i = 0; i < CM4000_MAX_DEV; i++) if (dev_table[i] == NULL) @@ -1923,7 +1899,7 @@ static int cm4000_attach(struct pcmcia_device *p_dev) if (dev == NULL) return -ENOMEM; - link = &dev->link; + dev->p_dev = link; link->priv = dev; link->conf.IntType = INT_MEMORY_AND_IO; dev_table[i] = link; @@ -1933,11 +1909,9 @@ static int cm4000_attach(struct pcmcia_device *p_dev) init_waitqueue_head(&dev->atrq); init_waitqueue_head(&dev->readq); - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - cm4000_config(link, i); + ret = cm4000_config(link, i); + if (ret) + return ret; class_device_create(cmm_class, NULL, MKDEV(major, i), NULL, "cmm%d", i); @@ -1945,9 +1919,8 @@ static int cm4000_attach(struct pcmcia_device *p_dev) return 0; } -static void cm4000_detach(struct pcmcia_device *p_dev) +static void cm4000_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct cm4000_dev *dev = link->priv; int devno; @@ -1958,11 +1931,9 @@ static void cm4000_detach(struct pcmcia_device *p_dev) if (devno == CM4000_MAX_DEV) return; - link->state &= ~DEV_PRESENT; stop_monitor(dev); - if (link->state & DEV_CONFIG) - cm4000_release(link); + cm4000_release(link); dev_table[devno] = NULL; kfree(dev); @@ -1993,7 +1964,7 @@ static struct pcmcia_driver cm4000_driver = { .drv = { .name = "cm4000_cs", }, - .probe = cm4000_attach, + .probe = cm4000_probe, .remove = cm4000_detach, .suspend = cm4000_suspend, .resume = cm4000_resume, diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c index 466e33bab029..29efa64580a8 100644 --- a/drivers/char/pcmcia/cm4040_cs.c +++ b/drivers/char/pcmcia/cm4040_cs.c @@ -41,7 +41,7 @@ #ifdef PCMCIA_DEBUG -#define reader_to_dev(x) (&handle_to_dev(x->link.handle)) +#define reader_to_dev(x) (&handle_to_dev(x->p_dev->handle)) static int pc_debug = PCMCIA_DEBUG; module_param(pc_debug, int, 0600); #define DEBUGP(n, rdr, x, args...) do { \ @@ -65,7 +65,7 @@ static char *version = /* how often to poll for fifo status change */ #define POLL_PERIOD msecs_to_jiffies(10) -static void reader_release(dev_link_t *link); +static void reader_release(struct pcmcia_device *link); static int major; static struct class *cmx_class; @@ -74,7 +74,7 @@ static struct class *cmx_class; #define BS_WRITABLE 0x02 struct reader_dev { - dev_link_t link; + struct pcmcia_device *p_dev; dev_node_t node; wait_queue_head_t devq; wait_queue_head_t poll_wait; @@ -87,7 +87,7 @@ struct reader_dev { struct timer_list poll_timer; }; -static dev_link_t *dev_table[CM_MAX_DEV]; +static struct pcmcia_device *dev_table[CM_MAX_DEV]; #ifndef PCMCIA_DEBUG #define xoutb outb @@ -116,7 +116,7 @@ static inline unsigned char xinb(unsigned short port) static void cm4040_do_poll(unsigned long dummy) { struct reader_dev *dev = (struct reader_dev *) dummy; - unsigned int obs = xinb(dev->link.io.BasePort1 + unsigned int obs = xinb(dev->p_dev->io.BasePort1 + REG_OFFSET_BUFFER_STATUS); if ((obs & BSR_BULK_IN_FULL)) { @@ -147,7 +147,7 @@ static void cm4040_stop_poll(struct reader_dev *dev) static int wait_for_bulk_out_ready(struct reader_dev *dev) { int i, rc; - int iobase = dev->link.io.BasePort1; + int iobase = dev->p_dev->io.BasePort1; for (i = 0; i < POLL_LOOP_COUNT; i++) { if ((xinb(iobase + REG_OFFSET_BUFFER_STATUS) @@ -177,7 +177,7 @@ static int wait_for_bulk_out_ready(struct reader_dev *dev) /* Write to Sync Control Register */ static int write_sync_reg(unsigned char val, struct reader_dev *dev) { - int iobase = dev->link.io.BasePort1; + int iobase = dev->p_dev->io.BasePort1; int rc; rc = wait_for_bulk_out_ready(dev); @@ -195,7 +195,7 @@ static int write_sync_reg(unsigned char val, struct reader_dev *dev) static int wait_for_bulk_in_ready(struct reader_dev *dev) { int i, rc; - int iobase = dev->link.io.BasePort1; + int iobase = dev->p_dev->io.BasePort1; for (i = 0; i < POLL_LOOP_COUNT; i++) { if ((xinb(iobase + REG_OFFSET_BUFFER_STATUS) @@ -225,7 +225,7 @@ static ssize_t cm4040_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { struct reader_dev *dev = filp->private_data; - int iobase = dev->link.io.BasePort1; + int iobase = dev->p_dev->io.BasePort1; size_t bytes_to_read; unsigned long i; size_t min_bytes_to_read; @@ -246,7 +246,7 @@ static ssize_t cm4040_read(struct file *filp, char __user *buf, return -EAGAIN; } - if ((dev->link.state & DEV_PRESENT)==0) + if (!pcmcia_dev_present(dev->p_dev)) return -ENODEV; for (i = 0; i < 5; i++) { @@ -328,7 +328,7 @@ static ssize_t cm4040_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { struct reader_dev *dev = filp->private_data; - int iobase = dev->link.io.BasePort1; + int iobase = dev->p_dev->io.BasePort1; ssize_t rc; int i; unsigned int bytes_to_write; @@ -351,7 +351,7 @@ static ssize_t cm4040_write(struct file *filp, const char __user *buf, return -EAGAIN; } - if ((dev->link.state & DEV_PRESENT) == 0) + if (!pcmcia_dev_present(dev->p_dev)) return -ENODEV; bytes_to_write = count; @@ -445,14 +445,14 @@ static unsigned int cm4040_poll(struct file *filp, poll_table *wait) static int cm4040_open(struct inode *inode, struct file *filp) { struct reader_dev *dev; - dev_link_t *link; + struct pcmcia_device *link; int minor = iminor(inode); if (minor >= CM_MAX_DEV) return -ENODEV; link = dev_table[minor]; - if (link == NULL || !(DEV_OK(link))) + if (link == NULL || !pcmcia_dev_present(link)) return -ENODEV; if (link->open) @@ -478,7 +478,7 @@ static int cm4040_open(struct inode *inode, struct file *filp) static int cm4040_close(struct inode *inode, struct file *filp) { struct reader_dev *dev = filp->private_data; - dev_link_t *link; + struct pcmcia_device *link; int minor = iminor(inode); DEBUGP(2, dev, "-> cm4040_close(maj/min=%d.%d)\n", imajor(inode), @@ -500,7 +500,7 @@ static int cm4040_close(struct inode *inode, struct file *filp) return 0; } -static void cm4040_reader_release(dev_link_t *link) +static void cm4040_reader_release(struct pcmcia_device *link) { struct reader_dev *dev = link->priv; @@ -514,60 +514,49 @@ static void cm4040_reader_release(dev_link_t *link) return; } -static void reader_config(dev_link_t *link, int devno) +static int reader_config(struct pcmcia_device *link, int devno) { - client_handle_t handle; struct reader_dev *dev; tuple_t tuple; cisparse_t parse; - config_info_t conf; u_char buf[64]; int fail_fn, fail_rc; int rc; - handle = link->handle; - tuple.DesiredTuple = CISTPL_CONFIG; tuple.Attributes = 0; tuple.TupleData = buf; tuple.TupleDataMax = sizeof(buf); tuple.TupleOffset = 0; - if ((fail_rc = pcmcia_get_first_tuple(handle, &tuple)) != CS_SUCCESS) { + if ((fail_rc = pcmcia_get_first_tuple(link, &tuple)) != CS_SUCCESS) { fail_fn = GetFirstTuple; goto cs_failed; } - if ((fail_rc = pcmcia_get_tuple_data(handle, &tuple)) != CS_SUCCESS) { + if ((fail_rc = pcmcia_get_tuple_data(link, &tuple)) != CS_SUCCESS) { fail_fn = GetTupleData; goto cs_failed; } - if ((fail_rc = pcmcia_parse_tuple(handle, &tuple, &parse)) + if ((fail_rc = pcmcia_parse_tuple(link, &tuple, &parse)) != CS_SUCCESS) { fail_fn = ParseTuple; goto cs_failed; } - if ((fail_rc = pcmcia_get_configuration_info(handle, &conf)) - != CS_SUCCESS) { - fail_fn = GetConfigurationInfo; - goto cs_failed; - } - link->state |= DEV_CONFIG; link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; - link->conf.Vcc = conf.Vcc; link->io.BasePort2 = 0; link->io.NumPorts2 = 0; link->io.Attributes2 = 0; tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - for (rc = pcmcia_get_first_tuple(handle, &tuple); + for (rc = pcmcia_get_first_tuple(link, &tuple); rc == CS_SUCCESS; - rc = pcmcia_get_next_tuple(handle, &tuple)) { - rc = pcmcia_get_tuple_data(handle, &tuple); + rc = pcmcia_get_next_tuple(link, &tuple)) { + rc = pcmcia_get_tuple_data(link, &tuple); if (rc != CS_SUCCESS) continue; - rc = pcmcia_parse_tuple(handle, &tuple, &parse); + rc = pcmcia_parse_tuple(link, &tuple, &parse); if (rc != CS_SUCCESS) continue; @@ -585,13 +574,13 @@ static void reader_config(dev_link_t *link, int devno) link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; link->io.IOAddrLines = parse.cftable_entry.io.flags & CISTPL_IO_LINES_MASK; - rc = pcmcia_request_io(handle, &link->io); + rc = pcmcia_request_io(link, &link->io); - dev_printk(KERN_INFO, &handle_to_dev(handle), "foo"); + dev_printk(KERN_INFO, &handle_to_dev(link), "foo"); if (rc == CS_SUCCESS) break; else - dev_printk(KERN_INFO, &handle_to_dev(handle), + dev_printk(KERN_INFO, &handle_to_dev(link), "pcmcia_request_io failed 0x%x\n", rc); } if (rc != CS_SUCCESS) @@ -599,10 +588,10 @@ static void reader_config(dev_link_t *link, int devno) link->conf.IntType = 00000002; - if ((fail_rc = pcmcia_request_configuration(handle,&link->conf)) + if ((fail_rc = pcmcia_request_configuration(link,&link->conf)) !=CS_SUCCESS) { fail_fn = RequestConfiguration; - dev_printk(KERN_INFO, &handle_to_dev(handle), + dev_printk(KERN_INFO, &handle_to_dev(link), "pcmcia_request_configuration failed 0x%x\n", fail_rc); goto cs_release; @@ -612,57 +601,31 @@ static void reader_config(dev_link_t *link, int devno) sprintf(dev->node.dev_name, DEVICE_NAME "%d", devno); dev->node.major = major; dev->node.minor = devno; - dev->node.next = NULL; - link->dev = &dev->node; - link->state &= ~DEV_CONFIG_PENDING; + dev->node.next = &dev->node; DEBUGP(2, dev, "device " DEVICE_NAME "%d at 0x%.4x-0x%.4x\n", devno, link->io.BasePort1, link->io.BasePort1+link->io.NumPorts1); DEBUGP(2, dev, "<- reader_config (succ)\n"); - return; + return 0; cs_failed: - cs_error(handle, fail_fn, fail_rc); + cs_error(link, fail_fn, fail_rc); cs_release: reader_release(link); - link->state &= ~DEV_CONFIG_PENDING; + return -ENODEV; } -static int reader_suspend(struct pcmcia_device *p_dev) -{ - dev_link_t *link = dev_to_instance(p_dev); - - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) - pcmcia_release_configuration(link->handle); - - return 0; -} - -static int reader_resume(struct pcmcia_device *p_dev) -{ - dev_link_t *link = dev_to_instance(p_dev); - - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) - pcmcia_request_configuration(link->handle, &link->conf); - - return 0; -} - -static void reader_release(dev_link_t *link) +static void reader_release(struct pcmcia_device *link) { cm4040_reader_release(link->priv); - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); + pcmcia_disable_device(link); } -static int reader_attach(struct pcmcia_device *p_dev) +static int reader_probe(struct pcmcia_device *link) { struct reader_dev *dev; - dev_link_t *link; - int i; + int i, ret; for (i = 0; i < CM_MAX_DEV; i++) { if (dev_table[i] == NULL) @@ -679,8 +642,8 @@ static int reader_attach(struct pcmcia_device *p_dev) dev->timeout = CCID_DRIVER_MINIMUM_TIMEOUT; dev->buffer_status = 0; - link = &dev->link; link->priv = dev; + dev->p_dev = link; link->conf.IntType = INT_MEMORY_AND_IO; dev_table[i] = link; @@ -692,11 +655,9 @@ static int reader_attach(struct pcmcia_device *p_dev) init_timer(&dev->poll_timer); dev->poll_timer.function = &cm4040_do_poll; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - reader_config(link, i); + ret = reader_config(link, i); + if (ret) + return ret; class_device_create(cmx_class, NULL, MKDEV(major, i), NULL, "cmx%d", i); @@ -704,9 +665,8 @@ static int reader_attach(struct pcmcia_device *p_dev) return 0; } -static void reader_detach(struct pcmcia_device *p_dev) +static void reader_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct reader_dev *dev = link->priv; int devno; @@ -718,10 +678,7 @@ static void reader_detach(struct pcmcia_device *p_dev) if (devno == CM_MAX_DEV) return; - link->state &= ~DEV_PRESENT; - - if (link->state & DEV_CONFIG) - reader_release(link); + reader_release(link); dev_table[devno] = NULL; kfree(dev); @@ -753,10 +710,8 @@ static struct pcmcia_driver reader_driver = { .drv = { .name = "cm4040_cs", }, - .probe = reader_attach, + .probe = reader_probe, .remove = reader_detach, - .suspend = reader_suspend, - .resume = reader_resume, .id_table = cm4040_ids, }; diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index e6b714b6390d..07213454c458 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -228,7 +228,7 @@ typedef struct _mgslpc_info { struct _input_signal_events input_signal_events; /* PCMCIA support */ - dev_link_t link; + struct pcmcia_device *p_dev; dev_node_t node; int stop; @@ -484,7 +484,7 @@ static void mgslpc_wait_until_sent(struct tty_struct *tty, int timeout); /* PCMCIA prototypes */ -static void mgslpc_config(dev_link_t *link); +static int mgslpc_config(struct pcmcia_device *link); static void mgslpc_release(u_long arg); static void mgslpc_detach(struct pcmcia_device *p_dev); @@ -533,14 +533,14 @@ static void ldisc_receive_buf(struct tty_struct *tty, } } -static int mgslpc_attach(struct pcmcia_device *p_dev) +static int mgslpc_probe(struct pcmcia_device *link) { MGSLPC_INFO *info; - dev_link_t *link; - + int ret; + if (debug_level >= DEBUG_LEVEL_INFO) printk("mgslpc_attach\n"); - + info = (MGSLPC_INFO *)kmalloc(sizeof(MGSLPC_INFO), GFP_KERNEL); if (!info) { printk("Error can't allocate device instance data\n"); @@ -565,25 +565,22 @@ static int mgslpc_attach(struct pcmcia_device *p_dev) info->imrb_value = 0xffff; info->pim_value = 0xff; - link = &info->link; + info->p_dev = link; link->priv = info; - - /* Initialize the dev_link_t structure */ + + /* Initialize the struct pcmcia_device structure */ /* Interrupt setup */ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->irq.Handler = NULL; - + link->conf.Attributes = 0; - link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - mgslpc_config(link); + ret = mgslpc_config(link); + if (ret) + return ret; mgslpc_add_device(info); @@ -596,15 +593,13 @@ static int mgslpc_attach(struct pcmcia_device *p_dev) #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) -static void mgslpc_config(dev_link_t *link) +static int mgslpc_config(struct pcmcia_device *link) { - client_handle_t handle = link->handle; MGSLPC_INFO *info = link->priv; tuple_t tuple; cisparse_t parse; int last_fn, last_ret; u_char buf[64]; - config_info_t conf; cistpl_cftable_entry_t dflt = { 0 }; cistpl_cftable_entry_t *cfg; @@ -617,27 +612,20 @@ static void mgslpc_config(dev_link_t *link) tuple.TupleData = buf; tuple.TupleDataMax = sizeof(buf); tuple.TupleOffset = 0; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; - - /* Configure card */ - link->state |= DEV_CONFIG; - - /* Look up the current Vcc */ - CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf)); - link->conf.Vcc = conf.Vcc; /* get CIS configuration entry */ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); cfg = &(parse.cftable_entry); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg; if (cfg->index == 0) @@ -658,11 +646,10 @@ static void mgslpc_config(dev_link_t *link) link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK; link->io.BasePort1 = io->win[0].base; link->io.NumPorts1 = io->win[0].len; - CS_CHECK(RequestIO, pcmcia_request_io(link->handle, &link->io)); + CS_CHECK(RequestIO, pcmcia_request_io(link, &link->io)); } link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; link->conf.ConfigIndex = 8; link->conf.Present = PRESENT_OPTION; @@ -670,9 +657,9 @@ static void mgslpc_config(dev_link_t *link) link->irq.Attributes |= IRQ_HANDLE_PRESENT; link->irq.Handler = mgslpc_isr; link->irq.Instance = info; - CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); info->io_base = link->io.BasePort1; info->irq_level = link->irq.AssignedIRQ; @@ -680,7 +667,7 @@ static void mgslpc_config(dev_link_t *link) /* add to linked list of devices */ sprintf(info->node.dev_name, "mgslpc0"); info->node.major = info->node.minor = 0; - link->dev = &info->node; + link->dev_node = &info->node; printk(KERN_INFO "%s: index 0x%02x:", info->node.dev_name, link->conf.ConfigIndex); @@ -690,13 +677,12 @@ static void mgslpc_config(dev_link_t *link) printk(", io 0x%04x-0x%04x", link->io.BasePort1, link->io.BasePort1+link->io.NumPorts1-1); printk("\n"); - - link->state &= ~DEV_CONFIG_PENDING; - return; + return 0; cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); mgslpc_release((u_long)link); + return -ENODEV; } /* Card has been removed. @@ -705,58 +691,38 @@ cs_failed: */ static void mgslpc_release(u_long arg) { - dev_link_t *link = (dev_link_t *)arg; + struct pcmcia_device *link = (struct pcmcia_device *)arg; - if (debug_level >= DEBUG_LEVEL_INFO) - printk("mgslpc_release(0x%p)\n", link); + if (debug_level >= DEBUG_LEVEL_INFO) + printk("mgslpc_release(0x%p)\n", link); - /* Unlink the device chain */ - link->dev = NULL; - link->state &= ~DEV_CONFIG; - - pcmcia_release_configuration(link->handle); - if (link->io.NumPorts1) - pcmcia_release_io(link->handle, &link->io); - if (link->irq.AssignedIRQ) - pcmcia_release_irq(link->handle, &link->irq); + pcmcia_disable_device(link); } -static void mgslpc_detach(struct pcmcia_device *p_dev) +static void mgslpc_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); + if (debug_level >= DEBUG_LEVEL_INFO) + printk("mgslpc_detach(0x%p)\n", link); - if (debug_level >= DEBUG_LEVEL_INFO) - printk("mgslpc_detach(0x%p)\n", link); + ((MGSLPC_INFO *)link->priv)->stop = 1; + mgslpc_release((u_long)link); - if (link->state & DEV_CONFIG) { - ((MGSLPC_INFO *)link->priv)->stop = 1; - mgslpc_release((u_long)link); - } - - mgslpc_remove_device((MGSLPC_INFO *)link->priv); + mgslpc_remove_device((MGSLPC_INFO *)link->priv); } -static int mgslpc_suspend(struct pcmcia_device *dev) +static int mgslpc_suspend(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(dev); MGSLPC_INFO *info = link->priv; - link->state |= DEV_SUSPEND; info->stop = 1; - if (link->state & DEV_CONFIG) - pcmcia_release_configuration(link->handle); return 0; } -static int mgslpc_resume(struct pcmcia_device *dev) +static int mgslpc_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(dev); MGSLPC_INFO *info = link->priv; - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) - pcmcia_request_configuration(link->handle, &link->conf); info->stop = 0; return 0; @@ -1280,7 +1246,7 @@ static irqreturn_t mgslpc_isr(int irq, void *dev_id, struct pt_regs * regs) if (!info) return IRQ_NONE; - if (!(info->link.state & DEV_CONFIG)) + if (!(info->p_dev->_locked)) return IRQ_HANDLED; spin_lock(&info->lock); @@ -3033,7 +2999,7 @@ static struct pcmcia_driver mgslpc_driver = { .drv = { .name = "synclink_cs", }, - .probe = mgslpc_attach, + .probe = mgslpc_probe, .remove = mgslpc_detach, .id_table = mgslpc_ids, .suspend = mgslpc_suspend, diff --git a/drivers/char/random.c b/drivers/char/random.c index 86be04b241e1..58f3512c52e1 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1584,7 +1584,6 @@ u32 secure_ipv6_port_ephemeral(const __u32 *saddr, const __u32 *daddr, __u16 dpo return twothirdsMD4Transform(daddr, hash); } -EXPORT_SYMBOL(secure_ipv6_port_ephemeral); #endif #if defined(CONFIG_IP_DCCP) || defined(CONFIG_IP_DCCP_MODULE) diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c index 3f5d6077f39c..a9c5a7230f89 100644 --- a/drivers/char/stallion.c +++ b/drivers/char/stallion.c @@ -504,7 +504,6 @@ static int stl_echmcaintr(stlbrd_t *brdp); static int stl_echpciintr(stlbrd_t *brdp); static int stl_echpci64intr(stlbrd_t *brdp); static void stl_offintr(void *private); -static void *stl_memalloc(int len); static stlbrd_t *stl_allocbrd(void); static stlport_t *stl_getport(int brdnr, int panelnr, int portnr); @@ -939,17 +938,6 @@ static int stl_parsebrd(stlconf_t *confp, char **argp) /*****************************************************************************/ -/* - * Local driver kernel memory allocation routine. - */ - -static void *stl_memalloc(int len) -{ - return (void *) kmalloc(len, GFP_KERNEL); -} - -/*****************************************************************************/ - /* * Allocate a new board structure. Fill out the basic info in it. */ @@ -958,14 +946,13 @@ static stlbrd_t *stl_allocbrd(void) { stlbrd_t *brdp; - brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t)); - if (brdp == (stlbrd_t *) NULL) { + brdp = kzalloc(sizeof(stlbrd_t), GFP_KERNEL); + if (!brdp) { printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlbrd_t)); - return (stlbrd_t *) NULL; + return NULL; } - memset(brdp, 0, sizeof(stlbrd_t)); brdp->magic = STL_BOARDMAGIC; return brdp; } @@ -1017,9 +1004,9 @@ static int stl_open(struct tty_struct *tty, struct file *filp) portp->refcount++; if ((portp->flags & ASYNC_INITIALIZED) == 0) { - if (portp->tx.buf == (char *) NULL) { - portp->tx.buf = (char *) stl_memalloc(STL_TXBUFSIZE); - if (portp->tx.buf == (char *) NULL) + if (!portp->tx.buf) { + portp->tx.buf = kmalloc(STL_TXBUFSIZE, GFP_KERNEL); + if (!portp->tx.buf) return -ENOMEM; portp->tx.head = portp->tx.buf; portp->tx.tail = portp->tx.buf; @@ -2178,13 +2165,12 @@ static int __init stl_initports(stlbrd_t *brdp, stlpanel_t *panelp) * each ports data structures. */ for (i = 0; (i < panelp->nrports); i++) { - portp = (stlport_t *) stl_memalloc(sizeof(stlport_t)); - if (portp == (stlport_t *) NULL) { + portp = kzalloc(sizeof(stlport_t), GFP_KERNEL); + if (!portp) { printk("STALLION: failed to allocate memory " "(size=%d)\n", sizeof(stlport_t)); break; } - memset(portp, 0, sizeof(stlport_t)); portp->magic = STL_PORTMAGIC; portp->portnr = i; @@ -2315,13 +2301,12 @@ static inline int stl_initeio(stlbrd_t *brdp) * can complete the setup. */ - panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t)); - if (panelp == (stlpanel_t *) NULL) { + panelp = kzalloc(sizeof(stlpanel_t), GFP_KERNEL); + if (!panelp) { printk(KERN_WARNING "STALLION: failed to allocate memory " "(size=%d)\n", sizeof(stlpanel_t)); - return(-ENOMEM); + return -ENOMEM; } - memset(panelp, 0, sizeof(stlpanel_t)); panelp->magic = STL_PANELMAGIC; panelp->brdnr = brdp->brdnr; @@ -2490,13 +2475,12 @@ static inline int stl_initech(stlbrd_t *brdp) status = inb(ioaddr + ECH_PNLSTATUS); if ((status & ECH_PNLIDMASK) != nxtid) break; - panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t)); - if (panelp == (stlpanel_t *) NULL) { + panelp = kzalloc(sizeof(stlpanel_t), GFP_KERNEL); + if (!panelp) { printk("STALLION: failed to allocate memory " "(size=%d)\n", sizeof(stlpanel_t)); break; } - memset(panelp, 0, sizeof(stlpanel_t)); panelp->magic = STL_PANELMAGIC; panelp->brdnr = brdp->brdnr; panelp->panelnr = panelnr; @@ -3074,8 +3058,8 @@ static int __init stl_init(void) /* * Allocate a temporary write buffer. */ - stl_tmpwritebuf = (char *) stl_memalloc(STL_TXBUFSIZE); - if (stl_tmpwritebuf == (char *) NULL) + stl_tmpwritebuf = kmalloc(STL_TXBUFSIZE, GFP_KERNEL); + if (!stl_tmpwritebuf) printk("STALLION: failed to allocate memory (size=%d)\n", STL_TXBUFSIZE); diff --git a/drivers/char/tlclk.c b/drivers/char/tlclk.c index 2546637a55c0..f58ad7f68267 100644 --- a/drivers/char/tlclk.c +++ b/drivers/char/tlclk.c @@ -327,7 +327,7 @@ static ssize_t store_received_ref_clk3a(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(received_ref_clk3a, S_IWUGO, NULL, +static DEVICE_ATTR(received_ref_clk3a, (S_IWUSR|S_IWGRP), NULL, store_received_ref_clk3a); @@ -349,7 +349,7 @@ static ssize_t store_received_ref_clk3b(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(received_ref_clk3b, S_IWUGO, NULL, +static DEVICE_ATTR(received_ref_clk3b, (S_IWUSR|S_IWGRP), NULL, store_received_ref_clk3b); @@ -371,7 +371,7 @@ static ssize_t store_enable_clk3b_output(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(enable_clk3b_output, S_IWUGO, NULL, +static DEVICE_ATTR(enable_clk3b_output, (S_IWUSR|S_IWGRP), NULL, store_enable_clk3b_output); static ssize_t store_enable_clk3a_output(struct device *d, @@ -392,7 +392,7 @@ static ssize_t store_enable_clk3a_output(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(enable_clk3a_output, S_IWUGO, NULL, +static DEVICE_ATTR(enable_clk3a_output, (S_IWUSR|S_IWGRP), NULL, store_enable_clk3a_output); static ssize_t store_enable_clkb1_output(struct device *d, @@ -413,7 +413,7 @@ static ssize_t store_enable_clkb1_output(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(enable_clkb1_output, S_IWUGO, NULL, +static DEVICE_ATTR(enable_clkb1_output, (S_IWUSR|S_IWGRP), NULL, store_enable_clkb1_output); @@ -435,7 +435,7 @@ static ssize_t store_enable_clka1_output(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(enable_clka1_output, S_IWUGO, NULL, +static DEVICE_ATTR(enable_clka1_output, (S_IWUSR|S_IWGRP), NULL, store_enable_clka1_output); static ssize_t store_enable_clkb0_output(struct device *d, @@ -456,7 +456,7 @@ static ssize_t store_enable_clkb0_output(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(enable_clkb0_output, S_IWUGO, NULL, +static DEVICE_ATTR(enable_clkb0_output, (S_IWUSR|S_IWGRP), NULL, store_enable_clkb0_output); static ssize_t store_enable_clka0_output(struct device *d, @@ -477,7 +477,7 @@ static ssize_t store_enable_clka0_output(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(enable_clka0_output, S_IWUGO, NULL, +static DEVICE_ATTR(enable_clka0_output, (S_IWUSR|S_IWGRP), NULL, store_enable_clka0_output); static ssize_t store_select_amcb2_transmit_clock(struct device *d, @@ -519,7 +519,7 @@ static ssize_t store_select_amcb2_transmit_clock(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(select_amcb2_transmit_clock, S_IWUGO, NULL, +static DEVICE_ATTR(select_amcb2_transmit_clock, (S_IWUSR|S_IWGRP), NULL, store_select_amcb2_transmit_clock); static ssize_t store_select_amcb1_transmit_clock(struct device *d, @@ -560,7 +560,7 @@ static ssize_t store_select_amcb1_transmit_clock(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(select_amcb1_transmit_clock, S_IWUGO, NULL, +static DEVICE_ATTR(select_amcb1_transmit_clock, (S_IWUSR|S_IWGRP), NULL, store_select_amcb1_transmit_clock); static ssize_t store_select_redundant_clock(struct device *d, @@ -581,7 +581,7 @@ static ssize_t store_select_redundant_clock(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(select_redundant_clock, S_IWUGO, NULL, +static DEVICE_ATTR(select_redundant_clock, (S_IWUSR|S_IWGRP), NULL, store_select_redundant_clock); static ssize_t store_select_ref_frequency(struct device *d, @@ -602,7 +602,7 @@ static ssize_t store_select_ref_frequency(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(select_ref_frequency, S_IWUGO, NULL, +static DEVICE_ATTR(select_ref_frequency, (S_IWUSR|S_IWGRP), NULL, store_select_ref_frequency); static ssize_t store_filter_select(struct device *d, @@ -623,7 +623,7 @@ static ssize_t store_filter_select(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(filter_select, S_IWUGO, NULL, store_filter_select); +static DEVICE_ATTR(filter_select, (S_IWUSR|S_IWGRP), NULL, store_filter_select); static ssize_t store_hardware_switching_mode(struct device *d, struct device_attribute *attr, const char *buf, size_t count) @@ -643,7 +643,7 @@ static ssize_t store_hardware_switching_mode(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(hardware_switching_mode, S_IWUGO, NULL, +static DEVICE_ATTR(hardware_switching_mode, (S_IWUSR|S_IWGRP), NULL, store_hardware_switching_mode); static ssize_t store_hardware_switching(struct device *d, @@ -664,7 +664,7 @@ static ssize_t store_hardware_switching(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(hardware_switching, S_IWUGO, NULL, +static DEVICE_ATTR(hardware_switching, (S_IWUSR|S_IWGRP), NULL, store_hardware_switching); static ssize_t store_refalign (struct device *d, @@ -684,7 +684,7 @@ static ssize_t store_refalign (struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(refalign, S_IWUGO, NULL, store_refalign); +static DEVICE_ATTR(refalign, (S_IWUSR|S_IWGRP), NULL, store_refalign); static ssize_t store_mode_select (struct device *d, struct device_attribute *attr, const char *buf, size_t count) @@ -704,7 +704,7 @@ static ssize_t store_mode_select (struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(mode_select, S_IWUGO, NULL, store_mode_select); +static DEVICE_ATTR(mode_select, (S_IWUSR|S_IWGRP), NULL, store_mode_select); static ssize_t store_reset (struct device *d, struct device_attribute *attr, const char *buf, size_t count) @@ -724,7 +724,7 @@ static ssize_t store_reset (struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(reset, S_IWUGO, NULL, store_reset); +static DEVICE_ATTR(reset, (S_IWUSR|S_IWGRP), NULL, store_reset); static struct attribute *tlclk_sysfs_entries[] = { &dev_attr_current_ref.attr, diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 0bfd1b63662e..f70a47eadb52 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -351,10 +351,10 @@ int tty_buffer_request_room(struct tty_struct *tty, size_t size) spin_unlock_irqrestore(&tty->buf.lock, flags); return size; } - EXPORT_SYMBOL_GPL(tty_buffer_request_room); -int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars, size_t size) +int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars, + size_t size) { int copied = 0; do { @@ -368,17 +368,16 @@ int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars, s tb->used += space; copied += space; chars += space; -/* printk("Flip insert %d.\n", space); */ } /* There is a small chance that we need to split the data over several buffers. If this is the case we must loop */ while (unlikely(size > copied)); return copied; } +EXPORT_SYMBOL(tty_insert_flip_string); -EXPORT_SYMBOL_GPL(tty_insert_flip_string); - -int tty_insert_flip_string_flags(struct tty_struct *tty, const unsigned char *chars, const char *flags, size_t size) +int tty_insert_flip_string_flags(struct tty_struct *tty, + const unsigned char *chars, const char *flags, size_t size) { int copied = 0; do { @@ -399,9 +398,20 @@ int tty_insert_flip_string_flags(struct tty_struct *tty, const unsigned char *ch while (unlikely(size > copied)); return copied; } - EXPORT_SYMBOL_GPL(tty_insert_flip_string_flags); +void tty_schedule_flip(struct tty_struct *tty) +{ + unsigned long flags; + spin_lock_irqsave(&tty->buf.lock, flags); + if (tty->buf.tail != NULL) { + tty->buf.tail->active = 0; + tty->buf.tail->commit = tty->buf.tail->used; + } + spin_unlock_irqrestore(&tty->buf.lock, flags); + schedule_delayed_work(&tty->buf.work, 1); +} +EXPORT_SYMBOL(tty_schedule_flip); /* * Prepare a block of space in the buffer for data. Returns the length @@ -1730,7 +1740,7 @@ static void release_dev(struct file * filp) { struct tty_struct *tty, *o_tty; int pty_master, tty_closing, o_tty_closing, do_sleep; - int devpts_master, devpts; + int devpts; int idx; char buf[64]; unsigned long flags; @@ -1747,7 +1757,6 @@ static void release_dev(struct file * filp) pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER); devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0; - devpts_master = pty_master && devpts; o_tty = tty->link; #ifdef TTY_PARANOIA_CHECK @@ -2185,6 +2194,7 @@ static int ptmx_open(struct inode * inode, struct file * filp) return 0; out1: release_dev(filp); + return retval; out: down(&allocated_ptys_lock); idr_remove(&allocated_ptys, index); diff --git a/drivers/char/vt.c b/drivers/char/vt.c index ca4844c527da..acc5d47844eb 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -2328,6 +2328,10 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) case TIOCL_SETVESABLANK: set_vesa_blanking(p); break; + case TIOCL_GETKMSGREDIRECT: + data = kmsg_redirect; + ret = __put_user(data, p); + break; case TIOCL_SETKMSGREDIRECT: if (!capable(CAP_SYS_ADMIN)) { ret = -EPERM; diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig index 16e99db2e12d..d53f664a4dd8 100644 --- a/drivers/char/watchdog/Kconfig +++ b/drivers/char/watchdog/Kconfig @@ -60,6 +60,13 @@ config SOFT_WATCHDOG # ARM Architecture +config AT91_WATCHDOG + tristate "AT91RM9200 watchdog" + depends on WATCHDOG && ARCH_AT91RM9200 + help + Watchdog timer embedded into AT91RM9200 chips. This will reboot your + system when the timeout is reached. + config 21285_WATCHDOG tristate "DC21285 watchdog" depends on WATCHDOG && FOOTBRIDGE diff --git a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile index d6f27fde9905..6ab77b61a643 100644 --- a/drivers/char/watchdog/Makefile +++ b/drivers/char/watchdog/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_WDTPCI) += wdt_pci.o obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o # ARM Architecture +obj-$(CONFIG_AT91_WATCHDOG) += at91_wdt.o obj-$(CONFIG_21285_WATCHDOG) += wdt285.o obj-$(CONFIG_977_WATCHDOG) += wdt977.o obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o diff --git a/drivers/char/watchdog/at91_wdt.c b/drivers/char/watchdog/at91_wdt.c new file mode 100644 index 000000000000..ac83bc4b019a --- /dev/null +++ b/drivers/char/watchdog/at91_wdt.c @@ -0,0 +1,228 @@ +/* + * Watchdog driver for Atmel AT91RM9200 (Thunder) + * + * Copyright (C) 2003 SAN People (Pty) Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define WDT_DEFAULT_TIME 5 /* 5 seconds */ +#define WDT_MAX_TIME 256 /* 256 seconds */ + +static int wdt_time = WDT_DEFAULT_TIME; +static int nowayout = WATCHDOG_NOWAYOUT; + +module_param(wdt_time, int, 0); +MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="__MODULE_STRING(WDT_DEFAULT_TIME) ")"); + +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + + +static unsigned long at91wdt_busy; + +/* ......................................................................... */ + +/* + * Disable the watchdog. + */ +static void inline at91_wdt_stop(void) +{ + at91_sys_write(AT91_ST_WDMR, AT91_ST_EXTEN); +} + +/* + * Enable and reset the watchdog. + */ +static void inline at91_wdt_start(void) +{ + at91_sys_write(AT91_ST_WDMR, AT91_ST_EXTEN | AT91_ST_RSTEN | (((65536 * wdt_time) >> 8) & AT91_ST_WDV)); + at91_sys_write(AT91_ST_CR, AT91_ST_WDRST); +} + +/* + * Reload the watchdog timer. (ie, pat the watchdog) + */ +static void inline at91_wdt_reload(void) +{ + at91_sys_write(AT91_ST_CR, AT91_ST_WDRST); +} + +/* ......................................................................... */ + +/* + * Watchdog device is opened, and watchdog starts running. + */ +static int at91_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &at91wdt_busy)) + return -EBUSY; + + at91_wdt_start(); + return nonseekable_open(inode, file); +} + +/* + * Close the watchdog device. + * If CONFIG_WATCHDOG_NOWAYOUT is NOT defined then the watchdog is also + * disabled. + */ +static int at91_wdt_close(struct inode *inode, struct file *file) +{ + if (!nowayout) + at91_wdt_stop(); /* Disable the watchdog when file is closed */ + + clear_bit(0, &at91wdt_busy); + return 0; +} + +/* + * Change the watchdog time interval. + */ +static int at91_wdt_settimeout(int new_time) +{ + /* + * All counting occurs at SLOW_CLOCK / 128 = 0.256 Hz + * + * Since WDV is a 16-bit counter, the maximum period is + * 65536 / 0.256 = 256 seconds. + */ + if ((new_time <= 0) || (new_time > WDT_MAX_TIME)) + return -EINVAL; + + /* Set new watchdog time. It will be used when at91_wdt_start() is called. */ + wdt_time = new_time; + return 0; +} + +static struct watchdog_info at91_wdt_info = { + .identity = "at91 watchdog", + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, +}; + +/* + * Handle commands from user-space. + */ +static int at91_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + int new_value; + + switch(cmd) { + case WDIOC_KEEPALIVE: + at91_wdt_reload(); /* pat the watchdog */ + return 0; + + case WDIOC_GETSUPPORT: + return copy_to_user(argp, &at91_wdt_info, sizeof(at91_wdt_info)) ? -EFAULT : 0; + + case WDIOC_SETTIMEOUT: + if (get_user(new_value, p)) + return -EFAULT; + + if (at91_wdt_settimeout(new_value)) + return -EINVAL; + + /* Enable new time value */ + at91_wdt_start(); + + /* Return current value */ + return put_user(wdt_time, p); + + case WDIOC_GETTIMEOUT: + return put_user(wdt_time, p); + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + + case WDIOC_SETOPTIONS: + if (get_user(new_value, p)) + return -EFAULT; + + if (new_value & WDIOS_DISABLECARD) + at91_wdt_stop(); + if (new_value & WDIOS_ENABLECARD) + at91_wdt_start(); + return 0; + + default: + return -ENOIOCTLCMD; + } +} + +/* + * Pat the watchdog whenever device is written to. + */ +static ssize_t at91_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ + at91_wdt_reload(); /* pat the watchdog */ + return len; +} + +/* ......................................................................... */ + +static struct file_operations at91wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = at91_wdt_ioctl, + .open = at91_wdt_open, + .release = at91_wdt_close, + .write = at91_wdt_write, +}; + +static struct miscdevice at91wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &at91wdt_fops, +}; + +static int __init at91_wdt_init(void) +{ + int res; + + /* Check that the heartbeat value is within range; if not reset to the default */ + if (at91_wdt_settimeout(wdt_time)) { + at91_wdt_settimeout(WDT_DEFAULT_TIME); + printk(KERN_INFO "at91_wdt: wdt_time value must be 1 <= wdt_time <= 256, using %d\n", wdt_time); + } + + res = misc_register(&at91wdt_miscdev); + if (res) + return res; + + printk("AT91 Watchdog Timer enabled (%d seconds, nowayout=%d)\n", wdt_time, nowayout); + return 0; +} + +static void __exit at91_wdt_exit(void) +{ + misc_deregister(&at91wdt_miscdev); +} + +module_init(at91_wdt_init); +module_exit(at91_wdt_exit); + +MODULE_AUTHOR("Andrew Victor"); +MODULE_DESCRIPTION("Watchdog driver for Atmel AT91RM9200"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/pcwd.c b/drivers/char/watchdog/pcwd.c index 8d6b249ad66b..6d44ca68312d 100644 --- a/drivers/char/watchdog/pcwd.c +++ b/drivers/char/watchdog/pcwd.c @@ -66,15 +66,13 @@ #include /* For file operations */ #include /* For io-port access */ #include /* For spin_lock/spin_unlock/... */ -#include /* TASK_INTERRUPTIBLE, set_current_state() and friends */ -#include /* For kmalloc */ #include /* For copy_to_user/put_user/... */ #include /* For inb/outb/... */ /* Module and version information */ -#define WATCHDOG_VERSION "1.16" -#define WATCHDOG_DATE "03 Jan 2006" +#define WATCHDOG_VERSION "1.17" +#define WATCHDOG_DATE "12 Feb 2006" #define WATCHDOG_DRIVER_NAME "ISA-PC Watchdog" #define WATCHDOG_NAME "pcwd" #define PFX WATCHDOG_NAME ": " @@ -96,15 +94,19 @@ * PCI-PC Watchdog card. */ /* Port 1 : Control Status #1 for the PC Watchdog card, revision A. */ -#define WD_WDRST 0x01 /* Previously reset state */ -#define WD_T110 0x02 /* Temperature overheat sense */ -#define WD_HRTBT 0x04 /* Heartbeat sense */ -#define WD_RLY2 0x08 /* External relay triggered */ -#define WD_SRLY2 0x80 /* Software external relay triggered */ +#define WD_WDRST 0x01 /* Previously reset state */ +#define WD_T110 0x02 /* Temperature overheat sense */ +#define WD_HRTBT 0x04 /* Heartbeat sense */ +#define WD_RLY2 0x08 /* External relay triggered */ +#define WD_SRLY2 0x80 /* Software external relay triggered */ /* Port 1 : Control Status #1 for the PC Watchdog card, revision C. */ -#define WD_REVC_WTRP 0x01 /* Watchdog Trip status */ -#define WD_REVC_HRBT 0x02 /* Watchdog Heartbeat */ -#define WD_REVC_TTRP 0x04 /* Temperature Trip status */ +#define WD_REVC_WTRP 0x01 /* Watchdog Trip status */ +#define WD_REVC_HRBT 0x02 /* Watchdog Heartbeat */ +#define WD_REVC_TTRP 0x04 /* Temperature Trip status */ +#define WD_REVC_RL2A 0x08 /* Relay 2 activated by on-board processor */ +#define WD_REVC_RL1A 0x10 /* Relay 1 active */ +#define WD_REVC_R2DS 0x40 /* Relay 2 disable */ +#define WD_REVC_RLY2 0x80 /* Relay 2 activated? */ /* Port 2 : Control Status #2 */ #define WD_WDIS 0x10 /* Watchdog Disabled */ #define WD_ENTP 0x20 /* Watchdog Enable Temperature Trip */ @@ -122,9 +124,14 @@ #define CMD_ISA_VERSION_HUNDRETH 0x03 #define CMD_ISA_VERSION_MINOR 0x04 #define CMD_ISA_SWITCH_SETTINGS 0x05 +#define CMD_ISA_RESET_PC 0x06 +#define CMD_ISA_ARM_0 0x07 +#define CMD_ISA_ARM_30 0x08 +#define CMD_ISA_ARM_60 0x09 #define CMD_ISA_DELAY_TIME_2SECS 0x0A #define CMD_ISA_DELAY_TIME_4SECS 0x0B #define CMD_ISA_DELAY_TIME_8SECS 0x0C +#define CMD_ISA_RESET_RELAYS 0x0D /* * We are using an kernel timer to do the pinging of the watchdog @@ -142,6 +149,7 @@ static atomic_t open_allowed = ATOMIC_INIT(1); static char expect_close; static int temp_panic; static struct { /* this is private data for each ISA-PC watchdog card */ + char fw_ver_str[6]; /* The cards firmware version */ int revision; /* The card's revision */ int supports_temp; /* Wether or not the card has a temperature device */ int command_mode; /* Wether or not the card is in command mode */ @@ -153,6 +161,13 @@ static struct { /* this is private data for each ISA-PC watchdog card */ } pcwd_private; /* module parameters */ +#define QUIET 0 /* Default */ +#define VERBOSE 1 /* Verbose */ +#define DEBUG 2 /* print fancy stuff too */ +static int debug = QUIET; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level: 0=Quiet, 1=Verbose, 2=Debug (default=0)"); + #define WATCHDOG_HEARTBEAT 60 /* 60 sec default heartbeat */ static int heartbeat = WATCHDOG_HEARTBEAT; module_param(heartbeat, int, 0); @@ -172,6 +187,10 @@ static int send_isa_command(int cmd) int control_status; int port0, last_port0; /* Double read for stabilising */ + if (debug >= DEBUG) + printk(KERN_DEBUG PFX "sending following data cmd=0x%02x\n", + cmd); + /* The WCMD bit must be 1 and the command is only 4 bits in size */ control_status = (cmd & 0x0F) | WD_WCMD; outb_p(control_status, pcwd_private.io_addr + 2); @@ -188,6 +207,10 @@ static int send_isa_command(int cmd) udelay (250); } + if (debug >= DEBUG) + printk(KERN_DEBUG PFX "received following data for cmd=0x%02x: port0=0x%02x last_port0=0x%02x\n", + cmd, port0, last_port0); + return port0; } @@ -214,6 +237,10 @@ static int set_command_mode(void) spin_unlock(&pcwd_private.io_lock); pcwd_private.command_mode = found; + if (debug >= DEBUG) + printk(KERN_DEBUG PFX "command_mode=%d\n", + pcwd_private.command_mode); + return(found); } @@ -226,6 +253,10 @@ static void unset_command_mode(void) spin_unlock(&pcwd_private.io_lock); pcwd_private.command_mode = 0; + + if (debug >= DEBUG) + printk(KERN_DEBUG PFX "command_mode=%d\n", + pcwd_private.command_mode); } static inline void pcwd_check_temperature_support(void) @@ -234,27 +265,22 @@ static inline void pcwd_check_temperature_support(void) pcwd_private.supports_temp = 1; } -static inline char *get_firmware(void) +static inline void pcwd_get_firmware(void) { int one, ten, hund, minor; - char *ret; - ret = kmalloc(6, GFP_KERNEL); - if(ret == NULL) - return NULL; + strcpy(pcwd_private.fw_ver_str, "ERROR"); if (set_command_mode()) { one = send_isa_command(CMD_ISA_VERSION_INTEGER); ten = send_isa_command(CMD_ISA_VERSION_TENTH); hund = send_isa_command(CMD_ISA_VERSION_HUNDRETH); minor = send_isa_command(CMD_ISA_VERSION_MINOR); - sprintf(ret, "%c.%c%c%c", one, ten, hund, minor); + sprintf(pcwd_private.fw_ver_str, "%c.%c%c%c", one, ten, hund, minor); } - else - sprintf(ret, "ERROR"); - unset_command_mode(); - return(ret); + + return; } static inline int pcwd_get_option_switches(void) @@ -272,17 +298,15 @@ static inline int pcwd_get_option_switches(void) static void pcwd_show_card_info(void) { - char *firmware; int option_switches; /* Get some extra info from the hardware (in command/debug/diag mode) */ if (pcwd_private.revision == PCWD_REVISION_A) printk(KERN_INFO PFX "ISA-PC Watchdog (REV.A) detected at port 0x%04x\n", pcwd_private.io_addr); else if (pcwd_private.revision == PCWD_REVISION_C) { - firmware = get_firmware(); + pcwd_get_firmware(); printk(KERN_INFO PFX "ISA-PC Watchdog (REV.C) detected at port 0x%04x (Firmware version: %s)\n", - pcwd_private.io_addr, firmware); - kfree(firmware); + pcwd_private.io_addr, pcwd_private.fw_ver_str); option_switches = pcwd_get_option_switches(); printk(KERN_INFO PFX "Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n", option_switches, @@ -362,6 +386,10 @@ static int pcwd_start(void) return -EIO; } } + + if (debug >= VERBOSE) + printk(KERN_DEBUG PFX "Watchdog started\n"); + return 0; } @@ -386,6 +414,10 @@ static int pcwd_stop(void) return -EIO; } } + + if (debug >= VERBOSE) + printk(KERN_DEBUG PFX "Watchdog stopped\n"); + return 0; } @@ -393,6 +425,10 @@ static int pcwd_keepalive(void) { /* user land ping */ pcwd_private.next_heartbeat = jiffies + (heartbeat * HZ); + + if (debug >= DEBUG) + printk(KERN_DEBUG PFX "Watchdog keepalive signal send\n"); + return 0; } @@ -402,12 +438,17 @@ static int pcwd_set_heartbeat(int t) return -EINVAL; heartbeat = t; + + if (debug >= VERBOSE) + printk(KERN_DEBUG PFX "New heartbeat: %d\n", + heartbeat); + return 0; } static int pcwd_get_status(int *status) { - int card_status; + int control_status; *status=0; spin_lock(&pcwd_private.io_lock); @@ -415,37 +456,39 @@ static int pcwd_get_status(int *status) /* Rev A cards return status information from * the base register, which is used for the * temperature in other cards. */ - card_status = inb(pcwd_private.io_addr); + control_status = inb(pcwd_private.io_addr); else { /* Rev C cards return card status in the base * address + 1 register. And use different bits * to indicate a card initiated reset, and an * over-temperature condition. And the reboot * status can be reset. */ - card_status = inb(pcwd_private.io_addr + 1); + control_status = inb(pcwd_private.io_addr + 1); } spin_unlock(&pcwd_private.io_lock); if (pcwd_private.revision == PCWD_REVISION_A) { - if (card_status & WD_WDRST) + if (control_status & WD_WDRST) *status |= WDIOF_CARDRESET; - if (card_status & WD_T110) { + if (control_status & WD_T110) { *status |= WDIOF_OVERHEAT; if (temp_panic) { printk (KERN_INFO PFX "Temperature overheat trip!\n"); kernel_power_off(); + /* or should we just do a: panic(PFX "Temperature overheat trip!\n"); */ } } } else { - if (card_status & WD_REVC_WTRP) + if (control_status & WD_REVC_WTRP) *status |= WDIOF_CARDRESET; - if (card_status & WD_REVC_TTRP) { + if (control_status & WD_REVC_TTRP) { *status |= WDIOF_OVERHEAT; if (temp_panic) { printk (KERN_INFO PFX "Temperature overheat trip!\n"); kernel_power_off(); + /* or should we just do a: panic(PFX "Temperature overheat trip!\n"); */ } } } @@ -455,9 +498,25 @@ static int pcwd_get_status(int *status) static int pcwd_clear_status(void) { + int control_status; + if (pcwd_private.revision == PCWD_REVISION_C) { spin_lock(&pcwd_private.io_lock); - outb_p(0x00, pcwd_private.io_addr + 1); /* clear reset status */ + + if (debug >= VERBOSE) + printk(KERN_INFO PFX "clearing watchdog trip status\n"); + + control_status = inb_p(pcwd_private.io_addr + 1); + + if (debug >= DEBUG) { + printk(KERN_DEBUG PFX "status was: 0x%02x\n", control_status); + printk(KERN_DEBUG PFX "sending: 0x%02x\n", + (control_status & WD_REVC_R2DS)); + } + + /* clear reset status & Keep Relay 2 disable state as it is */ + outb_p((control_status & WD_REVC_R2DS), pcwd_private.io_addr + 1); + spin_unlock(&pcwd_private.io_lock); } return 0; @@ -481,6 +540,11 @@ static int pcwd_get_temperature(int *temperature) *temperature = ((inb(pcwd_private.io_addr)) * 9 / 5) + 32; spin_unlock(&pcwd_private.io_lock); + if (debug >= DEBUG) { + printk(KERN_DEBUG PFX "temperature is: %d F\n", + *temperature); + } + return 0; } @@ -599,6 +663,8 @@ static ssize_t pcwd_write(struct file *file, const char __user *buf, size_t len, static int pcwd_open(struct inode *inode, struct file *file) { if (!atomic_dec_and_test(&open_allowed) ) { + if (debug >= VERBOSE) + printk(KERN_ERR PFX "Attempt to open already opened device.\n"); atomic_inc( &open_allowed ); return -EBUSY; } @@ -922,7 +988,8 @@ static void __exit pcwd_cleanup_module(void) { if (pcwd_private.io_addr) pcwatchdog_exit(); - return; + + printk(KERN_INFO PFX "Watchdog Module Unloaded.\n"); } module_init(pcwd_init_module); diff --git a/drivers/char/watchdog/pcwd_usb.c b/drivers/char/watchdog/pcwd_usb.c index 2700c5c45b8a..3fdfda9324fa 100644 --- a/drivers/char/watchdog/pcwd_usb.c +++ b/drivers/char/watchdog/pcwd_usb.c @@ -705,7 +705,8 @@ err_out_misc_deregister: err_out_unregister_reboot: unregister_reboot_notifier(&usb_pcwd_notifier); error: - usb_pcwd_delete (usb_pcwd); + if (usb_pcwd) + usb_pcwd_delete(usb_pcwd); usb_pcwd_device = NULL; return retval; } diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index b582d0cdc24f..4f0898400c6d 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -71,7 +71,7 @@ config EDAC_E7XXX config EDAC_E752X tristate "Intel e752x (e7520, e7525, e7320)" - depends on EDAC_MM_EDAC && PCI && X86 + depends on EDAC_MM_EDAC && PCI && X86 && HOTPLUG help Support for error detection and correction on the Intel E7520, E7525, E7320 server chipsets. diff --git a/drivers/hwmon/hdaps.c b/drivers/hwmon/hdaps.c index 7636c1a58f9c..1659f6c41458 100644 --- a/drivers/hwmon/hdaps.c +++ b/drivers/hwmon/hdaps.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #define HDAPS_LOW_PORT 0x1600 /* first port used by hdaps */ @@ -71,10 +70,10 @@ static u8 km_activity; static int rest_x; static int rest_y; -static DEFINE_MUTEX(hdaps_mutex); +static DECLARE_MUTEX(hdaps_sem); /* - * __get_latch - Get the value from a given port. Callers must hold hdaps_mutex. + * __get_latch - Get the value from a given port. Callers must hold hdaps_sem. */ static inline u8 __get_latch(u16 port) { @@ -83,7 +82,7 @@ static inline u8 __get_latch(u16 port) /* * __check_latch - Check a port latch for a given value. Returns zero if the - * port contains the given value. Callers must hold hdaps_mutex. + * port contains the given value. Callers must hold hdaps_sem. */ static inline int __check_latch(u16 port, u8 val) { @@ -94,7 +93,7 @@ static inline int __check_latch(u16 port, u8 val) /* * __wait_latch - Wait up to 100us for a port latch to get a certain value, - * returning zero if the value is obtained. Callers must hold hdaps_mutex. + * returning zero if the value is obtained. Callers must hold hdaps_sem. */ static int __wait_latch(u16 port, u8 val) { @@ -111,7 +110,7 @@ static int __wait_latch(u16 port, u8 val) /* * __device_refresh - request a refresh from the accelerometer. Does not wait - * for refresh to complete. Callers must hold hdaps_mutex. + * for refresh to complete. Callers must hold hdaps_sem. */ static void __device_refresh(void) { @@ -125,7 +124,7 @@ static void __device_refresh(void) /* * __device_refresh_sync - request a synchronous refresh from the * accelerometer. We wait for the refresh to complete. Returns zero if - * successful and nonzero on error. Callers must hold hdaps_mutex. + * successful and nonzero on error. Callers must hold hdaps_sem. */ static int __device_refresh_sync(void) { @@ -135,7 +134,7 @@ static int __device_refresh_sync(void) /* * __device_complete - indicate to the accelerometer that we are done reading - * data, and then initiate an async refresh. Callers must hold hdaps_mutex. + * data, and then initiate an async refresh. Callers must hold hdaps_sem. */ static inline void __device_complete(void) { @@ -153,7 +152,7 @@ static int hdaps_readb_one(unsigned int port, u8 *val) { int ret; - mutex_lock(&hdaps_mutex); + down(&hdaps_sem); /* do a sync refresh -- we need to be sure that we read fresh data */ ret = __device_refresh_sync(); @@ -164,7 +163,7 @@ static int hdaps_readb_one(unsigned int port, u8 *val) __device_complete(); out: - mutex_unlock(&hdaps_mutex); + up(&hdaps_sem); return ret; } @@ -199,9 +198,9 @@ static int hdaps_read_pair(unsigned int port1, unsigned int port2, { int ret; - mutex_lock(&hdaps_mutex); + down(&hdaps_sem); ret = __hdaps_read_pair(port1, port2, val1, val2); - mutex_unlock(&hdaps_mutex); + up(&hdaps_sem); return ret; } @@ -214,7 +213,7 @@ static int hdaps_device_init(void) { int total, ret = -ENXIO; - mutex_lock(&hdaps_mutex); + down(&hdaps_sem); outb(0x13, 0x1610); outb(0x01, 0x161f); @@ -280,7 +279,7 @@ static int hdaps_device_init(void) } out: - mutex_unlock(&hdaps_mutex); + up(&hdaps_sem); return ret; } @@ -314,7 +313,7 @@ static struct platform_driver hdaps_driver = { }; /* - * hdaps_calibrate - Set our "resting" values. Callers must hold hdaps_mutex. + * hdaps_calibrate - Set our "resting" values. Callers must hold hdaps_sem. */ static void hdaps_calibrate(void) { @@ -326,7 +325,7 @@ static void hdaps_mousedev_poll(unsigned long unused) int x, y; /* Cannot sleep. Try nonblockingly. If we fail, try again later. */ - if (!mutex_trylock(&hdaps_mutex)) { + if (down_trylock(&hdaps_sem)) { mod_timer(&hdaps_timer,jiffies + HDAPS_POLL_PERIOD); return; } @@ -341,7 +340,7 @@ static void hdaps_mousedev_poll(unsigned long unused) mod_timer(&hdaps_timer, jiffies + HDAPS_POLL_PERIOD); out: - mutex_unlock(&hdaps_mutex); + up(&hdaps_sem); } @@ -421,9 +420,9 @@ static ssize_t hdaps_calibrate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - mutex_lock(&hdaps_mutex); + down(&hdaps_sem); hdaps_calibrate(); - mutex_unlock(&hdaps_mutex); + up(&hdaps_sem); return count; } @@ -510,12 +509,22 @@ static int hdaps_dmi_match_invert(struct dmi_system_id *id) } \ } +#define HDAPS_DMI_MATCH_LENOVO(model) { \ + .ident = "Lenovo " model, \ + .callback = hdaps_dmi_match_invert, \ + .matches = { \ + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), \ + DMI_MATCH(DMI_PRODUCT_VERSION, model) \ + } \ +} + static int __init hdaps_init(void) { int ret; /* Note that DMI_MATCH(...,"ThinkPad T42") will match "ThinkPad T42p" */ struct dmi_system_id hdaps_whitelist[] = { + HDAPS_DMI_MATCH_NORMAL("ThinkPad H"), HDAPS_DMI_MATCH_INVERT("ThinkPad R50p"), HDAPS_DMI_MATCH_NORMAL("ThinkPad R50"), HDAPS_DMI_MATCH_NORMAL("ThinkPad R51"), @@ -525,15 +534,17 @@ static int __init hdaps_init(void) HDAPS_DMI_MATCH_INVERT("ThinkPad T42p"), HDAPS_DMI_MATCH_NORMAL("ThinkPad T42"), HDAPS_DMI_MATCH_NORMAL("ThinkPad T43"), + HDAPS_DMI_MATCH_LENOVO("ThinkPad T60p"), HDAPS_DMI_MATCH_NORMAL("ThinkPad X40"), HDAPS_DMI_MATCH_NORMAL("ThinkPad X41 Tablet"), HDAPS_DMI_MATCH_NORMAL("ThinkPad X41"), + HDAPS_DMI_MATCH_LENOVO("ThinkPad X60"), { .ident = NULL } }; if (!dmi_check_system(hdaps_whitelist)) { printk(KERN_WARNING "hdaps: supported laptop not found!\n"); - ret = -ENXIO; + ret = -ENODEV; goto out; } diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index ccf528d733bf..a5017de72da5 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -61,6 +61,7 @@ #include #include #include +#include #define _IDE_DISK @@ -317,6 +318,8 @@ static ide_startstop_t ide_do_rw_disk (ide_drive_t *drive, struct request *rq, s return ide_stopped; } + ledtrig_ide_activity(); + pr_debug("%s: %sing: block=%llu, sectors=%lu, buffer=0x%08lx\n", drive->name, rq_data_dir(rq) == READ ? "read" : "writ", (unsigned long long)block, rq->nr_sectors, diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c index 0606bd2f6020..9233b8109a0f 100644 --- a/drivers/ide/ide-taskfile.c +++ b/drivers/ide/ide-taskfile.c @@ -375,7 +375,13 @@ static void task_end_request(ide_drive_t *drive, struct request *rq, u8 stat) } } - ide_end_request(drive, 1, rq->hard_nr_sectors); + if (rq->rq_disk) { + ide_driver_t *drv; + + drv = *(ide_driver_t **)rq->rq_disk->private_data;; + drv->end_request(drive, 1, rq->hard_nr_sectors); + } else + ide_end_request(drive, 1, rq->hard_nr_sectors); } /* diff --git a/drivers/ide/legacy/ide-cs.c b/drivers/ide/legacy/ide-cs.c index 6213bd3caee5..4961f1e764a7 100644 --- a/drivers/ide/legacy/ide-cs.c +++ b/drivers/ide/legacy/ide-cs.c @@ -81,14 +81,14 @@ static const char ide_major[] = { }; typedef struct ide_info_t { - dev_link_t link; + struct pcmcia_device *p_dev; int ndev; dev_node_t node; int hd; } ide_info_t; -static void ide_release(dev_link_t *); -static void ide_config(dev_link_t *); +static void ide_release(struct pcmcia_device *); +static int ide_config(struct pcmcia_device *); static void ide_detach(struct pcmcia_device *p_dev); @@ -103,10 +103,9 @@ static void ide_detach(struct pcmcia_device *p_dev); ======================================================================*/ -static int ide_attach(struct pcmcia_device *p_dev) +static int ide_probe(struct pcmcia_device *link) { ide_info_t *info; - dev_link_t *link; DEBUG(0, "ide_attach()\n"); @@ -114,7 +113,9 @@ static int ide_attach(struct pcmcia_device *p_dev) info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; - link = &info->link; link->priv = info; + + info->p_dev = link; + link->priv = info; link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; link->io.Attributes2 = IO_DATA_PATH_WIDTH_8; @@ -122,16 +123,9 @@ static int ide_attach(struct pcmcia_device *p_dev) link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - ide_config(link); - - return 0; + return ide_config(link); } /* ide_attach */ /*====================================================================== @@ -143,14 +137,11 @@ static int ide_attach(struct pcmcia_device *p_dev) ======================================================================*/ -static void ide_detach(struct pcmcia_device *p_dev) +static void ide_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); - DEBUG(0, "ide_detach(0x%p)\n", link); - if (link->state & DEV_CONFIG) - ide_release(link); + ide_release(link); kfree(link->priv); } /* ide_detach */ @@ -177,9 +168,8 @@ static int idecs_register(unsigned long io, unsigned long ctl, unsigned long irq #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) -static void ide_config(dev_link_t *link) +static int ide_config(struct pcmcia_device *link) { - client_handle_t handle = link->handle; ide_info_t *info = link->priv; tuple_t tuple; struct { @@ -203,34 +193,30 @@ static void ide_config(dev_link_t *link) tuple.TupleDataMax = 255; tuple.Attributes = 0; tuple.DesiredTuple = CISTPL_CONFIG; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &stk->parse)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &stk->parse)); link->conf.ConfigBase = stk->parse.config.base; link->conf.Present = stk->parse.config.rmask[0]; tuple.DesiredTuple = CISTPL_MANFID; - if (!pcmcia_get_first_tuple(handle, &tuple) && - !pcmcia_get_tuple_data(handle, &tuple) && - !pcmcia_parse_tuple(handle, &tuple, &stk->parse)) + if (!pcmcia_get_first_tuple(link, &tuple) && + !pcmcia_get_tuple_data(link, &tuple) && + !pcmcia_parse_tuple(link, &tuple, &stk->parse)) is_kme = ((stk->parse.manfid.manf == MANFID_KME) && ((stk->parse.manfid.card == PRODID_KME_KXLC005_A) || (stk->parse.manfid.card == PRODID_KME_KXLC005_B))); - /* Configure card */ - link->state |= DEV_CONFIG; - /* Not sure if this is right... look up the current Vcc */ - CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &stk->conf)); - link->conf.Vcc = stk->conf.Vcc; + CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(link, &stk->conf)); pass = io_base = ctl_base = 0; tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; tuple.Attributes = 0; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); while (1) { - if (pcmcia_get_tuple_data(handle, &tuple) != 0) goto next_entry; - if (pcmcia_parse_tuple(handle, &tuple, &stk->parse) != 0) goto next_entry; + if (pcmcia_get_tuple_data(link, &tuple) != 0) goto next_entry; + if (pcmcia_parse_tuple(link, &tuple, &stk->parse) != 0) goto next_entry; /* Check for matching Vcc, unless we're desperate */ if (!pass) { @@ -244,10 +230,10 @@ static void ide_config(dev_link_t *link) } if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM)) - link->conf.Vpp1 = link->conf.Vpp2 = + link->conf.Vpp = cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000; else if (stk->dflt.vpp1.present & (1 << CISTPL_POWER_VNOM)) - link->conf.Vpp1 = link->conf.Vpp2 = + link->conf.Vpp = stk->dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000; if ((cfg->io.nwin > 0) || (stk->dflt.io.nwin > 0)) { @@ -261,14 +247,14 @@ static void ide_config(dev_link_t *link) link->io.NumPorts1 = 8; link->io.BasePort2 = io->win[1].base; link->io.NumPorts2 = (is_kme) ? 2 : 1; - if (pcmcia_request_io(link->handle, &link->io) != 0) + if (pcmcia_request_io(link, &link->io) != 0) goto next_entry; io_base = link->io.BasePort1; ctl_base = link->io.BasePort2; } else if ((io->nwin == 1) && (io->win[0].len >= 16)) { link->io.NumPorts1 = io->win[0].len; link->io.NumPorts2 = 0; - if (pcmcia_request_io(link->handle, &link->io) != 0) + if (pcmcia_request_io(link, &link->io) != 0) goto next_entry; io_base = link->io.BasePort1; ctl_base = link->io.BasePort1 + 0x0e; @@ -281,16 +267,16 @@ static void ide_config(dev_link_t *link) if (cfg->flags & CISTPL_CFTABLE_DEFAULT) memcpy(&stk->dflt, cfg, sizeof(stk->dflt)); if (pass) { - CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple)); - } else if (pcmcia_get_next_tuple(handle, &tuple) != 0) { - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple)); + } else if (pcmcia_get_next_tuple(link, &tuple) != 0) { + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); memset(&stk->dflt, 0, sizeof(stk->dflt)); pass++; } } - CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq)); - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); /* disable drive interrupts during IDE probe */ outb(0x02, ctl_base); @@ -301,12 +287,12 @@ static void ide_config(dev_link_t *link) /* retry registration in case device is still spinning up */ for (hd = -1, i = 0; i < 10; i++) { - hd = idecs_register(io_base, ctl_base, link->irq.AssignedIRQ, handle); + hd = idecs_register(io_base, ctl_base, link->irq.AssignedIRQ, link); if (hd >= 0) break; if (link->io.NumPorts1 == 0x20) { outb(0x02, ctl_base + 0x10); hd = idecs_register(io_base + 0x10, ctl_base + 0x10, - link->irq.AssignedIRQ, handle); + link->irq.AssignedIRQ, link); if (hd >= 0) { io_base += 0x10; ctl_base += 0x10; @@ -328,25 +314,23 @@ static void ide_config(dev_link_t *link) info->node.major = ide_major[hd]; info->node.minor = 0; info->hd = hd; - link->dev = &info->node; - printk(KERN_INFO "ide-cs: %s: Vcc = %d.%d, Vpp = %d.%d\n", - info->node.dev_name, link->conf.Vcc / 10, link->conf.Vcc % 10, - link->conf.Vpp1 / 10, link->conf.Vpp1 % 10); + link->dev_node = &info->node; + printk(KERN_INFO "ide-cs: %s: Vpp = %d.%d\n", + info->node.dev_name, link->conf.Vpp / 10, link->conf.Vpp % 10); - link->state &= ~DEV_CONFIG_PENDING; kfree(stk); - return; + return 0; err_mem: printk(KERN_NOTICE "ide-cs: ide_config failed memory allocation\n"); goto failed; cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); failed: kfree(stk); ide_release(link); - link->state &= ~DEV_CONFIG_PENDING; + return -ENODEV; } /* ide_config */ /*====================================================================== @@ -357,7 +341,7 @@ failed: ======================================================================*/ -void ide_release(dev_link_t *link) +void ide_release(struct pcmcia_device *link) { ide_info_t *info = link->priv; @@ -369,37 +353,10 @@ void ide_release(dev_link_t *link) ide_unregister(info->hd); } info->ndev = 0; - link->dev = NULL; - - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - - link->state &= ~DEV_CONFIG; + pcmcia_disable_device(link); } /* ide_release */ -static int ide_suspend(struct pcmcia_device *dev) -{ - dev_link_t *link = dev_to_instance(dev); - - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) - pcmcia_release_configuration(link->handle); - - return 0; -} - -static int ide_resume(struct pcmcia_device *dev) -{ - dev_link_t *link = dev_to_instance(dev); - - link->state &= ~DEV_SUSPEND; - if (DEV_OK(link)) - pcmcia_request_configuration(link->handle, &link->conf); - - return 0; -} /*====================================================================== @@ -459,11 +416,9 @@ static struct pcmcia_driver ide_cs_driver = { .drv = { .name = "ide-cs", }, - .probe = ide_attach, + .probe = ide_probe, .remove = ide_detach, .id_table = ide_ids, - .suspend = ide_suspend, - .resume = ide_resume, }; static int __init init_ide_cs(void) diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c index 2c765ca5aa50..f4206604db03 100644 --- a/drivers/ieee1394/sbp2.c +++ b/drivers/ieee1394/sbp2.c @@ -496,22 +496,17 @@ static struct sbp2_command_info *sbp2util_find_command_for_orb( /* * This function finds the sbp2_command for a given outstanding SCpnt. * Only looks at the inuse list. + * Must be called with scsi_id->sbp2_command_orb_lock held. */ -static struct sbp2_command_info *sbp2util_find_command_for_SCpnt(struct scsi_id_instance_data *scsi_id, void *SCpnt) +static struct sbp2_command_info *sbp2util_find_command_for_SCpnt( + struct scsi_id_instance_data *scsi_id, void *SCpnt) { struct sbp2_command_info *command; - unsigned long flags; - spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags); - if (!list_empty(&scsi_id->sbp2_command_orb_inuse)) { - list_for_each_entry(command, &scsi_id->sbp2_command_orb_inuse, list) { - if (command->Current_SCpnt == SCpnt) { - spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags); + if (!list_empty(&scsi_id->sbp2_command_orb_inuse)) + list_for_each_entry(command, &scsi_id->sbp2_command_orb_inuse, list) + if (command->Current_SCpnt == SCpnt) return command; - } - } - } - spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags); return NULL; } @@ -580,17 +575,15 @@ static void sbp2util_free_command_dma(struct sbp2_command_info *command) /* * This function moves a command to the completed orb list. + * Must be called with scsi_id->sbp2_command_orb_lock held. */ -static void sbp2util_mark_command_completed(struct scsi_id_instance_data *scsi_id, - struct sbp2_command_info *command) +static void sbp2util_mark_command_completed( + struct scsi_id_instance_data *scsi_id, + struct sbp2_command_info *command) { - unsigned long flags; - - spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags); list_del(&command->list); sbp2util_free_command_dma(command); list_add_tail(&command->list, &scsi_id->sbp2_command_orb_completed); - spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags); } /* @@ -2148,7 +2141,9 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int dest * Matched status with command, now grab scsi command pointers and check status */ SCpnt = command->Current_SCpnt; + spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags); sbp2util_mark_command_completed(scsi_id, command); + spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags); if (SCpnt) { @@ -2484,6 +2479,7 @@ static int sbp2scsi_abort(struct scsi_cmnd *SCpnt) (struct scsi_id_instance_data *)SCpnt->device->host->hostdata[0]; struct sbp2scsi_host_info *hi = scsi_id->hi; struct sbp2_command_info *command; + unsigned long flags; SBP2_ERR("aborting sbp2 command"); scsi_print_command(SCpnt); @@ -2494,6 +2490,7 @@ static int sbp2scsi_abort(struct scsi_cmnd *SCpnt) * Right now, just return any matching command structures * to the free pool. */ + spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags); command = sbp2util_find_command_for_SCpnt(scsi_id, SCpnt); if (command) { SBP2_DEBUG("Found command to abort"); @@ -2511,6 +2508,7 @@ static int sbp2scsi_abort(struct scsi_cmnd *SCpnt) command->Current_done(command->Current_SCpnt); } } + spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags); /* * Initiate a fetch agent reset. diff --git a/drivers/infiniband/Kconfig b/drivers/infiniband/Kconfig index bdf0891a92dd..afc612b8577d 100644 --- a/drivers/infiniband/Kconfig +++ b/drivers/infiniband/Kconfig @@ -30,6 +30,7 @@ config INFINIBAND_USER_ACCESS . source "drivers/infiniband/hw/mthca/Kconfig" +source "drivers/infiniband/hw/ipath/Kconfig" source "drivers/infiniband/ulp/ipoib/Kconfig" diff --git a/drivers/infiniband/Makefile b/drivers/infiniband/Makefile index a43fb34cca94..eea27322a22d 100644 --- a/drivers/infiniband/Makefile +++ b/drivers/infiniband/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_INFINIBAND) += core/ obj-$(CONFIG_INFINIBAND_MTHCA) += hw/mthca/ +obj-$(CONFIG_IPATH_CORE) += hw/ipath/ obj-$(CONFIG_INFINIBAND_IPOIB) += ulp/ipoib/ obj-$(CONFIG_INFINIBAND_SRP) += ulp/srp/ diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c index f7854b65fd55..ba54c856b0e5 100644 --- a/drivers/infiniband/core/mad.c +++ b/drivers/infiniband/core/mad.c @@ -227,6 +227,14 @@ struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device, if (!is_vendor_oui(mad_reg_req->oui)) goto error1; } + /* Make sure class supplied is consistent with RMPP */ + if (ib_is_mad_class_rmpp(mad_reg_req->mgmt_class)) { + if (!rmpp_version) + goto error1; + } else { + if (rmpp_version) + goto error1; + } /* Make sure class supplied is consistent with QP type */ if (qp_type == IB_QPT_SMI) { if ((mad_reg_req->mgmt_class != @@ -890,6 +898,35 @@ struct ib_mad_send_buf * ib_create_send_mad(struct ib_mad_agent *mad_agent, } EXPORT_SYMBOL(ib_create_send_mad); +int ib_get_mad_data_offset(u8 mgmt_class) +{ + if (mgmt_class == IB_MGMT_CLASS_SUBN_ADM) + return IB_MGMT_SA_HDR; + else if ((mgmt_class == IB_MGMT_CLASS_DEVICE_MGMT) || + (mgmt_class == IB_MGMT_CLASS_DEVICE_ADM) || + (mgmt_class == IB_MGMT_CLASS_BIS)) + return IB_MGMT_DEVICE_HDR; + else if ((mgmt_class >= IB_MGMT_CLASS_VENDOR_RANGE2_START) && + (mgmt_class <= IB_MGMT_CLASS_VENDOR_RANGE2_END)) + return IB_MGMT_VENDOR_HDR; + else + return IB_MGMT_MAD_HDR; +} +EXPORT_SYMBOL(ib_get_mad_data_offset); + +int ib_is_mad_class_rmpp(u8 mgmt_class) +{ + if ((mgmt_class == IB_MGMT_CLASS_SUBN_ADM) || + (mgmt_class == IB_MGMT_CLASS_DEVICE_MGMT) || + (mgmt_class == IB_MGMT_CLASS_DEVICE_ADM) || + (mgmt_class == IB_MGMT_CLASS_BIS) || + ((mgmt_class >= IB_MGMT_CLASS_VENDOR_RANGE2_START) && + (mgmt_class <= IB_MGMT_CLASS_VENDOR_RANGE2_END))) + return 1; + return 0; +} +EXPORT_SYMBOL(ib_is_mad_class_rmpp); + void *ib_get_rmpp_segment(struct ib_mad_send_buf *send_buf, int seg_num) { struct ib_mad_send_wr_private *mad_send_wr; @@ -1022,6 +1059,13 @@ int ib_post_send_mad(struct ib_mad_send_buf *send_buf, goto error; } + if (!ib_is_mad_class_rmpp(((struct ib_mad_hdr *) send_buf->mad)->mgmt_class)) { + if (mad_agent_priv->agent.rmpp_version) { + ret = -EINVAL; + goto error; + } + } + /* * Save pointer to next work request to post in case the * current one completes, and the user modifies the work @@ -1618,14 +1662,59 @@ static int is_data_mad(struct ib_mad_agent_private *mad_agent_priv, (rmpp_mad->rmpp_hdr.rmpp_type == IB_MGMT_RMPP_TYPE_DATA); } +static inline int rcv_has_same_class(struct ib_mad_send_wr_private *wr, + struct ib_mad_recv_wc *rwc) +{ + return ((struct ib_mad *)(wr->send_buf.mad))->mad_hdr.mgmt_class == + rwc->recv_buf.mad->mad_hdr.mgmt_class; +} + +static inline int rcv_has_same_gid(struct ib_mad_send_wr_private *wr, + struct ib_mad_recv_wc *rwc ) +{ + struct ib_ah_attr attr; + u8 send_resp, rcv_resp; + + send_resp = ((struct ib_mad *)(wr->send_buf.mad))-> + mad_hdr.method & IB_MGMT_METHOD_RESP; + rcv_resp = rwc->recv_buf.mad->mad_hdr.method & IB_MGMT_METHOD_RESP; + + if (!send_resp && rcv_resp) + /* is request/response. GID/LIDs are both local (same). */ + return 1; + + if (send_resp == rcv_resp) + /* both requests, or both responses. GIDs different */ + return 0; + + if (ib_query_ah(wr->send_buf.ah, &attr)) + /* Assume not equal, to avoid false positives. */ + return 0; + + if (!(attr.ah_flags & IB_AH_GRH) && !(rwc->wc->wc_flags & IB_WC_GRH)) + return attr.dlid == rwc->wc->slid; + else if ((attr.ah_flags & IB_AH_GRH) && + (rwc->wc->wc_flags & IB_WC_GRH)) + return memcmp(attr.grh.dgid.raw, + rwc->recv_buf.grh->sgid.raw, 16) == 0; + else + /* one has GID, other does not. Assume different */ + return 0; +} struct ib_mad_send_wr_private* -ib_find_send_mad(struct ib_mad_agent_private *mad_agent_priv, __be64 tid) +ib_find_send_mad(struct ib_mad_agent_private *mad_agent_priv, + struct ib_mad_recv_wc *mad_recv_wc) { struct ib_mad_send_wr_private *mad_send_wr; + struct ib_mad *mad; + + mad = (struct ib_mad *)mad_recv_wc->recv_buf.mad; list_for_each_entry(mad_send_wr, &mad_agent_priv->wait_list, agent_list) { - if (mad_send_wr->tid == tid) + if ((mad_send_wr->tid == mad->mad_hdr.tid) && + rcv_has_same_class(mad_send_wr, mad_recv_wc) && + rcv_has_same_gid(mad_send_wr, mad_recv_wc)) return mad_send_wr; } @@ -1636,7 +1725,10 @@ ib_find_send_mad(struct ib_mad_agent_private *mad_agent_priv, __be64 tid) list_for_each_entry(mad_send_wr, &mad_agent_priv->send_list, agent_list) { if (is_data_mad(mad_agent_priv, mad_send_wr->send_buf.mad) && - mad_send_wr->tid == tid && mad_send_wr->timeout) { + mad_send_wr->tid == mad->mad_hdr.tid && + mad_send_wr->timeout && + rcv_has_same_class(mad_send_wr, mad_recv_wc) && + rcv_has_same_gid(mad_send_wr, mad_recv_wc)) { /* Verify request has not been canceled */ return (mad_send_wr->status == IB_WC_SUCCESS) ? mad_send_wr : NULL; @@ -1661,7 +1753,6 @@ static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv, struct ib_mad_send_wr_private *mad_send_wr; struct ib_mad_send_wc mad_send_wc; unsigned long flags; - __be64 tid; INIT_LIST_HEAD(&mad_recv_wc->rmpp_list); list_add(&mad_recv_wc->recv_buf.list, &mad_recv_wc->rmpp_list); @@ -1677,9 +1768,8 @@ static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv, /* Complete corresponding request */ if (response_mad(mad_recv_wc->recv_buf.mad)) { - tid = mad_recv_wc->recv_buf.mad->mad_hdr.tid; spin_lock_irqsave(&mad_agent_priv->lock, flags); - mad_send_wr = ib_find_send_mad(mad_agent_priv, tid); + mad_send_wr = ib_find_send_mad(mad_agent_priv, mad_recv_wc); if (!mad_send_wr) { spin_unlock_irqrestore(&mad_agent_priv->lock, flags); ib_free_recv_mad(mad_recv_wc); @@ -2408,11 +2498,11 @@ static int ib_mad_post_receive_mads(struct ib_mad_qp_info *qp_info, } } sg_list.addr = dma_map_single(qp_info->port_priv-> - device->dma_device, - &mad_priv->grh, - sizeof *mad_priv - - sizeof mad_priv->header, - DMA_FROM_DEVICE); + device->dma_device, + &mad_priv->grh, + sizeof *mad_priv - + sizeof mad_priv->header, + DMA_FROM_DEVICE); pci_unmap_addr_set(&mad_priv->header, mapping, sg_list.addr); recv_wr.wr_id = (unsigned long)&mad_priv->header.mad_list; mad_priv->header.mad_list.mad_queue = recv_queue; diff --git a/drivers/infiniband/core/mad_priv.h b/drivers/infiniband/core/mad_priv.h index a7125d4b5ccf..6c9c133d71ef 100644 --- a/drivers/infiniband/core/mad_priv.h +++ b/drivers/infiniband/core/mad_priv.h @@ -216,7 +216,8 @@ extern kmem_cache_t *ib_mad_cache; int ib_send_mad(struct ib_mad_send_wr_private *mad_send_wr); struct ib_mad_send_wr_private * -ib_find_send_mad(struct ib_mad_agent_private *mad_agent_priv, __be64 tid); +ib_find_send_mad(struct ib_mad_agent_private *mad_agent_priv, + struct ib_mad_recv_wc *mad_recv_wc); void ib_mad_complete_send_wr(struct ib_mad_send_wr_private *mad_send_wr, struct ib_mad_send_wc *mad_send_wc); diff --git a/drivers/infiniband/core/mad_rmpp.c b/drivers/infiniband/core/mad_rmpp.c index bacfdd5bddad..dfd4e588ce03 100644 --- a/drivers/infiniband/core/mad_rmpp.c +++ b/drivers/infiniband/core/mad_rmpp.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2005 Intel Inc. All rights reserved. - * Copyright (c) 2005 Voltaire, Inc. All rights reserved. + * Copyright (c) 2005-2006 Voltaire, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -100,17 +100,6 @@ void ib_cancel_rmpp_recvs(struct ib_mad_agent_private *agent) } } -static int data_offset(u8 mgmt_class) -{ - if (mgmt_class == IB_MGMT_CLASS_SUBN_ADM) - return IB_MGMT_SA_HDR; - else if ((mgmt_class >= IB_MGMT_CLASS_VENDOR_RANGE2_START) && - (mgmt_class <= IB_MGMT_CLASS_VENDOR_RANGE2_END)) - return IB_MGMT_VENDOR_HDR; - else - return IB_MGMT_RMPP_HDR; -} - static void format_ack(struct ib_mad_send_buf *msg, struct ib_rmpp_mad *data, struct mad_rmpp_recv *rmpp_recv) @@ -137,7 +126,7 @@ static void ack_recv(struct mad_rmpp_recv *rmpp_recv, struct ib_mad_send_buf *msg; int ret, hdr_len; - hdr_len = data_offset(recv_wc->recv_buf.mad->mad_hdr.mgmt_class); + hdr_len = ib_get_mad_data_offset(recv_wc->recv_buf.mad->mad_hdr.mgmt_class); msg = ib_create_send_mad(&rmpp_recv->agent->agent, recv_wc->wc->src_qp, recv_wc->wc->pkey_index, 1, hdr_len, 0, GFP_KERNEL); @@ -163,7 +152,7 @@ static struct ib_mad_send_buf *alloc_response_msg(struct ib_mad_agent *agent, if (IS_ERR(ah)) return (void *) ah; - hdr_len = data_offset(recv_wc->recv_buf.mad->mad_hdr.mgmt_class); + hdr_len = ib_get_mad_data_offset(recv_wc->recv_buf.mad->mad_hdr.mgmt_class); msg = ib_create_send_mad(agent, recv_wc->wc->src_qp, recv_wc->wc->pkey_index, 1, hdr_len, 0, GFP_KERNEL); @@ -408,7 +397,7 @@ static inline int get_mad_len(struct mad_rmpp_recv *rmpp_recv) rmpp_mad = (struct ib_rmpp_mad *)rmpp_recv->cur_seg_buf->mad; - hdr_size = data_offset(rmpp_mad->mad_hdr.mgmt_class); + hdr_size = ib_get_mad_data_offset(rmpp_mad->mad_hdr.mgmt_class); data_size = sizeof(struct ib_rmpp_mad) - hdr_size; pad = IB_MGMT_RMPP_DATA - be32_to_cpu(rmpp_mad->rmpp_hdr.paylen_newwin); if (pad > IB_MGMT_RMPP_DATA || pad < 0) @@ -562,15 +551,15 @@ static int send_next_seg(struct ib_mad_send_wr_private *mad_send_wr) return ib_send_mad(mad_send_wr); } -static void abort_send(struct ib_mad_agent_private *agent, __be64 tid, - u8 rmpp_status) +static void abort_send(struct ib_mad_agent_private *agent, + struct ib_mad_recv_wc *mad_recv_wc, u8 rmpp_status) { struct ib_mad_send_wr_private *mad_send_wr; struct ib_mad_send_wc wc; unsigned long flags; spin_lock_irqsave(&agent->lock, flags); - mad_send_wr = ib_find_send_mad(agent, tid); + mad_send_wr = ib_find_send_mad(agent, mad_recv_wc); if (!mad_send_wr) goto out; /* Unmatched send */ @@ -612,8 +601,7 @@ static void process_rmpp_ack(struct ib_mad_agent_private *agent, rmpp_mad = (struct ib_rmpp_mad *)mad_recv_wc->recv_buf.mad; if (rmpp_mad->rmpp_hdr.rmpp_status) { - abort_send(agent, rmpp_mad->mad_hdr.tid, - IB_MGMT_RMPP_STATUS_BAD_STATUS); + abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BAD_STATUS); nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BAD_STATUS); return; } @@ -621,14 +609,13 @@ static void process_rmpp_ack(struct ib_mad_agent_private *agent, seg_num = be32_to_cpu(rmpp_mad->rmpp_hdr.seg_num); newwin = be32_to_cpu(rmpp_mad->rmpp_hdr.paylen_newwin); if (newwin < seg_num) { - abort_send(agent, rmpp_mad->mad_hdr.tid, - IB_MGMT_RMPP_STATUS_W2S); + abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_W2S); nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_W2S); return; } spin_lock_irqsave(&agent->lock, flags); - mad_send_wr = ib_find_send_mad(agent, rmpp_mad->mad_hdr.tid); + mad_send_wr = ib_find_send_mad(agent, mad_recv_wc); if (!mad_send_wr) goto out; /* Unmatched ACK */ @@ -639,8 +626,7 @@ static void process_rmpp_ack(struct ib_mad_agent_private *agent, if (seg_num > mad_send_wr->send_buf.seg_count || seg_num > mad_send_wr->newwin) { spin_unlock_irqrestore(&agent->lock, flags); - abort_send(agent, rmpp_mad->mad_hdr.tid, - IB_MGMT_RMPP_STATUS_S2B); + abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_S2B); nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_S2B); return; } @@ -728,12 +714,10 @@ static void process_rmpp_stop(struct ib_mad_agent_private *agent, rmpp_mad = (struct ib_rmpp_mad *)mad_recv_wc->recv_buf.mad; if (rmpp_mad->rmpp_hdr.rmpp_status != IB_MGMT_RMPP_STATUS_RESX) { - abort_send(agent, rmpp_mad->mad_hdr.tid, - IB_MGMT_RMPP_STATUS_BAD_STATUS); + abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BAD_STATUS); nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BAD_STATUS); } else - abort_send(agent, rmpp_mad->mad_hdr.tid, - rmpp_mad->rmpp_hdr.rmpp_status); + abort_send(agent, mad_recv_wc, rmpp_mad->rmpp_hdr.rmpp_status); } static void process_rmpp_abort(struct ib_mad_agent_private *agent, @@ -745,12 +729,10 @@ static void process_rmpp_abort(struct ib_mad_agent_private *agent, if (rmpp_mad->rmpp_hdr.rmpp_status < IB_MGMT_RMPP_STATUS_ABORT_MIN || rmpp_mad->rmpp_hdr.rmpp_status > IB_MGMT_RMPP_STATUS_ABORT_MAX) { - abort_send(agent, rmpp_mad->mad_hdr.tid, - IB_MGMT_RMPP_STATUS_BAD_STATUS); + abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BAD_STATUS); nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BAD_STATUS); } else - abort_send(agent, rmpp_mad->mad_hdr.tid, - rmpp_mad->rmpp_hdr.rmpp_status); + abort_send(agent, mad_recv_wc, rmpp_mad->rmpp_hdr.rmpp_status); } struct ib_mad_recv_wc * @@ -764,8 +746,7 @@ ib_process_rmpp_recv_wc(struct ib_mad_agent_private *agent, return mad_recv_wc; if (rmpp_mad->rmpp_hdr.rmpp_version != IB_MGMT_RMPP_VERSION) { - abort_send(agent, rmpp_mad->mad_hdr.tid, - IB_MGMT_RMPP_STATUS_UNV); + abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_UNV); nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_UNV); goto out; } @@ -783,8 +764,7 @@ ib_process_rmpp_recv_wc(struct ib_mad_agent_private *agent, process_rmpp_abort(agent, mad_recv_wc); break; default: - abort_send(agent, rmpp_mad->mad_hdr.tid, - IB_MGMT_RMPP_STATUS_BADT); + abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BADT); nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BADT); break; } diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c index fb6cd42601f9..afe70a549c2f 100644 --- a/drivers/infiniband/core/user_mad.c +++ b/drivers/infiniband/core/user_mad.c @@ -177,17 +177,6 @@ static int queue_packet(struct ib_umad_file *file, return ret; } -static int data_offset(u8 mgmt_class) -{ - if (mgmt_class == IB_MGMT_CLASS_SUBN_ADM) - return IB_MGMT_SA_HDR; - else if ((mgmt_class >= IB_MGMT_CLASS_VENDOR_RANGE2_START) && - (mgmt_class <= IB_MGMT_CLASS_VENDOR_RANGE2_END)) - return IB_MGMT_VENDOR_HDR; - else - return IB_MGMT_RMPP_HDR; -} - static void send_handler(struct ib_mad_agent *agent, struct ib_mad_send_wc *send_wc) { @@ -283,7 +272,7 @@ static ssize_t copy_recv_mad(char __user *buf, struct ib_umad_packet *packet, */ return -ENOSPC; } - offset = data_offset(recv_buf->mad->mad_hdr.mgmt_class); + offset = ib_get_mad_data_offset(recv_buf->mad->mad_hdr.mgmt_class); max_seg_payload = sizeof (struct ib_mad) - offset; for (left = packet->length - seg_payload, buf += seg_payload; @@ -441,21 +430,14 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf, } rmpp_mad = (struct ib_rmpp_mad *) packet->mad.data; - if (rmpp_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_ADM) { - hdr_len = IB_MGMT_SA_HDR; - copy_offset = IB_MGMT_RMPP_HDR; - rmpp_active = ib_get_rmpp_flags(&rmpp_mad->rmpp_hdr) & - IB_MGMT_RMPP_FLAG_ACTIVE; - } else if (rmpp_mad->mad_hdr.mgmt_class >= IB_MGMT_CLASS_VENDOR_RANGE2_START && - rmpp_mad->mad_hdr.mgmt_class <= IB_MGMT_CLASS_VENDOR_RANGE2_END) { - hdr_len = IB_MGMT_VENDOR_HDR; - copy_offset = IB_MGMT_RMPP_HDR; - rmpp_active = ib_get_rmpp_flags(&rmpp_mad->rmpp_hdr) & - IB_MGMT_RMPP_FLAG_ACTIVE; - } else { - hdr_len = IB_MGMT_MAD_HDR; + hdr_len = ib_get_mad_data_offset(rmpp_mad->mad_hdr.mgmt_class); + if (!ib_is_mad_class_rmpp(rmpp_mad->mad_hdr.mgmt_class)) { copy_offset = IB_MGMT_MAD_HDR; rmpp_active = 0; + } else { + copy_offset = IB_MGMT_RMPP_HDR; + rmpp_active = ib_get_rmpp_flags(&rmpp_mad->rmpp_hdr) & + IB_MGMT_RMPP_FLAG_ACTIVE; } data_len = count - sizeof (struct ib_user_mad) - hdr_len; diff --git a/drivers/infiniband/hw/ipath/Kconfig b/drivers/infiniband/hw/ipath/Kconfig new file mode 100644 index 000000000000..9ea67c409b6d --- /dev/null +++ b/drivers/infiniband/hw/ipath/Kconfig @@ -0,0 +1,16 @@ +config IPATH_CORE + tristate "PathScale InfiniPath Driver" + depends on 64BIT && PCI_MSI && NET + ---help--- + This is a low-level driver for PathScale InfiniPath host channel + adapters (HCAs) based on the HT-400 and PE-800 chips. + +config INFINIBAND_IPATH + tristate "PathScale InfiniPath Verbs Driver" + depends on IPATH_CORE && INFINIBAND + ---help--- + This is a driver that provides InfiniBand verbs support for + PathScale InfiniPath host channel adapters (HCAs). This + allows these devices to be used with both kernel upper level + protocols such as IP-over-InfiniBand as well as with userspace + applications (in conjunction with InfiniBand userspace access). diff --git a/drivers/infiniband/hw/ipath/Makefile b/drivers/infiniband/hw/ipath/Makefile new file mode 100644 index 000000000000..b4d084abfd22 --- /dev/null +++ b/drivers/infiniband/hw/ipath/Makefile @@ -0,0 +1,36 @@ +EXTRA_CFLAGS += -DIPATH_IDSTR='"PathScale kernel.org driver"' \ + -DIPATH_KERN_TYPE=0 + +obj-$(CONFIG_IPATH_CORE) += ipath_core.o +obj-$(CONFIG_INFINIBAND_IPATH) += ib_ipath.o + +ipath_core-y := \ + ipath_diag.o \ + ipath_driver.o \ + ipath_eeprom.o \ + ipath_file_ops.o \ + ipath_fs.o \ + ipath_ht400.o \ + ipath_init_chip.o \ + ipath_intr.o \ + ipath_layer.o \ + ipath_pe800.o \ + ipath_stats.o \ + ipath_sysfs.o \ + ipath_user_pages.o + +ipath_core-$(CONFIG_X86_64) += ipath_wc_x86_64.o + +ib_ipath-y := \ + ipath_cq.o \ + ipath_keys.o \ + ipath_mad.o \ + ipath_mr.o \ + ipath_qp.o \ + ipath_rc.o \ + ipath_ruc.o \ + ipath_srq.o \ + ipath_uc.o \ + ipath_ud.o \ + ipath_verbs.o \ + ipath_verbs_mcast.o diff --git a/drivers/infiniband/hw/ipath/ipath_common.h b/drivers/infiniband/hw/ipath/ipath_common.h new file mode 100644 index 000000000000..48a55247b832 --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_common.h @@ -0,0 +1,616 @@ +/* + * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _IPATH_COMMON_H +#define _IPATH_COMMON_H + +/* + * This file contains defines, structures, etc. that are used + * to communicate between kernel and user code. + */ + +/* This is the IEEE-assigned OUI for PathScale, Inc. */ +#define IPATH_SRC_OUI_1 0x00 +#define IPATH_SRC_OUI_2 0x11 +#define IPATH_SRC_OUI_3 0x75 + +/* version of protocol header (known to chip also). In the long run, + * we should be able to generate and accept a range of version numbers; + * for now we only accept one, and it's compiled in. + */ +#define IPS_PROTO_VERSION 2 + +/* + * These are compile time constants that you may want to enable or disable + * if you are trying to debug problems with code or performance. + * IPATH_VERBOSE_TRACING define as 1 if you want additional tracing in + * fastpath code + * IPATH_TRACE_REGWRITES define as 1 if you want register writes to be + * traced in faspath code + * _IPATH_TRACING define as 0 if you want to remove all tracing in a + * compilation unit + * _IPATH_DEBUGGING define as 0 if you want to remove debug prints + */ + +/* + * The value in the BTH QP field that InfiniPath uses to differentiate + * an infinipath protocol IB packet vs standard IB transport + */ +#define IPATH_KD_QP 0x656b79 + +/* + * valid states passed to ipath_set_linkstate() user call + */ +#define IPATH_IB_LINKDOWN 0 +#define IPATH_IB_LINKARM 1 +#define IPATH_IB_LINKACTIVE 2 +#define IPATH_IB_LINKINIT 3 +#define IPATH_IB_LINKDOWN_SLEEP 4 +#define IPATH_IB_LINKDOWN_DISABLE 5 + +/* + * stats maintained by the driver. For now, at least, this is global + * to all minor devices. + */ +struct infinipath_stats { + /* number of interrupts taken */ + __u64 sps_ints; + /* number of interrupts for errors */ + __u64 sps_errints; + /* number of errors from chip (not incl. packet errors or CRC) */ + __u64 sps_errs; + /* number of packet errors from chip other than CRC */ + __u64 sps_pkterrs; + /* number of packets with CRC errors (ICRC and VCRC) */ + __u64 sps_crcerrs; + /* number of hardware errors reported (parity, etc.) */ + __u64 sps_hwerrs; + /* number of times IB link changed state unexpectedly */ + __u64 sps_iblink; + /* no longer used; left for compatibility */ + __u64 sps_unused3; + /* number of kernel (port0) packets received */ + __u64 sps_port0pkts; + /* number of "ethernet" packets sent by driver */ + __u64 sps_ether_spkts; + /* number of "ethernet" packets received by driver */ + __u64 sps_ether_rpkts; + /* number of SMA packets sent by driver */ + __u64 sps_sma_spkts; + /* number of SMA packets received by driver */ + __u64 sps_sma_rpkts; + /* number of times all ports rcvhdrq was full and packet dropped */ + __u64 sps_hdrqfull; + /* number of times all ports egrtid was full and packet dropped */ + __u64 sps_etidfull; + /* + * number of times we tried to send from driver, but no pio buffers + * avail + */ + __u64 sps_nopiobufs; + /* number of ports currently open */ + __u64 sps_ports; + /* list of pkeys (other than default) accepted (0 means not set) */ + __u16 sps_pkeys[4]; + /* lids for up to 4 infinipaths, indexed by infinipath # */ + __u16 sps_lid[4]; + /* number of user ports per chip (not IB ports) */ + __u32 sps_nports; + /* not our interrupt, or already handled */ + __u32 sps_nullintr; + /* max number of packets handled per receive call */ + __u32 sps_maxpkts_call; + /* avg number of packets handled per receive call */ + __u32 sps_avgpkts_call; + /* total number of pages locked */ + __u64 sps_pagelocks; + /* total number of pages unlocked */ + __u64 sps_pageunlocks; + /* + * Number of packets dropped in kernel other than errors (ether + * packets if ipath not configured, sma/mad, etc.) + */ + __u64 sps_krdrops; + /* mlids for up to 4 infinipaths, indexed by infinipath # */ + __u16 sps_mlid[4]; + /* pad for future growth */ + __u64 __sps_pad[45]; +}; + +/* + * These are the status bits readable (in ascii form, 64bit value) + * from the "status" sysfs file. + */ +#define IPATH_STATUS_INITTED 0x1 /* basic initialization done */ +#define IPATH_STATUS_DISABLED 0x2 /* hardware disabled */ +/* Device has been disabled via admin request */ +#define IPATH_STATUS_ADMIN_DISABLED 0x4 +#define IPATH_STATUS_OIB_SMA 0x8 /* ipath_mad kernel SMA running */ +#define IPATH_STATUS_SMA 0x10 /* user SMA running */ +/* Chip has been found and initted */ +#define IPATH_STATUS_CHIP_PRESENT 0x20 +/* IB link is at ACTIVE, usable for data traffic */ +#define IPATH_STATUS_IB_READY 0x40 +/* link is configured, LID, MTU, etc. have been set */ +#define IPATH_STATUS_IB_CONF 0x80 +/* no link established, probably no cable */ +#define IPATH_STATUS_IB_NOCABLE 0x100 +/* A Fatal hardware error has occurred. */ +#define IPATH_STATUS_HWERROR 0x200 + +/* + * The list of usermode accessible registers. Also see Reg_* later in file. + */ +typedef enum _ipath_ureg { + /* (RO) DMA RcvHdr to be used next. */ + ur_rcvhdrtail = 0, + /* (RW) RcvHdr entry to be processed next by host. */ + ur_rcvhdrhead = 1, + /* (RO) Index of next Eager index to use. */ + ur_rcvegrindextail = 2, + /* (RW) Eager TID to be processed next */ + ur_rcvegrindexhead = 3, + /* For internal use only; max register number. */ + _IPATH_UregMax +} ipath_ureg; + +/* bit values for spi_runtime_flags */ +#define IPATH_RUNTIME_HT 0x1 +#define IPATH_RUNTIME_PCIE 0x2 +#define IPATH_RUNTIME_FORCE_WC_ORDER 0x4 +#define IPATH_RUNTIME_RCVHDR_COPY 0x8 + +/* + * This structure is returned by ipath_userinit() immediately after + * open to get implementation-specific info, and info specific to this + * instance. + * + * This struct must have explict pad fields where type sizes + * may result in different alignments between 32 and 64 bit + * programs, since the 64 bit * bit kernel requires the user code + * to have matching offsets + */ +struct ipath_base_info { + /* version of hardware, for feature checking. */ + __u32 spi_hw_version; + /* version of software, for feature checking. */ + __u32 spi_sw_version; + /* InfiniPath port assigned, goes into sent packets */ + __u32 spi_port; + /* + * IB MTU, packets IB data must be less than this. + * The MTU is in bytes, and will be a multiple of 4 bytes. + */ + __u32 spi_mtu; + /* + * Size of a PIO buffer. Any given packet's total size must be less + * than this (in words). Included is the starting control word, so + * if 513 is returned, then total pkt size is 512 words or less. + */ + __u32 spi_piosize; + /* size of the TID cache in infinipath, in entries */ + __u32 spi_tidcnt; + /* size of the TID Eager list in infinipath, in entries */ + __u32 spi_tidegrcnt; + /* size of a single receive header queue entry. */ + __u32 spi_rcvhdrent_size; + /* + * Count of receive header queue entries allocated. + * This may be less than the spu_rcvhdrcnt passed in!. + */ + __u32 spi_rcvhdr_cnt; + + /* per-chip and other runtime features bitmap (IPATH_RUNTIME_*) */ + __u32 spi_runtime_flags; + + /* address where receive buffer queue is mapped into */ + __u64 spi_rcvhdr_base; + + /* user program. */ + + /* base address of eager TID receive buffers. */ + __u64 spi_rcv_egrbufs; + + /* Allocated by initialization code, not by protocol. */ + + /* + * Size of each TID buffer in host memory, starting at + * spi_rcv_egrbufs. The buffers are virtually contiguous. + */ + __u32 spi_rcv_egrbufsize; + /* + * The special QP (queue pair) value that identifies an infinipath + * protocol packet from standard IB packets. More, probably much + * more, to be added. + */ + __u32 spi_qpair; + + /* + * User register base for init code, not to be used directly by + * protocol or applications. + */ + __u64 __spi_uregbase; + /* + * Maximum buffer size in bytes that can be used in a single TID + * entry (assuming the buffer is aligned to this boundary). This is + * the minimum of what the hardware and software support Guaranteed + * to be a power of 2. + */ + __u32 spi_tid_maxsize; + /* + * alignment of each pio send buffer (byte count + * to add to spi_piobufbase to get to second buffer) + */ + __u32 spi_pioalign; + /* + * The index of the first pio buffer available to this process; + * needed to do lookup in spi_pioavailaddr; not added to + * spi_piobufbase. + */ + __u32 spi_pioindex; + /* number of buffers mapped for this process */ + __u32 spi_piocnt; + + /* + * Base address of writeonly pio buffers for this process. + * Each buffer has spi_piosize words, and is aligned on spi_pioalign + * boundaries. spi_piocnt buffers are mapped from this address + */ + __u64 spi_piobufbase; + + /* + * Base address of readonly memory copy of the pioavail registers. + * There are 2 bits for each buffer. + */ + __u64 spi_pioavailaddr; + + /* + * Address where driver updates a copy of the interface and driver + * status (IPATH_STATUS_*) as a 64 bit value. It's followed by a + * string indicating hardware error, if there was one. + */ + __u64 spi_status; + + /* number of chip ports available to user processes */ + __u32 spi_nports; + /* unit number of chip we are using */ + __u32 spi_unit; + /* num bufs in each contiguous set */ + __u32 spi_rcv_egrperchunk; + /* size in bytes of each contiguous set */ + __u32 spi_rcv_egrchunksize; + /* total size of mmap to cover full rcvegrbuffers */ + __u32 spi_rcv_egrbuftotlen; +} __attribute__ ((aligned(8))); + + +/* + * This version number is given to the driver by the user code during + * initialization in the spu_userversion field of ipath_user_info, so + * the driver can check for compatibility with user code. + * + * The major version changes when data structures + * change in an incompatible way. The driver must be the same or higher + * for initialization to succeed. In some cases, a higher version + * driver will not interoperate with older software, and initialization + * will return an error. + */ +#define IPATH_USER_SWMAJOR 1 + +/* + * Minor version differences are always compatible + * a within a major version, however if if user software is larger + * than driver software, some new features and/or structure fields + * may not be implemented; the user code must deal with this if it + * cares, or it must abort after initialization reports the difference + */ +#define IPATH_USER_SWMINOR 2 + +#define IPATH_USER_SWVERSION ((IPATH_USER_SWMAJOR<<16) | IPATH_USER_SWMINOR) + +#define IPATH_KERN_TYPE 0 + +/* + * Similarly, this is the kernel version going back to the user. It's + * slightly different, in that we want to tell if the driver was built as + * part of a PathScale release, or from the driver from OpenIB, kernel.org, + * or a standard distribution, for support reasons. The high bit is 0 for + * non-PathScale, and 1 for PathScale-built/supplied. + * + * It's returned by the driver to the user code during initialization in the + * spi_sw_version field of ipath_base_info, so the user code can in turn + * check for compatibility with the kernel. +*/ +#define IPATH_KERN_SWVERSION ((IPATH_KERN_TYPE<<31) | IPATH_USER_SWVERSION) + +/* + * This structure is passed to ipath_userinit() to tell the driver where + * user code buffers are, sizes, etc. The offsets and sizes of the + * fields must remain unchanged, for binary compatibility. It can + * be extended, if userversion is changed so user code can tell, if needed + */ +struct ipath_user_info { + /* + * version of user software, to detect compatibility issues. + * Should be set to IPATH_USER_SWVERSION. + */ + __u32 spu_userversion; + + /* desired number of receive header queue entries */ + __u32 spu_rcvhdrcnt; + + /* size of struct base_info to write to */ + __u32 spu_base_info_size; + + /* + * number of words in KD protocol header + * This tells InfiniPath how many words to copy to rcvhdrq. If 0, + * kernel uses a default. Once set, attempts to set any other value + * are an error (EAGAIN) until driver is reloaded. + */ + __u32 spu_rcvhdrsize; + + /* + * cache line aligned (64 byte) user address to + * which the rcvhdrtail register will be written by infinipath + * whenever it changes, so that no chip registers are read in + * the performance path. + */ + __u64 spu_rcvhdraddr; + + /* + * address of struct base_info to write to + */ + __u64 spu_base_info; + +} __attribute__ ((aligned(8))); + +/* User commands. */ + +#define IPATH_CMD_MIN 16 + +#define IPATH_CMD_USER_INIT 16 /* set up userspace */ +#define IPATH_CMD_PORT_INFO 17 /* find out what resources we got */ +#define IPATH_CMD_RECV_CTRL 18 /* control receipt of packets */ +#define IPATH_CMD_TID_UPDATE 19 /* update expected TID entries */ +#define IPATH_CMD_TID_FREE 20 /* free expected TID entries */ +#define IPATH_CMD_SET_PART_KEY 21 /* add partition key */ + +#define IPATH_CMD_MAX 21 + +struct ipath_port_info { + __u32 num_active; /* number of active units */ + __u32 unit; /* unit (chip) assigned to caller */ + __u32 port; /* port on unit assigned to caller */ +}; + +struct ipath_tid_info { + __u32 tidcnt; + /* make structure same size in 32 and 64 bit */ + __u32 tid__unused; + /* virtual address of first page in transfer */ + __u64 tidvaddr; + /* pointer (same size 32/64 bit) to __u16 tid array */ + __u64 tidlist; + + /* + * pointer (same size 32/64 bit) to bitmap of TIDs used + * for this call; checked for being large enough at open + */ + __u64 tidmap; +}; + +struct ipath_cmd { + __u32 type; /* command type */ + union { + struct ipath_tid_info tid_info; + struct ipath_user_info user_info; + /* address in userspace of struct ipath_port_info to + write result to */ + __u64 port_info; + /* enable/disable receipt of packets */ + __u32 recv_ctrl; + /* partition key to set */ + __u16 part_key; + } cmd; +}; + +struct ipath_iovec { + /* Pointer to data, but same size 32 and 64 bit */ + __u64 iov_base; + + /* + * Length of data; don't need 64 bits, but want + * ipath_sendpkt to remain same size as before 32 bit changes, so... + */ + __u64 iov_len; +}; + +/* + * Describes a single packet for send. Each packet can have one or more + * buffers, but the total length (exclusive of IB headers) must be less + * than the MTU, and if using the PIO method, entire packet length, + * including IB headers, must be less than the ipath_piosize value (words). + * Use of this necessitates including sys/uio.h + */ +struct __ipath_sendpkt { + __u32 sps_flags; /* flags for packet (TBD) */ + __u32 sps_cnt; /* number of entries to use in sps_iov */ + /* array of iov's describing packet. TEMPORARY */ + struct ipath_iovec sps_iov[4]; +}; + +/* Passed into SMA special file's ->read and ->write methods. */ +struct ipath_sma_pkt +{ + __u32 unit; /* unit on which to send packet */ + __u64 data; /* address of payload in userspace */ + __u32 len; /* length of payload */ +}; + +/* + * Data layout in I2C flash (for GUID, etc.) + * All fields are little-endian binary unless otherwise stated + */ +#define IPATH_FLASH_VERSION 1 +struct ipath_flash { + /* flash layout version (IPATH_FLASH_VERSION) */ + __u8 if_fversion; + /* checksum protecting if_length bytes */ + __u8 if_csum; + /* + * valid length (in use, protected by if_csum), including + * if_fversion and if_sum themselves) + */ + __u8 if_length; + /* the GUID, in network order */ + __u8 if_guid[8]; + /* number of GUIDs to use, starting from if_guid */ + __u8 if_numguid; + /* the board serial number, in ASCII */ + char if_serial[12]; + /* board mfg date (YYYYMMDD ASCII) */ + char if_mfgdate[8]; + /* last board rework/test date (YYYYMMDD ASCII) */ + char if_testdate[8]; + /* logging of error counts, TBD */ + __u8 if_errcntp[4]; + /* powered on hours, updated at driver unload */ + __u8 if_powerhour[2]; + /* ASCII free-form comment field */ + char if_comment[32]; + /* 78 bytes used, min flash size is 128 bytes */ + __u8 if_future[50]; +}; + +/* + * These are the counters implemented in the chip, and are listed in order. + * The InterCaps naming is taken straight from the chip spec. + */ +struct infinipath_counters { + __u64 LBIntCnt; + __u64 LBFlowStallCnt; + __u64 Reserved1; + __u64 TxUnsupVLErrCnt; + __u64 TxDataPktCnt; + __u64 TxFlowPktCnt; + __u64 TxDwordCnt; + __u64 TxLenErrCnt; + __u64 TxMaxMinLenErrCnt; + __u64 TxUnderrunCnt; + __u64 TxFlowStallCnt; + __u64 TxDroppedPktCnt; + __u64 RxDroppedPktCnt; + __u64 RxDataPktCnt; + __u64 RxFlowPktCnt; + __u64 RxDwordCnt; + __u64 RxLenErrCnt; + __u64 RxMaxMinLenErrCnt; + __u64 RxICRCErrCnt; + __u64 RxVCRCErrCnt; + __u64 RxFlowCtrlErrCnt; + __u64 RxBadFormatCnt; + __u64 RxLinkProblemCnt; + __u64 RxEBPCnt; + __u64 RxLPCRCErrCnt; + __u64 RxBufOvflCnt; + __u64 RxTIDFullErrCnt; + __u64 RxTIDValidErrCnt; + __u64 RxPKeyMismatchCnt; + __u64 RxP0HdrEgrOvflCnt; + __u64 RxP1HdrEgrOvflCnt; + __u64 RxP2HdrEgrOvflCnt; + __u64 RxP3HdrEgrOvflCnt; + __u64 RxP4HdrEgrOvflCnt; + __u64 RxP5HdrEgrOvflCnt; + __u64 RxP6HdrEgrOvflCnt; + __u64 RxP7HdrEgrOvflCnt; + __u64 RxP8HdrEgrOvflCnt; + __u64 Reserved6; + __u64 Reserved7; + __u64 IBStatusChangeCnt; + __u64 IBLinkErrRecoveryCnt; + __u64 IBLinkDownedCnt; + __u64 IBSymbolErrCnt; +}; + +/* + * The next set of defines are for packet headers, and chip register + * and memory bits that are visible to and/or used by user-mode software + * The other bits that are used only by the driver or diags are in + * ipath_registers.h + */ + +/* RcvHdrFlags bits */ +#define INFINIPATH_RHF_LENGTH_MASK 0x7FF +#define INFINIPATH_RHF_LENGTH_SHIFT 0 +#define INFINIPATH_RHF_RCVTYPE_MASK 0x7 +#define INFINIPATH_RHF_RCVTYPE_SHIFT 11 +#define INFINIPATH_RHF_EGRINDEX_MASK 0x7FF +#define INFINIPATH_RHF_EGRINDEX_SHIFT 16 +#define INFINIPATH_RHF_H_ICRCERR 0x80000000 +#define INFINIPATH_RHF_H_VCRCERR 0x40000000 +#define INFINIPATH_RHF_H_PARITYERR 0x20000000 +#define INFINIPATH_RHF_H_LENERR 0x10000000 +#define INFINIPATH_RHF_H_MTUERR 0x08000000 +#define INFINIPATH_RHF_H_IHDRERR 0x04000000 +#define INFINIPATH_RHF_H_TIDERR 0x02000000 +#define INFINIPATH_RHF_H_MKERR 0x01000000 +#define INFINIPATH_RHF_H_IBERR 0x00800000 +#define INFINIPATH_RHF_L_SWA 0x00008000 +#define INFINIPATH_RHF_L_SWB 0x00004000 + +/* infinipath header fields */ +#define INFINIPATH_I_VERS_MASK 0xF +#define INFINIPATH_I_VERS_SHIFT 28 +#define INFINIPATH_I_PORT_MASK 0xF +#define INFINIPATH_I_PORT_SHIFT 24 +#define INFINIPATH_I_TID_MASK 0x7FF +#define INFINIPATH_I_TID_SHIFT 13 +#define INFINIPATH_I_OFFSET_MASK 0x1FFF +#define INFINIPATH_I_OFFSET_SHIFT 0 + +/* K_PktFlags bits */ +#define INFINIPATH_KPF_INTR 0x1 + +/* SendPIO per-buffer control */ +#define INFINIPATH_SP_LENGTHP1_MASK 0x3FF +#define INFINIPATH_SP_LENGTHP1_SHIFT 0 +#define INFINIPATH_SP_INTR 0x80000000 +#define INFINIPATH_SP_TEST 0x40000000 +#define INFINIPATH_SP_TESTEBP 0x20000000 + +/* SendPIOAvail bits */ +#define INFINIPATH_SENDPIOAVAIL_BUSY_SHIFT 1 +#define INFINIPATH_SENDPIOAVAIL_CHECK_SHIFT 0 + +#endif /* _IPATH_COMMON_H */ diff --git a/drivers/infiniband/hw/ipath/ipath_cq.c b/drivers/infiniband/hw/ipath/ipath_cq.c new file mode 100644 index 000000000000..7ece1135ddfe --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_cq.c @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include "ipath_verbs.h" + +/** + * ipath_cq_enter - add a new entry to the completion queue + * @cq: completion queue + * @entry: work completion entry to add + * @sig: true if @entry is a solicitated entry + * + * This may be called with one of the qp->s_lock or qp->r_rq.lock held. + */ +void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int solicited) +{ + unsigned long flags; + u32 next; + + spin_lock_irqsave(&cq->lock, flags); + + if (cq->head == cq->ibcq.cqe) + next = 0; + else + next = cq->head + 1; + if (unlikely(next == cq->tail)) { + spin_unlock_irqrestore(&cq->lock, flags); + if (cq->ibcq.event_handler) { + struct ib_event ev; + + ev.device = cq->ibcq.device; + ev.element.cq = &cq->ibcq; + ev.event = IB_EVENT_CQ_ERR; + cq->ibcq.event_handler(&ev, cq->ibcq.cq_context); + } + return; + } + cq->queue[cq->head] = *entry; + cq->head = next; + + if (cq->notify == IB_CQ_NEXT_COMP || + (cq->notify == IB_CQ_SOLICITED && solicited)) { + cq->notify = IB_CQ_NONE; + cq->triggered++; + /* + * This will cause send_complete() to be called in + * another thread. + */ + tasklet_hi_schedule(&cq->comptask); + } + + spin_unlock_irqrestore(&cq->lock, flags); + + if (entry->status != IB_WC_SUCCESS) + to_idev(cq->ibcq.device)->n_wqe_errs++; +} + +/** + * ipath_poll_cq - poll for work completion entries + * @ibcq: the completion queue to poll + * @num_entries: the maximum number of entries to return + * @entry: pointer to array where work completions are placed + * + * Returns the number of completion entries polled. + * + * This may be called from interrupt context. Also called by ib_poll_cq() + * in the generic verbs code. + */ +int ipath_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry) +{ + struct ipath_cq *cq = to_icq(ibcq); + unsigned long flags; + int npolled; + + spin_lock_irqsave(&cq->lock, flags); + + for (npolled = 0; npolled < num_entries; ++npolled, ++entry) { + if (cq->tail == cq->head) + break; + *entry = cq->queue[cq->tail]; + if (cq->tail == cq->ibcq.cqe) + cq->tail = 0; + else + cq->tail++; + } + + spin_unlock_irqrestore(&cq->lock, flags); + + return npolled; +} + +static void send_complete(unsigned long data) +{ + struct ipath_cq *cq = (struct ipath_cq *)data; + + /* + * The completion handler will most likely rearm the notification + * and poll for all pending entries. If a new completion entry + * is added while we are in this routine, tasklet_hi_schedule() + * won't call us again until we return so we check triggered to + * see if we need to call the handler again. + */ + for (;;) { + u8 triggered = cq->triggered; + + cq->ibcq.comp_handler(&cq->ibcq, cq->ibcq.cq_context); + + if (cq->triggered == triggered) + return; + } +} + +/** + * ipath_create_cq - create a completion queue + * @ibdev: the device this completion queue is attached to + * @entries: the minimum size of the completion queue + * @context: unused by the InfiniPath driver + * @udata: unused by the InfiniPath driver + * + * Returns a pointer to the completion queue or negative errno values + * for failure. + * + * Called by ib_create_cq() in the generic verbs code. + */ +struct ib_cq *ipath_create_cq(struct ib_device *ibdev, int entries, + struct ib_ucontext *context, + struct ib_udata *udata) +{ + struct ipath_cq *cq; + struct ib_wc *wc; + struct ib_cq *ret; + + /* + * Need to use vmalloc() if we want to support large #s of + * entries. + */ + cq = kmalloc(sizeof(*cq), GFP_KERNEL); + if (!cq) { + ret = ERR_PTR(-ENOMEM); + goto bail; + } + + /* + * Need to use vmalloc() if we want to support large #s of entries. + */ + wc = vmalloc(sizeof(*wc) * (entries + 1)); + if (!wc) { + kfree(cq); + ret = ERR_PTR(-ENOMEM); + goto bail; + } + /* + * ib_create_cq() will initialize cq->ibcq except for cq->ibcq.cqe. + * The number of entries should be >= the number requested or return + * an error. + */ + cq->ibcq.cqe = entries; + cq->notify = IB_CQ_NONE; + cq->triggered = 0; + spin_lock_init(&cq->lock); + tasklet_init(&cq->comptask, send_complete, (unsigned long)cq); + cq->head = 0; + cq->tail = 0; + cq->queue = wc; + + ret = &cq->ibcq; + +bail: + return ret; +} + +/** + * ipath_destroy_cq - destroy a completion queue + * @ibcq: the completion queue to destroy. + * + * Returns 0 for success. + * + * Called by ib_destroy_cq() in the generic verbs code. + */ +int ipath_destroy_cq(struct ib_cq *ibcq) +{ + struct ipath_cq *cq = to_icq(ibcq); + + tasklet_kill(&cq->comptask); + vfree(cq->queue); + kfree(cq); + + return 0; +} + +/** + * ipath_req_notify_cq - change the notification type for a completion queue + * @ibcq: the completion queue + * @notify: the type of notification to request + * + * Returns 0 for success. + * + * This may be called from interrupt context. Also called by + * ib_req_notify_cq() in the generic verbs code. + */ +int ipath_req_notify_cq(struct ib_cq *ibcq, enum ib_cq_notify notify) +{ + struct ipath_cq *cq = to_icq(ibcq); + unsigned long flags; + + spin_lock_irqsave(&cq->lock, flags); + /* + * Don't change IB_CQ_NEXT_COMP to IB_CQ_SOLICITED but allow + * any other transitions. + */ + if (cq->notify != IB_CQ_NEXT_COMP) + cq->notify = notify; + spin_unlock_irqrestore(&cq->lock, flags); + return 0; +} + +int ipath_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata) +{ + struct ipath_cq *cq = to_icq(ibcq); + struct ib_wc *wc, *old_wc; + u32 n; + int ret; + + /* + * Need to use vmalloc() if we want to support large #s of entries. + */ + wc = vmalloc(sizeof(*wc) * (cqe + 1)); + if (!wc) { + ret = -ENOMEM; + goto bail; + } + + spin_lock_irq(&cq->lock); + if (cq->head < cq->tail) + n = cq->ibcq.cqe + 1 + cq->head - cq->tail; + else + n = cq->head - cq->tail; + if (unlikely((u32)cqe < n)) { + spin_unlock_irq(&cq->lock); + vfree(wc); + ret = -EOVERFLOW; + goto bail; + } + for (n = 0; cq->tail != cq->head; n++) { + wc[n] = cq->queue[cq->tail]; + if (cq->tail == cq->ibcq.cqe) + cq->tail = 0; + else + cq->tail++; + } + cq->ibcq.cqe = cqe; + cq->head = n; + cq->tail = 0; + old_wc = cq->queue; + cq->queue = wc; + spin_unlock_irq(&cq->lock); + + vfree(old_wc); + + ret = 0; + +bail: + return ret; +} diff --git a/drivers/infiniband/hw/ipath/ipath_debug.h b/drivers/infiniband/hw/ipath/ipath_debug.h new file mode 100644 index 000000000000..593e28969c69 --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_debug.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _IPATH_DEBUG_H +#define _IPATH_DEBUG_H + +#ifndef _IPATH_DEBUGGING /* debugging enabled or not */ +#define _IPATH_DEBUGGING 1 +#endif + +#if _IPATH_DEBUGGING + +/* + * Mask values for debugging. The scheme allows us to compile out any + * of the debug tracing stuff, and if compiled in, to enable or disable + * dynamically. This can be set at modprobe time also: + * modprobe infinipath.ko infinipath_debug=7 + */ + +#define __IPATH_INFO 0x1 /* generic low verbosity stuff */ +#define __IPATH_DBG 0x2 /* generic debug */ +#define __IPATH_TRSAMPLE 0x8 /* generate trace buffer sample entries */ +/* leave some low verbosity spots open */ +#define __IPATH_VERBDBG 0x40 /* very verbose debug */ +#define __IPATH_PKTDBG 0x80 /* print packet data */ +/* print process startup (init)/exit messages */ +#define __IPATH_PROCDBG 0x100 +/* print mmap/nopage stuff, not using VDBG any more */ +#define __IPATH_MMDBG 0x200 +#define __IPATH_USER_SEND 0x1000 /* use user mode send */ +#define __IPATH_KERNEL_SEND 0x2000 /* use kernel mode send */ +#define __IPATH_EPKTDBG 0x4000 /* print ethernet packet data */ +#define __IPATH_SMADBG 0x8000 /* sma packet debug */ +#define __IPATH_IPATHDBG 0x10000 /* Ethernet (IPATH) general debug on */ +#define __IPATH_IPATHWARN 0x20000 /* Ethernet (IPATH) warnings on */ +#define __IPATH_IPATHERR 0x40000 /* Ethernet (IPATH) errors on */ +#define __IPATH_IPATHPD 0x80000 /* Ethernet (IPATH) packet dump on */ +#define __IPATH_IPATHTABLE 0x100000 /* Ethernet (IPATH) table dump on */ + +#else /* _IPATH_DEBUGGING */ + +/* + * define all of these even with debugging off, for the few places that do + * if(infinipath_debug & _IPATH_xyzzy), but in a way that will make the + * compiler eliminate the code + */ + +#define __IPATH_INFO 0x0 /* generic low verbosity stuff */ +#define __IPATH_DBG 0x0 /* generic debug */ +#define __IPATH_TRSAMPLE 0x0 /* generate trace buffer sample entries */ +#define __IPATH_VERBDBG 0x0 /* very verbose debug */ +#define __IPATH_PKTDBG 0x0 /* print packet data */ +#define __IPATH_PROCDBG 0x0 /* print process startup (init)/exit messages */ +/* print mmap/nopage stuff, not using VDBG any more */ +#define __IPATH_MMDBG 0x0 +#define __IPATH_EPKTDBG 0x0 /* print ethernet packet data */ +#define __IPATH_SMADBG 0x0 /* print process startup (init)/exit messages */#define __IPATH_IPATHDBG 0x0 /* Ethernet (IPATH) table dump on */ +#define __IPATH_IPATHWARN 0x0 /* Ethernet (IPATH) warnings on */ +#define __IPATH_IPATHERR 0x0 /* Ethernet (IPATH) errors on */ +#define __IPATH_IPATHPD 0x0 /* Ethernet (IPATH) packet dump on */ +#define __IPATH_IPATHTABLE 0x0 /* Ethernet (IPATH) packet dump on */ + +#endif /* _IPATH_DEBUGGING */ + +#define __IPATH_VERBOSEDBG __IPATH_VERBDBG + +#endif /* _IPATH_DEBUG_H */ diff --git a/drivers/infiniband/hw/ipath/ipath_diag.c b/drivers/infiniband/hw/ipath/ipath_diag.c new file mode 100644 index 000000000000..cd533cf951c2 --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_diag.c @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * This file contains support for diagnostic functions. It is accessed by + * opening the ipath_diag device, normally minor number 129. Diagnostic use + * of the InfiniPath chip may render the chip or board unusable until the + * driver is unloaded, or in some cases, until the system is rebooted. + * + * Accesses to the chip through this interface are not similar to going + * through the /sys/bus/pci resource mmap interface. + */ + +#include +#include + +#include "ipath_common.h" +#include "ipath_kernel.h" +#include "ips_common.h" +#include "ipath_layer.h" + +int ipath_diag_inuse; +static int diag_set_link; + +static int ipath_diag_open(struct inode *in, struct file *fp); +static int ipath_diag_release(struct inode *in, struct file *fp); +static ssize_t ipath_diag_read(struct file *fp, char __user *data, + size_t count, loff_t *off); +static ssize_t ipath_diag_write(struct file *fp, const char __user *data, + size_t count, loff_t *off); + +static struct file_operations diag_file_ops = { + .owner = THIS_MODULE, + .write = ipath_diag_write, + .read = ipath_diag_read, + .open = ipath_diag_open, + .release = ipath_diag_release +}; + +static struct cdev *diag_cdev; +static struct class_device *diag_class_dev; + +int ipath_diag_init(void) +{ + return ipath_cdev_init(IPATH_DIAG_MINOR, "ipath_diag", + &diag_file_ops, &diag_cdev, &diag_class_dev); +} + +void ipath_diag_cleanup(void) +{ + ipath_cdev_cleanup(&diag_cdev, &diag_class_dev); +} + +/** + * ipath_read_umem64 - read a 64-bit quantity from the chip into user space + * @dd: the infinipath device + * @uaddr: the location to store the data in user memory + * @caddr: the source chip address (full pointer, not offset) + * @count: number of bytes to copy (multiple of 32 bits) + * + * This function also localizes all chip memory accesses. + * The copy should be written such that we read full cacheline packets + * from the chip. This is usually used for a single qword + * + * NOTE: This assumes the chip address is 64-bit aligned. + */ +static int ipath_read_umem64(struct ipath_devdata *dd, void __user *uaddr, + const void __iomem *caddr, size_t count) +{ + const u64 __iomem *reg_addr = caddr; + const u64 __iomem *reg_end = reg_addr + (count / sizeof(u64)); + int ret; + + /* not very efficient, but it works for now */ + if (reg_addr < dd->ipath_kregbase || + reg_end > dd->ipath_kregend) { + ret = -EINVAL; + goto bail; + } + while (reg_addr < reg_end) { + u64 data = readq(reg_addr); + if (copy_to_user(uaddr, &data, sizeof(u64))) { + ret = -EFAULT; + goto bail; + } + reg_addr++; + uaddr++; + } + ret = 0; +bail: + return ret; +} + +/** + * ipath_write_umem64 - write a 64-bit quantity to the chip from user space + * @dd: the infinipath device + * @caddr: the destination chip address (full pointer, not offset) + * @uaddr: the source of the data in user memory + * @count: the number of bytes to copy (multiple of 32 bits) + * + * This is usually used for a single qword + * NOTE: This assumes the chip address is 64-bit aligned. + */ + +static int ipath_write_umem64(struct ipath_devdata *dd, void __iomem *caddr, + const void __user *uaddr, size_t count) +{ + u64 __iomem *reg_addr = caddr; + const u64 __iomem *reg_end = reg_addr + (count / sizeof(u64)); + int ret; + + /* not very efficient, but it works for now */ + if (reg_addr < dd->ipath_kregbase || + reg_end > dd->ipath_kregend) { + ret = -EINVAL; + goto bail; + } + while (reg_addr < reg_end) { + u64 data; + if (copy_from_user(&data, uaddr, sizeof(data))) { + ret = -EFAULT; + goto bail; + } + writeq(data, reg_addr); + + reg_addr++; + uaddr++; + } + ret = 0; +bail: + return ret; +} + +/** + * ipath_read_umem32 - read a 32-bit quantity from the chip into user space + * @dd: the infinipath device + * @uaddr: the location to store the data in user memory + * @caddr: the source chip address (full pointer, not offset) + * @count: number of bytes to copy + * + * read 32 bit values, not 64 bit; for memories that only + * support 32 bit reads; usually a single dword. + */ +static int ipath_read_umem32(struct ipath_devdata *dd, void __user *uaddr, + const void __iomem *caddr, size_t count) +{ + const u32 __iomem *reg_addr = caddr; + const u32 __iomem *reg_end = reg_addr + (count / sizeof(u32)); + int ret; + + if (reg_addr < (u32 __iomem *) dd->ipath_kregbase || + reg_end > (u32 __iomem *) dd->ipath_kregend) { + ret = -EINVAL; + goto bail; + } + /* not very efficient, but it works for now */ + while (reg_addr < reg_end) { + u32 data = readl(reg_addr); + if (copy_to_user(uaddr, &data, sizeof(data))) { + ret = -EFAULT; + goto bail; + } + + reg_addr++; + uaddr++; + } + ret = 0; +bail: + return ret; +} + +/** + * ipath_write_umem32 - write a 32-bit quantity to the chip from user space + * @dd: the infinipath device + * @caddr: the destination chip address (full pointer, not offset) + * @uaddr: the source of the data in user memory + * @count: number of bytes to copy + * + * write 32 bit values, not 64 bit; for memories that only + * support 32 bit write; usually a single dword. + */ + +static int ipath_write_umem32(struct ipath_devdata *dd, void __iomem *caddr, + const void __user *uaddr, size_t count) +{ + u32 __iomem *reg_addr = caddr; + const u32 __iomem *reg_end = reg_addr + (count / sizeof(u32)); + int ret; + + if (reg_addr < (u32 __iomem *) dd->ipath_kregbase || + reg_end > (u32 __iomem *) dd->ipath_kregend) { + ret = -EINVAL; + goto bail; + } + while (reg_addr < reg_end) { + u32 data; + if (copy_from_user(&data, uaddr, sizeof(data))) { + ret = -EFAULT; + goto bail; + } + writel(data, reg_addr); + + reg_addr++; + uaddr++; + } + ret = 0; +bail: + return ret; +} + +static int ipath_diag_open(struct inode *in, struct file *fp) +{ + struct ipath_devdata *dd; + int unit = 0; /* XXX this is bogus */ + unsigned long flags; + int ret; + + dd = ipath_lookup(unit); + + mutex_lock(&ipath_mutex); + spin_lock_irqsave(&ipath_devs_lock, flags); + + if (ipath_diag_inuse) { + ret = -EBUSY; + goto bail; + } + + list_for_each_entry(dd, &ipath_dev_list, ipath_list) { + /* + * we need at least one infinipath device to be present + * (don't use INITTED, because we want to be able to open + * even if device is in freeze mode, which cleared INITTED). + * There is a small amount of risk to this, which is why we + * also verify kregbase is set. + */ + + if (!(dd->ipath_flags & IPATH_PRESENT) || + !dd->ipath_kregbase) + continue; + + ipath_diag_inuse = 1; + diag_set_link = 0; + ret = 0; + goto bail; + } + + ret = -ENODEV; + +bail: + spin_unlock_irqrestore(&ipath_devs_lock, flags); + mutex_unlock(&ipath_mutex); + + /* Only expose a way to reset the device if we + make it into diag mode. */ + if (ret == 0) + ipath_expose_reset(&dd->pcidev->dev); + + return ret; +} + +static int ipath_diag_release(struct inode *i, struct file *f) +{ + mutex_lock(&ipath_mutex); + ipath_diag_inuse = 0; + mutex_unlock(&ipath_mutex); + return 0; +} + +static ssize_t ipath_diag_read(struct file *fp, char __user *data, + size_t count, loff_t *off) +{ + int unit = 0; /* XXX provide for reads on other units some day */ + struct ipath_devdata *dd; + void __iomem *kreg_base; + ssize_t ret; + + dd = ipath_lookup(unit); + if (!dd) { + ret = -ENODEV; + goto bail; + } + + kreg_base = dd->ipath_kregbase; + + if (count == 0) + ret = 0; + else if ((count % 4) || (*off % 4)) + /* address or length is not 32-bit aligned, hence invalid */ + ret = -EINVAL; + else if ((count % 8) || (*off % 8)) + /* address or length not 64-bit aligned; do 32-bit reads */ + ret = ipath_read_umem32(dd, data, kreg_base + *off, count); + else + ret = ipath_read_umem64(dd, data, kreg_base + *off, count); + + if (ret >= 0) { + *off += count; + ret = count; + } + +bail: + return ret; +} + +static ssize_t ipath_diag_write(struct file *fp, const char __user *data, + size_t count, loff_t *off) +{ + int unit = 0; /* XXX this is bogus */ + struct ipath_devdata *dd; + void __iomem *kreg_base; + ssize_t ret; + + dd = ipath_lookup(unit); + if (!dd) { + ret = -ENODEV; + goto bail; + } + kreg_base = dd->ipath_kregbase; + + if (count == 0) + ret = 0; + else if ((count % 4) || (*off % 4)) + /* address or length is not 32-bit aligned, hence invalid */ + ret = -EINVAL; + else if ((count % 8) || (*off % 8)) + /* address or length not 64-bit aligned; do 32-bit writes */ + ret = ipath_write_umem32(dd, kreg_base + *off, data, count); + else + ret = ipath_write_umem64(dd, kreg_base + *off, data, count); + + if (ret >= 0) { + *off += count; + ret = count; + } + +bail: + return ret; +} + +void ipath_diag_bringup_link(struct ipath_devdata *dd) +{ + if (diag_set_link || (dd->ipath_flags & IPATH_LINKACTIVE)) + return; + + diag_set_link = 1; + ipath_cdbg(VERBOSE, "Trying to set to set link active for " + "diag pkt\n"); + ipath_layer_set_linkstate(dd, IPATH_IB_LINKARM); + ipath_layer_set_linkstate(dd, IPATH_IB_LINKACTIVE); +} diff --git a/drivers/infiniband/hw/ipath/ipath_driver.c b/drivers/infiniband/hw/ipath/ipath_driver.c new file mode 100644 index 000000000000..58a94efb0070 --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_driver.c @@ -0,0 +1,1983 @@ +/* + * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include "ipath_kernel.h" +#include "ips_common.h" +#include "ipath_layer.h" + +static void ipath_update_pio_bufs(struct ipath_devdata *); + +const char *ipath_get_unit_name(int unit) +{ + static char iname[16]; + snprintf(iname, sizeof iname, "infinipath%u", unit); + return iname; +} + +EXPORT_SYMBOL_GPL(ipath_get_unit_name); + +#define DRIVER_LOAD_MSG "PathScale " IPATH_DRV_NAME " loaded: " +#define PFX IPATH_DRV_NAME ": " + +/* + * The size has to be longer than this string, so we can append + * board/chip information to it in the init code. + */ +const char ipath_core_version[] = IPATH_IDSTR "\n"; + +static struct idr unit_table; +DEFINE_SPINLOCK(ipath_devs_lock); +LIST_HEAD(ipath_dev_list); + +wait_queue_head_t ipath_sma_state_wait; + +unsigned ipath_debug = __IPATH_INFO; + +module_param_named(debug, ipath_debug, uint, S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(debug, "mask for debug prints"); +EXPORT_SYMBOL_GPL(ipath_debug); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("PathScale "); +MODULE_DESCRIPTION("Pathscale InfiniPath driver"); + +const char *ipath_ibcstatus_str[] = { + "Disabled", + "LinkUp", + "PollActive", + "PollQuiet", + "SleepDelay", + "SleepQuiet", + "LState6", /* unused */ + "LState7", /* unused */ + "CfgDebounce", + "CfgRcvfCfg", + "CfgWaitRmt", + "CfgIdle", + "RecovRetrain", + "LState0xD", /* unused */ + "RecovWaitRmt", + "RecovIdle", +}; + +/* + * These variables are initialized in the chip-specific files + * but are defined here. + */ +u16 ipath_gpio_sda_num, ipath_gpio_scl_num; +u64 ipath_gpio_sda, ipath_gpio_scl; +u64 infinipath_i_bitsextant; +ipath_err_t infinipath_e_bitsextant, infinipath_hwe_bitsextant; +u32 infinipath_i_rcvavail_mask, infinipath_i_rcvurg_mask; + +static void __devexit ipath_remove_one(struct pci_dev *); +static int __devinit ipath_init_one(struct pci_dev *, + const struct pci_device_id *); + +/* Only needed for registration, nothing else needs this info */ +#define PCI_VENDOR_ID_PATHSCALE 0x1fc1 +#define PCI_DEVICE_ID_INFINIPATH_HT 0xd +#define PCI_DEVICE_ID_INFINIPATH_PE800 0x10 + +static const struct pci_device_id ipath_pci_tbl[] = { + {PCI_DEVICE(PCI_VENDOR_ID_PATHSCALE, + PCI_DEVICE_ID_INFINIPATH_HT)}, + {PCI_DEVICE(PCI_VENDOR_ID_PATHSCALE, + PCI_DEVICE_ID_INFINIPATH_PE800)}, +}; + +MODULE_DEVICE_TABLE(pci, ipath_pci_tbl); + +static struct pci_driver ipath_driver = { + .name = IPATH_DRV_NAME, + .probe = ipath_init_one, + .remove = __devexit_p(ipath_remove_one), + .id_table = ipath_pci_tbl, +}; + +/* + * This is where port 0's rcvhdrtail register is written back; we also + * want nothing else sharing the cache line, so make it a cache line + * in size. Used for all units. + */ +volatile __le64 *ipath_port0_rcvhdrtail; +dma_addr_t ipath_port0_rcvhdrtail_dma; +static int port0_rcvhdrtail_refs; + +static inline void read_bars(struct ipath_devdata *dd, struct pci_dev *dev, + u32 *bar0, u32 *bar1) +{ + int ret; + + ret = pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, bar0); + if (ret) + ipath_dev_err(dd, "failed to read bar0 before enable: " + "error %d\n", -ret); + + ret = pci_read_config_dword(dev, PCI_BASE_ADDRESS_1, bar1); + if (ret) + ipath_dev_err(dd, "failed to read bar1 before enable: " + "error %d\n", -ret); + + ipath_dbg("Read bar0 %x bar1 %x\n", *bar0, *bar1); +} + +static void ipath_free_devdata(struct pci_dev *pdev, + struct ipath_devdata *dd) +{ + unsigned long flags; + + pci_set_drvdata(pdev, NULL); + + if (dd->ipath_unit != -1) { + spin_lock_irqsave(&ipath_devs_lock, flags); + idr_remove(&unit_table, dd->ipath_unit); + list_del(&dd->ipath_list); + spin_unlock_irqrestore(&ipath_devs_lock, flags); + } + dma_free_coherent(&pdev->dev, sizeof(*dd), dd, dd->ipath_dma_addr); +} + +static struct ipath_devdata *ipath_alloc_devdata(struct pci_dev *pdev) +{ + unsigned long flags; + struct ipath_devdata *dd; + dma_addr_t dma_addr; + int ret; + + if (!idr_pre_get(&unit_table, GFP_KERNEL)) { + dd = ERR_PTR(-ENOMEM); + goto bail; + } + + dd = dma_alloc_coherent(&pdev->dev, sizeof(*dd), &dma_addr, + GFP_KERNEL); + + if (!dd) { + dd = ERR_PTR(-ENOMEM); + goto bail; + } + + dd->ipath_dma_addr = dma_addr; + dd->ipath_unit = -1; + + spin_lock_irqsave(&ipath_devs_lock, flags); + + ret = idr_get_new(&unit_table, dd, &dd->ipath_unit); + if (ret < 0) { + printk(KERN_ERR IPATH_DRV_NAME + ": Could not allocate unit ID: error %d\n", -ret); + ipath_free_devdata(pdev, dd); + dd = ERR_PTR(ret); + goto bail_unlock; + } + + dd->pcidev = pdev; + pci_set_drvdata(pdev, dd); + + list_add(&dd->ipath_list, &ipath_dev_list); + +bail_unlock: + spin_unlock_irqrestore(&ipath_devs_lock, flags); + +bail: + return dd; +} + +static inline struct ipath_devdata *__ipath_lookup(int unit) +{ + return idr_find(&unit_table, unit); +} + +struct ipath_devdata *ipath_lookup(int unit) +{ + struct ipath_devdata *dd; + unsigned long flags; + + spin_lock_irqsave(&ipath_devs_lock, flags); + dd = __ipath_lookup(unit); + spin_unlock_irqrestore(&ipath_devs_lock, flags); + + return dd; +} + +int ipath_count_units(int *npresentp, int *nupp, u32 *maxportsp) +{ + int nunits, npresent, nup; + struct ipath_devdata *dd; + unsigned long flags; + u32 maxports; + + nunits = npresent = nup = maxports = 0; + + spin_lock_irqsave(&ipath_devs_lock, flags); + + list_for_each_entry(dd, &ipath_dev_list, ipath_list) { + nunits++; + if ((dd->ipath_flags & IPATH_PRESENT) && dd->ipath_kregbase) + npresent++; + if (dd->ipath_lid && + !(dd->ipath_flags & (IPATH_DISABLED | IPATH_LINKDOWN + | IPATH_LINKUNK))) + nup++; + if (dd->ipath_cfgports > maxports) + maxports = dd->ipath_cfgports; + } + + spin_unlock_irqrestore(&ipath_devs_lock, flags); + + if (npresentp) + *npresentp = npresent; + if (nupp) + *nupp = nup; + if (maxportsp) + *maxportsp = maxports; + + return nunits; +} + +static int init_port0_rcvhdrtail(struct pci_dev *pdev) +{ + int ret; + + mutex_lock(&ipath_mutex); + + if (!ipath_port0_rcvhdrtail) { + ipath_port0_rcvhdrtail = + dma_alloc_coherent(&pdev->dev, + IPATH_PORT0_RCVHDRTAIL_SIZE, + &ipath_port0_rcvhdrtail_dma, + GFP_KERNEL); + + if (!ipath_port0_rcvhdrtail) { + ret = -ENOMEM; + goto bail; + } + } + port0_rcvhdrtail_refs++; + ret = 0; + +bail: + mutex_unlock(&ipath_mutex); + + return ret; +} + +static void cleanup_port0_rcvhdrtail(struct pci_dev *pdev) +{ + mutex_lock(&ipath_mutex); + + if (!--port0_rcvhdrtail_refs) { + dma_free_coherent(&pdev->dev, IPATH_PORT0_RCVHDRTAIL_SIZE, + (void *) ipath_port0_rcvhdrtail, + ipath_port0_rcvhdrtail_dma); + ipath_port0_rcvhdrtail = NULL; + } + + mutex_unlock(&ipath_mutex); +} + +/* + * These next two routines are placeholders in case we don't have per-arch + * code for controlling write combining. If explicit control of write + * combining is not available, performance will probably be awful. + */ + +int __attribute__((weak)) ipath_enable_wc(struct ipath_devdata *dd) +{ + return -EOPNOTSUPP; +} + +void __attribute__((weak)) ipath_disable_wc(struct ipath_devdata *dd) +{ +} + +static int __devinit ipath_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int ret, len, j; + struct ipath_devdata *dd; + unsigned long long addr; + u32 bar0 = 0, bar1 = 0; + u8 rev; + + ret = init_port0_rcvhdrtail(pdev); + if (ret < 0) { + printk(KERN_ERR IPATH_DRV_NAME + ": Could not allocate port0_rcvhdrtail: error %d\n", + -ret); + goto bail; + } + + dd = ipath_alloc_devdata(pdev); + if (IS_ERR(dd)) { + ret = PTR_ERR(dd); + printk(KERN_ERR IPATH_DRV_NAME + ": Could not allocate devdata: error %d\n", -ret); + goto bail_rcvhdrtail; + } + + ipath_cdbg(VERBOSE, "initializing unit #%u\n", dd->ipath_unit); + + read_bars(dd, pdev, &bar0, &bar1); + + ret = pci_enable_device(pdev); + if (ret) { + /* This can happen iff: + * + * We did a chip reset, and then failed to reprogram the + * BAR, or the chip reset due to an internal error. We then + * unloaded the driver and reloaded it. + * + * Both reset cases set the BAR back to initial state. For + * the latter case, the AER sticky error bit at offset 0x718 + * should be set, but the Linux kernel doesn't yet know + * about that, it appears. If the original BAR was retained + * in the kernel data structures, this may be OK. + */ + ipath_dev_err(dd, "enable unit %d failed: error %d\n", + dd->ipath_unit, -ret); + goto bail_devdata; + } + addr = pci_resource_start(pdev, 0); + len = pci_resource_len(pdev, 0); + ipath_cdbg(VERBOSE, "regbase (0) %llx len %d irq %x, vend %x/%x " + "driver_data %lx\n", addr, len, pdev->irq, ent->vendor, + ent->device, ent->driver_data); + + read_bars(dd, pdev, &bar0, &bar1); + + if (!bar1 && !(bar0 & ~0xf)) { + if (addr) { + dev_info(&pdev->dev, "BAR is 0 (probable RESET), " + "rewriting as %llx\n", addr); + ret = pci_write_config_dword( + pdev, PCI_BASE_ADDRESS_0, addr); + if (ret) { + ipath_dev_err(dd, "rewrite of BAR0 " + "failed: err %d\n", -ret); + goto bail_disable; + } + ret = pci_write_config_dword( + pdev, PCI_BASE_ADDRESS_1, addr >> 32); + if (ret) { + ipath_dev_err(dd, "rewrite of BAR1 " + "failed: err %d\n", -ret); + goto bail_disable; + } + } else { + ipath_dev_err(dd, "BAR is 0 (probable RESET), " + "not usable until reboot\n"); + ret = -ENODEV; + goto bail_disable; + } + } + + ret = pci_request_regions(pdev, IPATH_DRV_NAME); + if (ret) { + dev_info(&pdev->dev, "pci_request_regions unit %u fails: " + "err %d\n", dd->ipath_unit, -ret); + goto bail_disable; + } + + ret = pci_set_dma_mask(pdev, DMA_64BIT_MASK); + if (ret) { + dev_info(&pdev->dev, "pci_set_dma_mask unit %u " + "fails: %d\n", dd->ipath_unit, ret); + goto bail_regions; + } + + pci_set_master(pdev); + + /* + * Save BARs to rewrite after device reset. Save all 64 bits of + * BAR, just in case. + */ + dd->ipath_pcibar0 = addr; + dd->ipath_pcibar1 = addr >> 32; + dd->ipath_deviceid = ent->device; /* save for later use */ + dd->ipath_vendorid = ent->vendor; + + /* setup the chip-specific functions, as early as possible. */ + switch (ent->device) { + case PCI_DEVICE_ID_INFINIPATH_HT: + ipath_init_ht400_funcs(dd); + break; + case PCI_DEVICE_ID_INFINIPATH_PE800: + ipath_init_pe800_funcs(dd); + break; + default: + ipath_dev_err(dd, "Found unknown PathScale deviceid 0x%x, " + "failing\n", ent->device); + return -ENODEV; + } + + for (j = 0; j < 6; j++) { + if (!pdev->resource[j].start) + continue; + ipath_cdbg(VERBOSE, "BAR %d start %lx, end %lx, len %lx\n", + j, pdev->resource[j].start, + pdev->resource[j].end, + pci_resource_len(pdev, j)); + } + + if (!addr) { + ipath_dev_err(dd, "No valid address in BAR 0!\n"); + ret = -ENODEV; + goto bail_regions; + } + + dd->ipath_deviceid = ent->device; /* save for later use */ + dd->ipath_vendorid = ent->vendor; + + ret = pci_read_config_byte(pdev, PCI_REVISION_ID, &rev); + if (ret) { + ipath_dev_err(dd, "Failed to read PCI revision ID unit " + "%u: err %d\n", dd->ipath_unit, -ret); + goto bail_regions; /* shouldn't ever happen */ + } + dd->ipath_pcirev = rev; + + dd->ipath_kregbase = ioremap_nocache(addr, len); + + if (!dd->ipath_kregbase) { + ipath_dbg("Unable to map io addr %llx to kvirt, failing\n", + addr); + ret = -ENOMEM; + goto bail_iounmap; + } + dd->ipath_kregend = (u64 __iomem *) + ((void __iomem *)dd->ipath_kregbase + len); + dd->ipath_physaddr = addr; /* used for io_remap, etc. */ + /* for user mmap */ + dd->ipath_kregvirt = (u64 __iomem *) phys_to_virt(addr); + ipath_cdbg(VERBOSE, "mapped io addr %llx to kregbase %p " + "kregvirt %p\n", addr, dd->ipath_kregbase, + dd->ipath_kregvirt); + + /* + * clear ipath_flags here instead of in ipath_init_chip as it is set + * by ipath_setup_htconfig. + */ + dd->ipath_flags = 0; + + if (dd->ipath_f_bus(dd, pdev)) + ipath_dev_err(dd, "Failed to setup config space; " + "continuing anyway\n"); + + /* + * set up our interrupt handler; SA_SHIRQ probably not needed, + * since MSI interrupts shouldn't be shared but won't hurt for now. + * check 0 irq after we return from chip-specific bus setup, since + * that can affect this due to setup + */ + if (!pdev->irq) + ipath_dev_err(dd, "irq is 0, BIOS error? Interrupts won't " + "work\n"); + else { + ret = request_irq(pdev->irq, ipath_intr, SA_SHIRQ, + IPATH_DRV_NAME, dd); + if (ret) { + ipath_dev_err(dd, "Couldn't setup irq handler, " + "irq=%u: %d\n", pdev->irq, ret); + goto bail_iounmap; + } + } + + ret = ipath_init_chip(dd, 0); /* do the chip-specific init */ + if (ret) + goto bail_iounmap; + + ret = ipath_enable_wc(dd); + + if (ret) { + ipath_dev_err(dd, "Write combining not enabled " + "(err %d): performance may be poor\n", + -ret); + ret = 0; + } + + ipath_device_create_group(&pdev->dev, dd); + ipathfs_add_device(dd); + ipath_user_add(dd); + ipath_layer_add(dd); + + goto bail; + +bail_iounmap: + iounmap((volatile void __iomem *) dd->ipath_kregbase); + +bail_regions: + pci_release_regions(pdev); + +bail_disable: + pci_disable_device(pdev); + +bail_devdata: + ipath_free_devdata(pdev, dd); + +bail_rcvhdrtail: + cleanup_port0_rcvhdrtail(pdev); + +bail: + return ret; +} + +static void __devexit ipath_remove_one(struct pci_dev *pdev) +{ + struct ipath_devdata *dd; + + ipath_cdbg(VERBOSE, "removing, pdev=%p\n", pdev); + if (!pdev) + return; + + dd = pci_get_drvdata(pdev); + ipath_layer_del(dd); + ipath_user_del(dd); + ipathfs_remove_device(dd); + ipath_device_remove_group(&pdev->dev, dd); + ipath_cdbg(VERBOSE, "Releasing pci memory regions, dd %p, " + "unit %u\n", dd, (u32) dd->ipath_unit); + if (dd->ipath_kregbase) { + ipath_cdbg(VERBOSE, "Unmapping kregbase %p\n", + dd->ipath_kregbase); + iounmap((volatile void __iomem *) dd->ipath_kregbase); + dd->ipath_kregbase = NULL; + } + pci_release_regions(pdev); + ipath_cdbg(VERBOSE, "calling pci_disable_device\n"); + pci_disable_device(pdev); + + ipath_free_devdata(pdev, dd); + cleanup_port0_rcvhdrtail(pdev); +} + +/* general driver use */ +DEFINE_MUTEX(ipath_mutex); + +static DEFINE_SPINLOCK(ipath_pioavail_lock); + +/** + * ipath_disarm_piobufs - cancel a range of PIO buffers + * @dd: the infinipath device + * @first: the first PIO buffer to cancel + * @cnt: the number of PIO buffers to cancel + * + * cancel a range of PIO buffers, used when they might be armed, but + * not triggered. Used at init to ensure buffer state, and also user + * process close, in case it died while writing to a PIO buffer + * Also after errors. + */ +void ipath_disarm_piobufs(struct ipath_devdata *dd, unsigned first, + unsigned cnt) +{ + unsigned i, last = first + cnt; + u64 sendctrl, sendorig; + + ipath_cdbg(PKT, "disarm %u PIObufs first=%u\n", cnt, first); + sendorig = dd->ipath_sendctrl | INFINIPATH_S_DISARM; + for (i = first; i < last; i++) { + sendctrl = sendorig | + (i << INFINIPATH_S_DISARMPIOBUF_SHIFT); + ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, + sendctrl); + } + + /* + * Write it again with current value, in case ipath_sendctrl changed + * while we were looping; no critical bits that would require + * locking. + * + * Write a 0, and then the original value, reading scratch in + * between. This seems to avoid a chip timing race that causes + * pioavail updates to memory to stop. + */ + ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, + 0); + sendorig = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch); + ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, + dd->ipath_sendctrl); +} + +/** + * ipath_wait_linkstate - wait for an IB link state change to occur + * @dd: the infinipath device + * @state: the state to wait for + * @msecs: the number of milliseconds to wait + * + * wait up to msecs milliseconds for IB link state change to occur for + * now, take the easy polling route. Currently used only by + * ipath_layer_set_linkstate. Returns 0 if state reached, otherwise + * -ETIMEDOUT state can have multiple states set, for any of several + * transitions. + */ +int ipath_wait_linkstate(struct ipath_devdata *dd, u32 state, int msecs) +{ + dd->ipath_sma_state_wanted = state; + wait_event_interruptible_timeout(ipath_sma_state_wait, + (dd->ipath_flags & state), + msecs_to_jiffies(msecs)); + dd->ipath_sma_state_wanted = 0; + + if (!(dd->ipath_flags & state)) { + u64 val; + ipath_cdbg(SMA, "Didn't reach linkstate %s within %u ms\n", + /* test INIT ahead of DOWN, both can be set */ + (state & IPATH_LINKINIT) ? "INIT" : + ((state & IPATH_LINKDOWN) ? "DOWN" : + ((state & IPATH_LINKARMED) ? "ARM" : "ACTIVE")), + msecs); + val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_ibcstatus); + ipath_cdbg(VERBOSE, "ibcc=%llx ibcstatus=%llx (%s)\n", + (unsigned long long) ipath_read_kreg64( + dd, dd->ipath_kregs->kr_ibcctrl), + (unsigned long long) val, + ipath_ibcstatus_str[val & 0xf]); + } + return (dd->ipath_flags & state) ? 0 : -ETIMEDOUT; +} + +void ipath_decode_err(char *buf, size_t blen, ipath_err_t err) +{ + *buf = '\0'; + if (err & INFINIPATH_E_RHDRLEN) + strlcat(buf, "rhdrlen ", blen); + if (err & INFINIPATH_E_RBADTID) + strlcat(buf, "rbadtid ", blen); + if (err & INFINIPATH_E_RBADVERSION) + strlcat(buf, "rbadversion ", blen); + if (err & INFINIPATH_E_RHDR) + strlcat(buf, "rhdr ", blen); + if (err & INFINIPATH_E_RLONGPKTLEN) + strlcat(buf, "rlongpktlen ", blen); + if (err & INFINIPATH_E_RSHORTPKTLEN) + strlcat(buf, "rshortpktlen ", blen); + if (err & INFINIPATH_E_RMAXPKTLEN) + strlcat(buf, "rmaxpktlen ", blen); + if (err & INFINIPATH_E_RMINPKTLEN) + strlcat(buf, "rminpktlen ", blen); + if (err & INFINIPATH_E_RFORMATERR) + strlcat(buf, "rformaterr ", blen); + if (err & INFINIPATH_E_RUNSUPVL) + strlcat(buf, "runsupvl ", blen); + if (err & INFINIPATH_E_RUNEXPCHAR) + strlcat(buf, "runexpchar ", blen); + if (err & INFINIPATH_E_RIBFLOW) + strlcat(buf, "ribflow ", blen); + if (err & INFINIPATH_E_REBP) + strlcat(buf, "EBP ", blen); + if (err & INFINIPATH_E_SUNDERRUN) + strlcat(buf, "sunderrun ", blen); + if (err & INFINIPATH_E_SPIOARMLAUNCH) + strlcat(buf, "spioarmlaunch ", blen); + if (err & INFINIPATH_E_SUNEXPERRPKTNUM) + strlcat(buf, "sunexperrpktnum ", blen); + if (err & INFINIPATH_E_SDROPPEDDATAPKT) + strlcat(buf, "sdroppeddatapkt ", blen); + if (err & INFINIPATH_E_SDROPPEDSMPPKT) + strlcat(buf, "sdroppedsmppkt ", blen); + if (err & INFINIPATH_E_SMAXPKTLEN) + strlcat(buf, "smaxpktlen ", blen); + if (err & INFINIPATH_E_SMINPKTLEN) + strlcat(buf, "sminpktlen ", blen); + if (err & INFINIPATH_E_SUNSUPVL) + strlcat(buf, "sunsupVL ", blen); + if (err & INFINIPATH_E_SPKTLEN) + strlcat(buf, "spktlen ", blen); + if (err & INFINIPATH_E_INVALIDADDR) + strlcat(buf, "invalidaddr ", blen); + if (err & INFINIPATH_E_RICRC) + strlcat(buf, "CRC ", blen); + if (err & INFINIPATH_E_RVCRC) + strlcat(buf, "VCRC ", blen); + if (err & INFINIPATH_E_RRCVEGRFULL) + strlcat(buf, "rcvegrfull ", blen); + if (err & INFINIPATH_E_RRCVHDRFULL) + strlcat(buf, "rcvhdrfull ", blen); + if (err & INFINIPATH_E_IBSTATUSCHANGED) + strlcat(buf, "ibcstatuschg ", blen); + if (err & INFINIPATH_E_RIBLOSTLINK) + strlcat(buf, "riblostlink ", blen); + if (err & INFINIPATH_E_HARDWARE) + strlcat(buf, "hardware ", blen); + if (err & INFINIPATH_E_RESET) + strlcat(buf, "reset ", blen); +} + +/** + * get_rhf_errstring - decode RHF errors + * @err: the err number + * @msg: the output buffer + * @len: the length of the output buffer + * + * only used one place now, may want more later + */ +static void get_rhf_errstring(u32 err, char *msg, size_t len) +{ + /* if no errors, and so don't need to check what's first */ + *msg = '\0'; + + if (err & INFINIPATH_RHF_H_ICRCERR) + strlcat(msg, "icrcerr ", len); + if (err & INFINIPATH_RHF_H_VCRCERR) + strlcat(msg, "vcrcerr ", len); + if (err & INFINIPATH_RHF_H_PARITYERR) + strlcat(msg, "parityerr ", len); + if (err & INFINIPATH_RHF_H_LENERR) + strlcat(msg, "lenerr ", len); + if (err & INFINIPATH_RHF_H_MTUERR) + strlcat(msg, "mtuerr ", len); + if (err & INFINIPATH_RHF_H_IHDRERR) + /* infinipath hdr checksum error */ + strlcat(msg, "ipathhdrerr ", len); + if (err & INFINIPATH_RHF_H_TIDERR) + strlcat(msg, "tiderr ", len); + if (err & INFINIPATH_RHF_H_MKERR) + /* bad port, offset, etc. */ + strlcat(msg, "invalid ipathhdr ", len); + if (err & INFINIPATH_RHF_H_IBERR) + strlcat(msg, "iberr ", len); + if (err & INFINIPATH_RHF_L_SWA) + strlcat(msg, "swA ", len); + if (err & INFINIPATH_RHF_L_SWB) + strlcat(msg, "swB ", len); +} + +/** + * ipath_get_egrbuf - get an eager buffer + * @dd: the infinipath device + * @bufnum: the eager buffer to get + * @err: unused + * + * must only be called if ipath_pd[port] is known to be allocated + */ +static inline void *ipath_get_egrbuf(struct ipath_devdata *dd, u32 bufnum, + int err) +{ + return dd->ipath_port0_skbs ? + (void *)dd->ipath_port0_skbs[bufnum]->data : NULL; +} + +/** + * ipath_alloc_skb - allocate an skb and buffer with possible constraints + * @dd: the infinipath device + * @gfp_mask: the sk_buff SFP mask + */ +struct sk_buff *ipath_alloc_skb(struct ipath_devdata *dd, + gfp_t gfp_mask) +{ + struct sk_buff *skb; + u32 len; + + /* + * Only fully supported way to handle this is to allocate lots + * extra, align as needed, and then do skb_reserve(). That wastes + * a lot of memory... I'll have to hack this into infinipath_copy + * also. + */ + + /* + * We need 4 extra bytes for unaligned transfer copying + */ + if (dd->ipath_flags & IPATH_4BYTE_TID) { + /* we need a 4KB multiple alignment, and there is no way + * to do it except to allocate extra and then skb_reserve + * enough to bring it up to the right alignment. + */ + len = dd->ipath_ibmaxlen + 4 + (1 << 11) - 1; + } + else + len = dd->ipath_ibmaxlen + 4; + skb = __dev_alloc_skb(len, gfp_mask); + if (!skb) { + ipath_dev_err(dd, "Failed to allocate skbuff, length %u\n", + len); + goto bail; + } + if (dd->ipath_flags & IPATH_4BYTE_TID) { + u32 una = ((1 << 11) - 1) & (unsigned long)(skb->data + 4); + if (una) + skb_reserve(skb, 4 + (1 << 11) - una); + else + skb_reserve(skb, 4); + } else + skb_reserve(skb, 4); + +bail: + return skb; +} + +/** + * ipath_rcv_layer - receive a packet for the layered (ethernet) driver + * @dd: the infinipath device + * @etail: the sk_buff number + * @tlen: the total packet length + * @hdr: the ethernet header + * + * Separate routine for better overall optimization + */ +static void ipath_rcv_layer(struct ipath_devdata *dd, u32 etail, + u32 tlen, struct ether_header *hdr) +{ + u32 elen; + u8 pad, *bthbytes; + struct sk_buff *skb, *nskb; + + if (dd->ipath_port0_skbs && hdr->sub_opcode == OPCODE_ENCAP) { + /* + * Allocate a new sk_buff to replace the one we give + * to the network stack. + */ + nskb = ipath_alloc_skb(dd, GFP_ATOMIC); + if (!nskb) { + /* count OK packets that we drop */ + ipath_stats.sps_krdrops++; + return; + } + + bthbytes = (u8 *) hdr->bth; + pad = (bthbytes[1] >> 4) & 3; + /* +CRC32 */ + elen = tlen - (sizeof(*hdr) + pad + sizeof(u32)); + + skb = dd->ipath_port0_skbs[etail]; + dd->ipath_port0_skbs[etail] = nskb; + skb_put(skb, elen); + + dd->ipath_f_put_tid(dd, etail + (u64 __iomem *) + ((char __iomem *) dd->ipath_kregbase + + dd->ipath_rcvegrbase), 0, + virt_to_phys(nskb->data)); + + __ipath_layer_rcv(dd, hdr, skb); + + /* another ether packet received */ + ipath_stats.sps_ether_rpkts++; + } + else if (hdr->sub_opcode == OPCODE_LID_ARP) + __ipath_layer_rcv_lid(dd, hdr); +} + +/* + * ipath_kreceive - receive a packet + * @dd: the infinipath device + * + * called from interrupt handler for errors or receive interrupt + */ +void ipath_kreceive(struct ipath_devdata *dd) +{ + u64 *rc; + void *ebuf; + const u32 rsize = dd->ipath_rcvhdrentsize; /* words */ + const u32 maxcnt = dd->ipath_rcvhdrcnt * rsize; /* words */ + u32 etail = -1, l, hdrqtail; + struct ips_message_header *hdr; + u32 eflags, i, etype, tlen, pkttot = 0; + static u64 totcalls; /* stats, may eventually remove */ + char emsg[128]; + + if (!dd->ipath_hdrqtailptr) { + ipath_dev_err(dd, + "hdrqtailptr not set, can't do receives\n"); + goto bail; + } + + /* There is already a thread processing this queue. */ + if (test_and_set_bit(0, &dd->ipath_rcv_pending)) + goto bail; + + if (dd->ipath_port0head == + (u32)le64_to_cpu(*dd->ipath_hdrqtailptr)) + goto done; + +gotmore: + /* + * read only once at start. If in flood situation, this helps + * performance slightly. If more arrive while we are processing, + * we'll come back here and do them + */ + hdrqtail = (u32)le64_to_cpu(*dd->ipath_hdrqtailptr); + + for (i = 0, l = dd->ipath_port0head; l != hdrqtail; i++) { + u32 qp; + u8 *bthbytes; + + rc = (u64 *) (dd->ipath_pd[0]->port_rcvhdrq + (l << 2)); + hdr = (struct ips_message_header *)&rc[1]; + /* + * could make a network order version of IPATH_KD_QP, and + * do the obvious shift before masking to speed this up. + */ + qp = ntohl(hdr->bth[1]) & 0xffffff; + bthbytes = (u8 *) hdr->bth; + + eflags = ips_get_hdr_err_flags((__le32 *) rc); + etype = ips_get_rcv_type((__le32 *) rc); + /* total length */ + tlen = ips_get_length_in_bytes((__le32 *) rc); + ebuf = NULL; + if (etype != RCVHQ_RCV_TYPE_EXPECTED) { + /* + * it turns out that the chips uses an eager buffer + * for all non-expected packets, whether it "needs" + * one or not. So always get the index, but don't + * set ebuf (so we try to copy data) unless the + * length requires it. + */ + etail = ips_get_index((__le32 *) rc); + if (tlen > sizeof(*hdr) || + etype == RCVHQ_RCV_TYPE_NON_KD) + ebuf = ipath_get_egrbuf(dd, etail, 0); + } + + /* + * both tiderr and ipathhdrerr are set for all plain IB + * packets; only ipathhdrerr should be set. + */ + + if (etype != RCVHQ_RCV_TYPE_NON_KD && etype != + RCVHQ_RCV_TYPE_ERROR && ips_get_ipath_ver( + hdr->iph.ver_port_tid_offset) != + IPS_PROTO_VERSION) { + ipath_cdbg(PKT, "Bad InfiniPath protocol version " + "%x\n", etype); + } + + if (eflags & ~(INFINIPATH_RHF_H_TIDERR | + INFINIPATH_RHF_H_IHDRERR)) { + get_rhf_errstring(eflags, emsg, sizeof emsg); + ipath_cdbg(PKT, "RHFerrs %x hdrqtail=%x typ=%u " + "tlen=%x opcode=%x egridx=%x: %s\n", + eflags, l, etype, tlen, bthbytes[0], + ips_get_index((__le32 *) rc), emsg); + } else if (etype == RCVHQ_RCV_TYPE_NON_KD) { + int ret = __ipath_verbs_rcv(dd, rc + 1, + ebuf, tlen); + if (ret == -ENODEV) + ipath_cdbg(VERBOSE, + "received IB packet, " + "not SMA (QP=%x)\n", qp); + } else if (etype == RCVHQ_RCV_TYPE_EAGER) { + if (qp == IPATH_KD_QP && + bthbytes[0] == ipath_layer_rcv_opcode && + ebuf) + ipath_rcv_layer(dd, etail, tlen, + (struct ether_header *)hdr); + else + ipath_cdbg(PKT, "typ %x, opcode %x (eager, " + "qp=%x), len %x; ignored\n", + etype, bthbytes[0], qp, tlen); + } + else if (etype == RCVHQ_RCV_TYPE_EXPECTED) + ipath_dbg("Bug: Expected TID, opcode %x; ignored\n", + be32_to_cpu(hdr->bth[0]) & 0xff); + else if (eflags & (INFINIPATH_RHF_H_TIDERR | + INFINIPATH_RHF_H_IHDRERR)) { + /* + * This is a type 3 packet, only the LRH is in the + * rcvhdrq, the rest of the header is in the eager + * buffer. + */ + u8 opcode; + if (ebuf) { + bthbytes = (u8 *) ebuf; + opcode = *bthbytes; + } + else + opcode = 0; + get_rhf_errstring(eflags, emsg, sizeof emsg); + ipath_dbg("Err %x (%s), opcode %x, egrbuf %x, " + "len %x\n", eflags, emsg, opcode, etail, + tlen); + } else { + /* + * error packet, type of error unknown. + * Probably type 3, but we don't know, so don't + * even try to print the opcode, etc. + */ + ipath_dbg("Error Pkt, but no eflags! egrbuf %x, " + "len %x\nhdrq@%lx;hdrq+%x rhf: %llx; " + "hdr %llx %llx %llx %llx %llx\n", + etail, tlen, (unsigned long) rc, l, + (unsigned long long) rc[0], + (unsigned long long) rc[1], + (unsigned long long) rc[2], + (unsigned long long) rc[3], + (unsigned long long) rc[4], + (unsigned long long) rc[5]); + } + l += rsize; + if (l >= maxcnt) + l = 0; + /* + * update for each packet, to help prevent overflows if we + * have lots of packets. + */ + (void)ipath_write_ureg(dd, ur_rcvhdrhead, + dd->ipath_rhdrhead_intr_off | l, 0); + if (etype != RCVHQ_RCV_TYPE_EXPECTED) + (void)ipath_write_ureg(dd, ur_rcvegrindexhead, + etail, 0); + } + + pkttot += i; + + dd->ipath_port0head = l; + + if (hdrqtail != (u32)le64_to_cpu(*dd->ipath_hdrqtailptr)) + /* more arrived while we handled first batch */ + goto gotmore; + + if (pkttot > ipath_stats.sps_maxpkts_call) + ipath_stats.sps_maxpkts_call = pkttot; + ipath_stats.sps_port0pkts += pkttot; + ipath_stats.sps_avgpkts_call = + ipath_stats.sps_port0pkts / ++totcalls; + +done: + clear_bit(0, &dd->ipath_rcv_pending); + smp_mb__after_clear_bit(); + +bail:; +} + +/** + * ipath_update_pio_bufs - update shadow copy of the PIO availability map + * @dd: the infinipath device + * + * called whenever our local copy indicates we have run out of send buffers + * NOTE: This can be called from interrupt context by some code + * and from non-interrupt context by ipath_getpiobuf(). + */ + +static void ipath_update_pio_bufs(struct ipath_devdata *dd) +{ + unsigned long flags; + int i; + const unsigned piobregs = (unsigned)dd->ipath_pioavregs; + + /* If the generation (check) bits have changed, then we update the + * busy bit for the corresponding PIO buffer. This algorithm will + * modify positions to the value they already have in some cases + * (i.e., no change), but it's faster than changing only the bits + * that have changed. + * + * We would like to do this atomicly, to avoid spinlocks in the + * critical send path, but that's not really possible, given the + * type of changes, and that this routine could be called on + * multiple cpu's simultaneously, so we lock in this routine only, + * to avoid conflicting updates; all we change is the shadow, and + * it's a single 64 bit memory location, so by definition the update + * is atomic in terms of what other cpu's can see in testing the + * bits. The spin_lock overhead isn't too bad, since it only + * happens when all buffers are in use, so only cpu overhead, not + * latency or bandwidth is affected. + */ +#define _IPATH_ALL_CHECKBITS 0x5555555555555555ULL + if (!dd->ipath_pioavailregs_dma) { + ipath_dbg("Update shadow pioavail, but regs_dma NULL!\n"); + return; + } + if (ipath_debug & __IPATH_VERBDBG) { + /* only if packet debug and verbose */ + volatile __le64 *dma = dd->ipath_pioavailregs_dma; + unsigned long *shadow = dd->ipath_pioavailshadow; + + ipath_cdbg(PKT, "Refill avail, dma0=%llx shad0=%lx, " + "d1=%llx s1=%lx, d2=%llx s2=%lx, d3=%llx " + "s3=%lx\n", + (unsigned long long) le64_to_cpu(dma[0]), + shadow[0], + (unsigned long long) le64_to_cpu(dma[1]), + shadow[1], + (unsigned long long) le64_to_cpu(dma[2]), + shadow[2], + (unsigned long long) le64_to_cpu(dma[3]), + shadow[3]); + if (piobregs > 4) + ipath_cdbg( + PKT, "2nd group, dma4=%llx shad4=%lx, " + "d5=%llx s5=%lx, d6=%llx s6=%lx, " + "d7=%llx s7=%lx\n", + (unsigned long long) le64_to_cpu(dma[4]), + shadow[4], + (unsigned long long) le64_to_cpu(dma[5]), + shadow[5], + (unsigned long long) le64_to_cpu(dma[6]), + shadow[6], + (unsigned long long) le64_to_cpu(dma[7]), + shadow[7]); + } + spin_lock_irqsave(&ipath_pioavail_lock, flags); + for (i = 0; i < piobregs; i++) { + u64 pchbusy, pchg, piov, pnew; + /* + * Chip Errata: bug 6641; even and odd qwords>3 are swapped + */ + if (i > 3) { + if (i & 1) + piov = le64_to_cpu( + dd->ipath_pioavailregs_dma[i - 1]); + else + piov = le64_to_cpu( + dd->ipath_pioavailregs_dma[i + 1]); + } else + piov = le64_to_cpu(dd->ipath_pioavailregs_dma[i]); + pchg = _IPATH_ALL_CHECKBITS & + ~(dd->ipath_pioavailshadow[i] ^ piov); + pchbusy = pchg << INFINIPATH_SENDPIOAVAIL_BUSY_SHIFT; + if (pchg && (pchbusy & dd->ipath_pioavailshadow[i])) { + pnew = dd->ipath_pioavailshadow[i] & ~pchbusy; + pnew |= piov & pchbusy; + dd->ipath_pioavailshadow[i] = pnew; + } + } + spin_unlock_irqrestore(&ipath_pioavail_lock, flags); +} + +/** + * ipath_setrcvhdrsize - set the receive header size + * @dd: the infinipath device + * @rhdrsize: the receive header size + * + * called from user init code, and also layered driver init + */ +int ipath_setrcvhdrsize(struct ipath_devdata *dd, unsigned rhdrsize) +{ + int ret = 0; + + if (dd->ipath_flags & IPATH_RCVHDRSZ_SET) { + if (dd->ipath_rcvhdrsize != rhdrsize) { + dev_info(&dd->pcidev->dev, + "Error: can't set protocol header " + "size %u, already %u\n", + rhdrsize, dd->ipath_rcvhdrsize); + ret = -EAGAIN; + } else + ipath_cdbg(VERBOSE, "Reuse same protocol header " + "size %u\n", dd->ipath_rcvhdrsize); + } else if (rhdrsize > (dd->ipath_rcvhdrentsize - + (sizeof(u64) / sizeof(u32)))) { + ipath_dbg("Error: can't set protocol header size %u " + "(> max %u)\n", rhdrsize, + dd->ipath_rcvhdrentsize - + (u32) (sizeof(u64) / sizeof(u32))); + ret = -EOVERFLOW; + } else { + dd->ipath_flags |= IPATH_RCVHDRSZ_SET; + dd->ipath_rcvhdrsize = rhdrsize; + ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvhdrsize, + dd->ipath_rcvhdrsize); + ipath_cdbg(VERBOSE, "Set protocol header size to %u\n", + dd->ipath_rcvhdrsize); + } + return ret; +} + +/** + * ipath_getpiobuf - find an available pio buffer + * @dd: the infinipath device + * @pbufnum: the buffer number is placed here + * + * do appropriate marking as busy, etc. + * returns buffer number if one found (>=0), negative number is error. + * Used by ipath_sma_send_pkt and ipath_layer_send + */ +u32 __iomem *ipath_getpiobuf(struct ipath_devdata *dd, u32 * pbufnum) +{ + int i, j, starti, updated = 0; + unsigned piobcnt, iter; + unsigned long flags; + unsigned long *shadow = dd->ipath_pioavailshadow; + u32 __iomem *buf; + + piobcnt = (unsigned)(dd->ipath_piobcnt2k + + dd->ipath_piobcnt4k); + starti = dd->ipath_lastport_piobuf; + iter = piobcnt - starti; + if (dd->ipath_upd_pio_shadow) { + /* + * Minor optimization. If we had no buffers on last call, + * start out by doing the update; continue and do scan even + * if no buffers were updated, to be paranoid + */ + ipath_update_pio_bufs(dd); + /* we scanned here, don't do it at end of scan */ + updated = 1; + i = starti; + } else + i = dd->ipath_lastpioindex; + +rescan: + /* + * while test_and_set_bit() is atomic, we do that and then the + * change_bit(), and the pair is not. See if this is the cause + * of the remaining armlaunch errors. + */ + spin_lock_irqsave(&ipath_pioavail_lock, flags); + for (j = 0; j < iter; j++, i++) { + if (i >= piobcnt) + i = starti; + /* + * To avoid bus lock overhead, we first find a candidate + * buffer, then do the test and set, and continue if that + * fails. + */ + if (test_bit((2 * i) + 1, shadow) || + test_and_set_bit((2 * i) + 1, shadow)) + continue; + /* flip generation bit */ + change_bit(2 * i, shadow); + break; + } + spin_unlock_irqrestore(&ipath_pioavail_lock, flags); + + if (j == iter) { + volatile __le64 *dma = dd->ipath_pioavailregs_dma; + + /* + * first time through; shadow exhausted, but may be real + * buffers available, so go see; if any updated, rescan + * (once) + */ + if (!updated) { + ipath_update_pio_bufs(dd); + updated = 1; + i = starti; + goto rescan; + } + dd->ipath_upd_pio_shadow = 1; + /* + * not atomic, but if we lose one once in a while, that's OK + */ + ipath_stats.sps_nopiobufs++; + if (!(++dd->ipath_consec_nopiobuf % 100000)) { + ipath_dbg( + "%u pio sends with no bufavail; dmacopy: " + "%llx %llx %llx %llx; shadow: " + "%lx %lx %lx %lx\n", + dd->ipath_consec_nopiobuf, + (unsigned long long) le64_to_cpu(dma[0]), + (unsigned long long) le64_to_cpu(dma[1]), + (unsigned long long) le64_to_cpu(dma[2]), + (unsigned long long) le64_to_cpu(dma[3]), + shadow[0], shadow[1], shadow[2], + shadow[3]); + /* + * 4 buffers per byte, 4 registers above, cover rest + * below + */ + if ((dd->ipath_piobcnt2k + dd->ipath_piobcnt4k) > + (sizeof(shadow[0]) * 4 * 4)) + ipath_dbg("2nd group: dmacopy: %llx %llx " + "%llx %llx; shadow: %lx %lx " + "%lx %lx\n", + (unsigned long long) + le64_to_cpu(dma[4]), + (unsigned long long) + le64_to_cpu(dma[5]), + (unsigned long long) + le64_to_cpu(dma[6]), + (unsigned long long) + le64_to_cpu(dma[7]), + shadow[4], shadow[5], + shadow[6], shadow[7]); + } + buf = NULL; + goto bail; + } + + if (updated) + /* + * ran out of bufs, now some (at least this one we just + * got) are now available, so tell the layered driver. + */ + __ipath_layer_intr(dd, IPATH_LAYER_INT_SEND_CONTINUE); + + /* + * set next starting place. Since it's just an optimization, + * it doesn't matter who wins on this, so no locking + */ + dd->ipath_lastpioindex = i + 1; + if (dd->ipath_upd_pio_shadow) + dd->ipath_upd_pio_shadow = 0; + if (dd->ipath_consec_nopiobuf) + dd->ipath_consec_nopiobuf = 0; + if (i < dd->ipath_piobcnt2k) + buf = (u32 __iomem *) (dd->ipath_pio2kbase + + i * dd->ipath_palign); + else + buf = (u32 __iomem *) + (dd->ipath_pio4kbase + + (i - dd->ipath_piobcnt2k) * dd->ipath_4kalign); + ipath_cdbg(VERBOSE, "Return piobuf%u %uk @ %p\n", + i, (i < dd->ipath_piobcnt2k) ? 2 : 4, buf); + if (pbufnum) + *pbufnum = i; + +bail: + return buf; +} + +/** + * ipath_create_rcvhdrq - create a receive header queue + * @dd: the infinipath device + * @pd: the port data + * + * this *must* be physically contiguous memory, and for now, + * that limits it to what kmalloc can do. + */ +int ipath_create_rcvhdrq(struct ipath_devdata *dd, + struct ipath_portdata *pd) +{ + int ret = 0, amt; + + amt = ALIGN(dd->ipath_rcvhdrcnt * dd->ipath_rcvhdrentsize * + sizeof(u32), PAGE_SIZE); + if (!pd->port_rcvhdrq) { + /* + * not using REPEAT isn't viable; at 128KB, we can easily + * fail this. The problem with REPEAT is we can block here + * "forever". There isn't an inbetween, unfortunately. We + * could reduce the risk by never freeing the rcvhdrq except + * at unload, but even then, the first time a port is used, + * we could delay for some time... + */ + gfp_t gfp_flags = GFP_USER | __GFP_COMP; + + pd->port_rcvhdrq = dma_alloc_coherent( + &dd->pcidev->dev, amt, &pd->port_rcvhdrq_phys, + gfp_flags); + + if (!pd->port_rcvhdrq) { + ipath_dev_err(dd, "attempt to allocate %d bytes " + "for port %u rcvhdrq failed\n", + amt, pd->port_port); + ret = -ENOMEM; + goto bail; + } + + pd->port_rcvhdrq_size = amt; + + ipath_cdbg(VERBOSE, "%d pages at %p (phys %lx) size=%lu " + "for port %u rcvhdr Q\n", + amt >> PAGE_SHIFT, pd->port_rcvhdrq, + (unsigned long) pd->port_rcvhdrq_phys, + (unsigned long) pd->port_rcvhdrq_size, + pd->port_port); + } else { + /* + * clear for security, sanity, and/or debugging, each + * time we reuse + */ + memset(pd->port_rcvhdrq, 0, amt); + } + + /* + * tell chip each time we init it, even if we are re-using previous + * memory (we zero it at process close) + */ + ipath_cdbg(VERBOSE, "writing port %d rcvhdraddr as %lx\n", + pd->port_port, (unsigned long) pd->port_rcvhdrq_phys); + ipath_write_kreg_port(dd, dd->ipath_kregs->kr_rcvhdraddr, + pd->port_port, pd->port_rcvhdrq_phys); + + ret = 0; +bail: + return ret; +} + +int ipath_waitfor_complete(struct ipath_devdata *dd, ipath_kreg reg_id, + u64 bits_to_wait_for, u64 * valp) +{ + unsigned long timeout; + u64 lastval, val; + int ret; + + lastval = ipath_read_kreg64(dd, reg_id); + /* wait a ridiculously long time */ + timeout = jiffies + msecs_to_jiffies(5); + do { + val = ipath_read_kreg64(dd, reg_id); + /* set so they have something, even on failures. */ + *valp = val; + if ((val & bits_to_wait_for) == bits_to_wait_for) { + ret = 0; + break; + } + if (val != lastval) + ipath_cdbg(VERBOSE, "Changed from %llx to %llx, " + "waiting for %llx bits\n", + (unsigned long long) lastval, + (unsigned long long) val, + (unsigned long long) bits_to_wait_for); + cond_resched(); + if (time_after(jiffies, timeout)) { + ipath_dbg("Didn't get bits %llx in register 0x%x, " + "got %llx\n", + (unsigned long long) bits_to_wait_for, + reg_id, (unsigned long long) *valp); + ret = -ENODEV; + break; + } + } while (1); + + return ret; +} + +/** + * ipath_waitfor_mdio_cmdready - wait for last command to complete + * @dd: the infinipath device + * + * Like ipath_waitfor_complete(), but we wait for the CMDVALID bit to go + * away indicating the last command has completed. It doesn't return data + */ +int ipath_waitfor_mdio_cmdready(struct ipath_devdata *dd) +{ + unsigned long timeout; + u64 val; + int ret; + + /* wait a ridiculously long time */ + timeout = jiffies + msecs_to_jiffies(5); + do { + val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_mdio); + if (!(val & IPATH_MDIO_CMDVALID)) { + ret = 0; + break; + } + cond_resched(); + if (time_after(jiffies, timeout)) { + ipath_dbg("CMDVALID stuck in mdio reg? (%llx)\n", + (unsigned long long) val); + ret = -ENODEV; + break; + } + } while (1); + + return ret; +} + +void ipath_set_ib_lstate(struct ipath_devdata *dd, int which) +{ + static const char *what[4] = { + [0] = "DOWN", + [INFINIPATH_IBCC_LINKCMD_INIT] = "INIT", + [INFINIPATH_IBCC_LINKCMD_ARMED] = "ARMED", + [INFINIPATH_IBCC_LINKCMD_ACTIVE] = "ACTIVE" + }; + ipath_cdbg(SMA, "Trying to move unit %u to %s, current ltstate " + "is %s\n", dd->ipath_unit, + what[(which >> INFINIPATH_IBCC_LINKCMD_SHIFT) & + INFINIPATH_IBCC_LINKCMD_MASK], + ipath_ibcstatus_str[ + (ipath_read_kreg64 + (dd, dd->ipath_kregs->kr_ibcstatus) >> + INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT) & + INFINIPATH_IBCS_LINKTRAININGSTATE_MASK]); + + ipath_write_kreg(dd, dd->ipath_kregs->kr_ibcctrl, + dd->ipath_ibcctrl | which); +} + +/** + * ipath_read_kreg64_port - read a device's per-port 64-bit kernel register + * @dd: the infinipath device + * @regno: the register number to read + * @port: the port containing the register + * + * Registers that vary with the chip implementation constants (port) + * use this routine. + */ +u64 ipath_read_kreg64_port(const struct ipath_devdata *dd, ipath_kreg regno, + unsigned port) +{ + u16 where; + + if (port < dd->ipath_portcnt && + (regno == dd->ipath_kregs->kr_rcvhdraddr || + regno == dd->ipath_kregs->kr_rcvhdrtailaddr)) + where = regno + port; + else + where = -1; + + return ipath_read_kreg64(dd, where); +} + +/** + * ipath_write_kreg_port - write a device's per-port 64-bit kernel register + * @dd: the infinipath device + * @regno: the register number to write + * @port: the port containing the register + * @value: the value to write + * + * Registers that vary with the chip implementation constants (port) + * use this routine. + */ +void ipath_write_kreg_port(const struct ipath_devdata *dd, ipath_kreg regno, + unsigned port, u64 value) +{ + u16 where; + + if (port < dd->ipath_portcnt && + (regno == dd->ipath_kregs->kr_rcvhdraddr || + regno == dd->ipath_kregs->kr_rcvhdrtailaddr)) + where = regno + port; + else + where = -1; + + ipath_write_kreg(dd, where, value); +} + +/** + * ipath_shutdown_device - shut down a device + * @dd: the infinipath device + * + * This is called to make the device quiet when we are about to + * unload the driver, and also when the device is administratively + * disabled. It does not free any data structures. + * Everything it does has to be setup again by ipath_init_chip(dd,1) + */ +void ipath_shutdown_device(struct ipath_devdata *dd) +{ + u64 val; + + ipath_dbg("Shutting down the device\n"); + + dd->ipath_flags |= IPATH_LINKUNK; + dd->ipath_flags &= ~(IPATH_INITTED | IPATH_LINKDOWN | + IPATH_LINKINIT | IPATH_LINKARMED | + IPATH_LINKACTIVE); + *dd->ipath_statusp &= ~(IPATH_STATUS_IB_CONF | + IPATH_STATUS_IB_READY); + + /* mask interrupts, but not errors */ + ipath_write_kreg(dd, dd->ipath_kregs->kr_intmask, 0ULL); + + dd->ipath_rcvctrl = 0; + ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, + dd->ipath_rcvctrl); + + /* + * gracefully stop all sends allowing any in progress to trickle out + * first. + */ + ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, 0ULL); + /* flush it */ + val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch); + /* + * enough for anything that's going to trickle out to have actually + * done so. + */ + udelay(5); + + /* + * abort any armed or launched PIO buffers that didn't go. (self + * clearing). Will cause any packet currently being transmitted to + * go out with an EBP, and may also cause a short packet error on + * the receiver. + */ + ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, + INFINIPATH_S_ABORT); + + ipath_set_ib_lstate(dd, INFINIPATH_IBCC_LINKINITCMD_DISABLE << + INFINIPATH_IBCC_LINKINITCMD_SHIFT); + + /* + * we are shutting down, so tell the layered driver. We don't do + * this on just a link state change, much like ethernet, a cable + * unplug, etc. doesn't change driver state + */ + ipath_layer_intr(dd, IPATH_LAYER_INT_IF_DOWN); + + /* disable IBC */ + dd->ipath_control &= ~INFINIPATH_C_LINKENABLE; + ipath_write_kreg(dd, dd->ipath_kregs->kr_control, + dd->ipath_control); + + /* + * clear SerdesEnable and turn the leds off; do this here because + * we are unloading, so don't count on interrupts to move along + * Turn the LEDs off explictly for the same reason. + */ + dd->ipath_f_quiet_serdes(dd); + dd->ipath_f_setextled(dd, 0, 0); + + if (dd->ipath_stats_timer_active) { + del_timer_sync(&dd->ipath_stats_timer); + dd->ipath_stats_timer_active = 0; + } + + /* + * clear all interrupts and errors, so that the next time the driver + * is loaded or device is enabled, we know that whatever is set + * happened while we were unloaded + */ + ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrclear, + ~0ULL & ~INFINIPATH_HWE_MEMBISTFAILED); + ipath_write_kreg(dd, dd->ipath_kregs->kr_errorclear, -1LL); + ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, -1LL); +} + +/** + * ipath_free_pddata - free a port's allocated data + * @dd: the infinipath device + * @port: the port + * @freehdrq: free the port data structure if true + * + * when closing, free up any allocated data for a port, if the + * reference count goes to zero + * Note: this also optionally frees the portdata itself! + * Any changes here have to be matched up with the reinit case + * of ipath_init_chip(), which calls this routine on reinit after reset. + */ +void ipath_free_pddata(struct ipath_devdata *dd, u32 port, int freehdrq) +{ + struct ipath_portdata *pd = dd->ipath_pd[port]; + + if (!pd) + return; + if (freehdrq) + /* + * only clear and free portdata if we are going to also + * release the hdrq, otherwise we leak the hdrq on each + * open/close cycle + */ + dd->ipath_pd[port] = NULL; + if (freehdrq && pd->port_rcvhdrq) { + ipath_cdbg(VERBOSE, "free closed port %d rcvhdrq @ %p " + "(size=%lu)\n", pd->port_port, pd->port_rcvhdrq, + (unsigned long) pd->port_rcvhdrq_size); + dma_free_coherent(&dd->pcidev->dev, pd->port_rcvhdrq_size, + pd->port_rcvhdrq, pd->port_rcvhdrq_phys); + pd->port_rcvhdrq = NULL; + } + if (port && pd->port_rcvegrbuf) { + /* always free this */ + if (pd->port_rcvegrbuf) { + unsigned e; + + for (e = 0; e < pd->port_rcvegrbuf_chunks; e++) { + void *base = pd->port_rcvegrbuf[e]; + size_t size = pd->port_rcvegrbuf_size; + + ipath_cdbg(VERBOSE, "egrbuf free(%p, %lu), " + "chunk %u/%u\n", base, + (unsigned long) size, + e, pd->port_rcvegrbuf_chunks); + dma_free_coherent( + &dd->pcidev->dev, size, base, + pd->port_rcvegrbuf_phys[e]); + } + vfree(pd->port_rcvegrbuf); + pd->port_rcvegrbuf = NULL; + vfree(pd->port_rcvegrbuf_phys); + pd->port_rcvegrbuf_phys = NULL; + } + pd->port_rcvegrbuf_chunks = 0; + } else if (port == 0 && dd->ipath_port0_skbs) { + unsigned e; + struct sk_buff **skbs = dd->ipath_port0_skbs; + + dd->ipath_port0_skbs = NULL; + ipath_cdbg(VERBOSE, "free closed port %d ipath_port0_skbs " + "@ %p\n", pd->port_port, skbs); + for (e = 0; e < dd->ipath_rcvegrcnt; e++) + if (skbs[e]) + dev_kfree_skb(skbs[e]); + vfree(skbs); + } + if (freehdrq) { + kfree(pd->port_tid_pg_list); + kfree(pd); + } +} + +int __init infinipath_init(void) +{ + int ret; + + ipath_dbg(KERN_INFO DRIVER_LOAD_MSG "%s", ipath_core_version); + + /* + * These must be called before the driver is registered with + * the PCI subsystem. + */ + idr_init(&unit_table); + if (!idr_pre_get(&unit_table, GFP_KERNEL)) { + ret = -ENOMEM; + goto bail; + } + + ret = pci_register_driver(&ipath_driver); + if (ret < 0) { + printk(KERN_ERR IPATH_DRV_NAME + ": Unable to register driver: error %d\n", -ret); + goto bail_unit; + } + + ret = ipath_driver_create_group(&ipath_driver.driver); + if (ret < 0) { + printk(KERN_ERR IPATH_DRV_NAME ": Unable to create driver " + "sysfs entries: error %d\n", -ret); + goto bail_pci; + } + + ret = ipath_init_ipathfs(); + if (ret < 0) { + printk(KERN_ERR IPATH_DRV_NAME ": Unable to create " + "ipathfs: error %d\n", -ret); + goto bail_group; + } + + goto bail; + +bail_group: + ipath_driver_remove_group(&ipath_driver.driver); + +bail_pci: + pci_unregister_driver(&ipath_driver); + +bail_unit: + idr_destroy(&unit_table); + +bail: + return ret; +} + +static void cleanup_device(struct ipath_devdata *dd) +{ + int port; + + ipath_shutdown_device(dd); + + if (*dd->ipath_statusp & IPATH_STATUS_CHIP_PRESENT) { + /* can't do anything more with chip; needs re-init */ + *dd->ipath_statusp &= ~IPATH_STATUS_CHIP_PRESENT; + if (dd->ipath_kregbase) { + /* + * if we haven't already cleaned up before these are + * to ensure any register reads/writes "fail" until + * re-init + */ + dd->ipath_kregbase = NULL; + dd->ipath_kregvirt = NULL; + dd->ipath_uregbase = 0; + dd->ipath_sregbase = 0; + dd->ipath_cregbase = 0; + dd->ipath_kregsize = 0; + } + ipath_disable_wc(dd); + } + + if (dd->ipath_pioavailregs_dma) { + dma_free_coherent(&dd->pcidev->dev, PAGE_SIZE, + (void *) dd->ipath_pioavailregs_dma, + dd->ipath_pioavailregs_phys); + dd->ipath_pioavailregs_dma = NULL; + } + + if (dd->ipath_pageshadow) { + struct page **tmpp = dd->ipath_pageshadow; + int i, cnt = 0; + + ipath_cdbg(VERBOSE, "Unlocking any expTID pages still " + "locked\n"); + for (port = 0; port < dd->ipath_cfgports; port++) { + int port_tidbase = port * dd->ipath_rcvtidcnt; + int maxtid = port_tidbase + dd->ipath_rcvtidcnt; + for (i = port_tidbase; i < maxtid; i++) { + if (!tmpp[i]) + continue; + ipath_release_user_pages(&tmpp[i], 1); + tmpp[i] = NULL; + cnt++; + } + } + if (cnt) { + ipath_stats.sps_pageunlocks += cnt; + ipath_cdbg(VERBOSE, "There were still %u expTID " + "entries locked\n", cnt); + } + if (ipath_stats.sps_pagelocks || + ipath_stats.sps_pageunlocks) + ipath_cdbg(VERBOSE, "%llu pages locked, %llu " + "unlocked via ipath_m{un}lock\n", + (unsigned long long) + ipath_stats.sps_pagelocks, + (unsigned long long) + ipath_stats.sps_pageunlocks); + + ipath_cdbg(VERBOSE, "Free shadow page tid array at %p\n", + dd->ipath_pageshadow); + vfree(dd->ipath_pageshadow); + dd->ipath_pageshadow = NULL; + } + + /* + * free any resources still in use (usually just kernel ports) + * at unload + */ + for (port = 0; port < dd->ipath_cfgports; port++) + ipath_free_pddata(dd, port, 1); + kfree(dd->ipath_pd); + /* + * debuggability, in case some cleanup path tries to use it + * after this + */ + dd->ipath_pd = NULL; +} + +static void __exit infinipath_cleanup(void) +{ + struct ipath_devdata *dd, *tmp; + unsigned long flags; + + ipath_exit_ipathfs(); + + ipath_driver_remove_group(&ipath_driver.driver); + + spin_lock_irqsave(&ipath_devs_lock, flags); + + /* + * turn off rcv, send, and interrupts for all ports, all drivers + * should also hard reset the chip here? + * free up port 0 (kernel) rcvhdr, egr bufs, and eventually tid bufs + * for all versions of the driver, if they were allocated + */ + list_for_each_entry_safe(dd, tmp, &ipath_dev_list, ipath_list) { + spin_unlock_irqrestore(&ipath_devs_lock, flags); + + if (dd->ipath_kregbase) + cleanup_device(dd); + + if (dd->pcidev) { + if (dd->pcidev->irq) { + ipath_cdbg(VERBOSE, + "unit %u free_irq of irq %x\n", + dd->ipath_unit, dd->pcidev->irq); + free_irq(dd->pcidev->irq, dd); + } else + ipath_dbg("irq is 0, not doing free_irq " + "for unit %u\n", dd->ipath_unit); + dd->pcidev = NULL; + } + + /* + * we check for NULL here, because it's outside the kregbase + * check, and we need to call it after the free_irq. Thus + * it's possible that the function pointers were never + * initialized. + */ + if (dd->ipath_f_cleanup) + /* clean up chip-specific stuff */ + dd->ipath_f_cleanup(dd); + + spin_lock_irqsave(&ipath_devs_lock, flags); + } + + spin_unlock_irqrestore(&ipath_devs_lock, flags); + + ipath_cdbg(VERBOSE, "Unregistering pci driver\n"); + pci_unregister_driver(&ipath_driver); + + idr_destroy(&unit_table); +} + +/** + * ipath_reset_device - reset the chip if possible + * @unit: the device to reset + * + * Whether or not reset is successful, we attempt to re-initialize the chip + * (that is, much like a driver unload/reload). We clear the INITTED flag + * so that the various entry points will fail until we reinitialize. For + * now, we only allow this if no user ports are open that use chip resources + */ +int ipath_reset_device(int unit) +{ + int ret, i; + struct ipath_devdata *dd = ipath_lookup(unit); + + if (!dd) { + ret = -ENODEV; + goto bail; + } + + dev_info(&dd->pcidev->dev, "Reset on unit %u requested\n", unit); + + if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_PRESENT)) { + dev_info(&dd->pcidev->dev, "Invalid unit number %u or " + "not initialized or not present\n", unit); + ret = -ENXIO; + goto bail; + } + + if (dd->ipath_pd) + for (i = 1; i < dd->ipath_portcnt; i++) { + if (dd->ipath_pd[i] && dd->ipath_pd[i]->port_cnt) { + ipath_dbg("unit %u port %d is in use " + "(PID %u cmd %s), can't reset\n", + unit, i, + dd->ipath_pd[i]->port_pid, + dd->ipath_pd[i]->port_comm); + ret = -EBUSY; + goto bail; + } + } + + dd->ipath_flags &= ~IPATH_INITTED; + ret = dd->ipath_f_reset(dd); + if (ret != 1) + ipath_dbg("reset was not successful\n"); + ipath_dbg("Trying to reinitialize unit %u after reset attempt\n", + unit); + ret = ipath_init_chip(dd, 1); + if (ret) + ipath_dev_err(dd, "Reinitialize unit %u after " + "reset failed with %d\n", unit, ret); + else + dev_info(&dd->pcidev->dev, "Reinitialized unit %u after " + "resetting\n", unit); + +bail: + return ret; +} + +module_init(infinipath_init); +module_exit(infinipath_cleanup); diff --git a/drivers/infiniband/hw/ipath/ipath_eeprom.c b/drivers/infiniband/hw/ipath/ipath_eeprom.c new file mode 100644 index 000000000000..f11a900e8cd7 --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_eeprom.c @@ -0,0 +1,613 @@ +/* + * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + +#include "ipath_kernel.h" + +/* + * InfiniPath I2C driver for a serial eeprom. This is not a generic + * I2C interface. For a start, the device we're using (Atmel AT24C11) + * doesn't work like a regular I2C device. It looks like one + * electrically, but not logically. Normal I2C devices have a single + * 7-bit or 10-bit I2C address that they respond to. Valid 7-bit + * addresses range from 0x03 to 0x77. Addresses 0x00 to 0x02 and 0x78 + * to 0x7F are special reserved addresses (e.g. 0x00 is the "general + * call" address.) The Atmel device, on the other hand, responds to ALL + * 7-bit addresses. It's designed to be the only device on a given I2C + * bus. A 7-bit address corresponds to the memory address within the + * Atmel device itself. + * + * Also, the timing requirements mean more than simple software + * bitbanging, with readbacks from chip to ensure timing (simple udelay + * is not enough). + * + * This all means that accessing the device is specialized enough + * that using the standard kernel I2C bitbanging interface would be + * impossible. For example, the core I2C eeprom driver expects to find + * a device at one or more of a limited set of addresses only. It doesn't + * allow writing to an eeprom. It also doesn't provide any means of + * accessing eeprom contents from within the kernel, only via sysfs. + */ + +enum i2c_type { + i2c_line_scl = 0, + i2c_line_sda +}; + +enum i2c_state { + i2c_line_low = 0, + i2c_line_high +}; + +#define READ_CMD 1 +#define WRITE_CMD 0 + +static int eeprom_init; + +/* + * The gpioval manipulation really should be protected by spinlocks + * or be converted to use atomic operations. + */ + +/** + * i2c_gpio_set - set a GPIO line + * @dd: the infinipath device + * @line: the line to set + * @new_line_state: the state to set + * + * Returns 0 if the line was set to the new state successfully, non-zero + * on error. + */ +static int i2c_gpio_set(struct ipath_devdata *dd, + enum i2c_type line, + enum i2c_state new_line_state) +{ + u64 read_val, write_val, mask, *gpioval; + + gpioval = &dd->ipath_gpio_out; + read_val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extctrl); + if (line == i2c_line_scl) + mask = ipath_gpio_scl; + else + mask = ipath_gpio_sda; + + if (new_line_state == i2c_line_high) + /* tri-state the output rather than force high */ + write_val = read_val & ~mask; + else + /* config line to be an output */ + write_val = read_val | mask; + ipath_write_kreg(dd, dd->ipath_kregs->kr_extctrl, write_val); + + /* set high and verify */ + if (new_line_state == i2c_line_high) + write_val = 0x1UL; + else + write_val = 0x0UL; + + if (line == i2c_line_scl) { + write_val <<= ipath_gpio_scl_num; + *gpioval = *gpioval & ~(1UL << ipath_gpio_scl_num); + *gpioval |= write_val; + } else { + write_val <<= ipath_gpio_sda_num; + *gpioval = *gpioval & ~(1UL << ipath_gpio_sda_num); + *gpioval |= write_val; + } + ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_out, *gpioval); + + return 0; +} + +/** + * i2c_gpio_get - get a GPIO line state + * @dd: the infinipath device + * @line: the line to get + * @curr_statep: where to put the line state + * + * Returns 0 if the line was set to the new state successfully, non-zero + * on error. curr_state is not set on error. + */ +static int i2c_gpio_get(struct ipath_devdata *dd, + enum i2c_type line, + enum i2c_state *curr_statep) +{ + u64 read_val, write_val, mask; + int ret; + + /* check args */ + if (curr_statep == NULL) { + ret = 1; + goto bail; + } + + read_val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extctrl); + /* config line to be an input */ + if (line == i2c_line_scl) + mask = ipath_gpio_scl; + else + mask = ipath_gpio_sda; + write_val = read_val & ~mask; + ipath_write_kreg(dd, dd->ipath_kregs->kr_extctrl, write_val); + read_val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extstatus); + + if (read_val & mask) + *curr_statep = i2c_line_high; + else + *curr_statep = i2c_line_low; + + ret = 0; + +bail: + return ret; +} + +/** + * i2c_wait_for_writes - wait for a write + * @dd: the infinipath device + * + * We use this instead of udelay directly, so we can make sure + * that previous register writes have been flushed all the way + * to the chip. Since we are delaying anyway, the cost doesn't + * hurt, and makes the bit twiddling more regular + */ +static void i2c_wait_for_writes(struct ipath_devdata *dd) +{ + (void)ipath_read_kreg32(dd, dd->ipath_kregs->kr_scratch); +} + +static void scl_out(struct ipath_devdata *dd, u8 bit) +{ + i2c_gpio_set(dd, i2c_line_scl, bit ? i2c_line_high : i2c_line_low); + + i2c_wait_for_writes(dd); +} + +static void sda_out(struct ipath_devdata *dd, u8 bit) +{ + i2c_gpio_set(dd, i2c_line_sda, bit ? i2c_line_high : i2c_line_low); + + i2c_wait_for_writes(dd); +} + +static u8 sda_in(struct ipath_devdata *dd, int wait) +{ + enum i2c_state bit; + + if (i2c_gpio_get(dd, i2c_line_sda, &bit)) + ipath_dbg("get bit failed!\n"); + + if (wait) + i2c_wait_for_writes(dd); + + return bit == i2c_line_high ? 1U : 0; +} + +/** + * i2c_ackrcv - see if ack following write is true + * @dd: the infinipath device + */ +static int i2c_ackrcv(struct ipath_devdata *dd) +{ + u8 ack_received; + + /* AT ENTRY SCL = LOW */ + /* change direction, ignore data */ + ack_received = sda_in(dd, 1); + scl_out(dd, i2c_line_high); + ack_received = sda_in(dd, 1) == 0; + scl_out(dd, i2c_line_low); + return ack_received; +} + +/** + * wr_byte - write a byte, one bit at a time + * @dd: the infinipath device + * @data: the byte to write + * + * Returns 0 if we got the following ack, otherwise 1 + */ +static int wr_byte(struct ipath_devdata *dd, u8 data) +{ + int bit_cntr; + u8 bit; + + for (bit_cntr = 7; bit_cntr >= 0; bit_cntr--) { + bit = (data >> bit_cntr) & 1; + sda_out(dd, bit); + scl_out(dd, i2c_line_high); + scl_out(dd, i2c_line_low); + } + return (!i2c_ackrcv(dd)) ? 1 : 0; +} + +static void send_ack(struct ipath_devdata *dd) +{ + sda_out(dd, i2c_line_low); + scl_out(dd, i2c_line_high); + scl_out(dd, i2c_line_low); + sda_out(dd, i2c_line_high); +} + +/** + * i2c_startcmd - transmit the start condition, followed by address/cmd + * @dd: the infinipath device + * @offset_dir: direction byte + * + * (both clock/data high, clock high, data low while clock is high) + */ +static int i2c_startcmd(struct ipath_devdata *dd, u8 offset_dir) +{ + int res; + + /* issue start sequence */ + sda_out(dd, i2c_line_high); + scl_out(dd, i2c_line_high); + sda_out(dd, i2c_line_low); + scl_out(dd, i2c_line_low); + + /* issue length and direction byte */ + res = wr_byte(dd, offset_dir); + + if (res) + ipath_cdbg(VERBOSE, "No ack to complete start\n"); + + return res; +} + +/** + * stop_cmd - transmit the stop condition + * @dd: the infinipath device + * + * (both clock/data low, clock high, data high while clock is high) + */ +static void stop_cmd(struct ipath_devdata *dd) +{ + scl_out(dd, i2c_line_low); + sda_out(dd, i2c_line_low); + scl_out(dd, i2c_line_high); + sda_out(dd, i2c_line_high); + udelay(2); +} + +/** + * eeprom_reset - reset I2C communication + * @dd: the infinipath device + */ + +static int eeprom_reset(struct ipath_devdata *dd) +{ + int clock_cycles_left = 9; + u64 *gpioval = &dd->ipath_gpio_out; + int ret; + + eeprom_init = 1; + *gpioval = ipath_read_kreg64(dd, dd->ipath_kregs->kr_gpio_out); + ipath_cdbg(VERBOSE, "Resetting i2c eeprom; initial gpioout reg " + "is %llx\n", (unsigned long long) *gpioval); + + /* + * This is to get the i2c into a known state, by first going low, + * then tristate sda (and then tristate scl as first thing + * in loop) + */ + scl_out(dd, i2c_line_low); + sda_out(dd, i2c_line_high); + + while (clock_cycles_left--) { + scl_out(dd, i2c_line_high); + + if (sda_in(dd, 0)) { + sda_out(dd, i2c_line_low); + scl_out(dd, i2c_line_low); + ret = 0; + goto bail; + } + + scl_out(dd, i2c_line_low); + } + + ret = 1; + +bail: + return ret; +} + +/** + * ipath_eeprom_read - receives bytes from the eeprom via I2C + * @dd: the infinipath device + * @eeprom_offset: address to read from + * @buffer: where to store result + * @len: number of bytes to receive + */ + +int ipath_eeprom_read(struct ipath_devdata *dd, u8 eeprom_offset, + void *buffer, int len) +{ + /* compiler complains unless initialized */ + u8 single_byte = 0; + int bit_cntr; + int ret; + + if (!eeprom_init) + eeprom_reset(dd); + + eeprom_offset = (eeprom_offset << 1) | READ_CMD; + + if (i2c_startcmd(dd, eeprom_offset)) { + ipath_dbg("Failed startcmd\n"); + stop_cmd(dd); + ret = 1; + goto bail; + } + + /* + * eeprom keeps clocking data out as long as we ack, automatically + * incrementing the address. + */ + while (len-- > 0) { + /* get data */ + single_byte = 0; + for (bit_cntr = 8; bit_cntr; bit_cntr--) { + u8 bit; + scl_out(dd, i2c_line_high); + bit = sda_in(dd, 0); + single_byte |= bit << (bit_cntr - 1); + scl_out(dd, i2c_line_low); + } + + /* send ack if not the last byte */ + if (len) + send_ack(dd); + + *((u8 *) buffer) = single_byte; + buffer++; + } + + stop_cmd(dd); + + ret = 0; + +bail: + return ret; +} + +/** + * ipath_eeprom_write - writes data to the eeprom via I2C + * @dd: the infinipath device + * @eeprom_offset: where to place data + * @buffer: data to write + * @len: number of bytes to write + */ +int ipath_eeprom_write(struct ipath_devdata *dd, u8 eeprom_offset, + const void *buffer, int len) +{ + u8 single_byte; + int sub_len; + const u8 *bp = buffer; + int max_wait_time, i; + int ret; + + if (!eeprom_init) + eeprom_reset(dd); + + while (len > 0) { + if (i2c_startcmd(dd, (eeprom_offset << 1) | WRITE_CMD)) { + ipath_dbg("Failed to start cmd offset %u\n", + eeprom_offset); + goto failed_write; + } + + sub_len = min(len, 4); + eeprom_offset += sub_len; + len -= sub_len; + + for (i = 0; i < sub_len; i++) { + if (wr_byte(dd, *bp++)) { + ipath_dbg("no ack after byte %u/%u (%u " + "total remain)\n", i, sub_len, + len + sub_len - i); + goto failed_write; + } + } + + stop_cmd(dd); + + /* + * wait for write complete by waiting for a successful + * read (the chip replies with a zero after the write + * cmd completes, and before it writes to the eeprom. + * The startcmd for the read will fail the ack until + * the writes have completed. We do this inline to avoid + * the debug prints that are in the real read routine + * if the startcmd fails. + */ + max_wait_time = 100; + while (i2c_startcmd(dd, READ_CMD)) { + stop_cmd(dd); + if (!--max_wait_time) { + ipath_dbg("Did not get successful read to " + "complete write\n"); + goto failed_write; + } + } + /* now read the zero byte */ + for (i = single_byte = 0; i < 8; i++) { + u8 bit; + scl_out(dd, i2c_line_high); + bit = sda_in(dd, 0); + scl_out(dd, i2c_line_low); + single_byte <<= 1; + single_byte |= bit; + } + stop_cmd(dd); + } + + ret = 0; + goto bail; + +failed_write: + stop_cmd(dd); + ret = 1; + +bail: + return ret; +} + +static u8 flash_csum(struct ipath_flash *ifp, int adjust) +{ + u8 *ip = (u8 *) ifp; + u8 csum = 0, len; + + for (len = 0; len < ifp->if_length; len++) + csum += *ip++; + csum -= ifp->if_csum; + csum = ~csum; + if (adjust) + ifp->if_csum = csum; + + return csum; +} + +/** + * ipath_get_guid - get the GUID from the i2c device + * @dd: the infinipath device + * + * When we add the multi-chip support, we will probably have to add + * the ability to use the number of guids field, and get the guid from + * the first chip's flash, to use for all of them. + */ +void ipath_get_guid(struct ipath_devdata *dd) +{ + void *buf; + struct ipath_flash *ifp; + __be64 guid; + int len; + u8 csum, *bguid; + int t = dd->ipath_unit; + struct ipath_devdata *dd0 = ipath_lookup(0); + + if (t && dd0->ipath_nguid > 1 && t <= dd0->ipath_nguid) { + u8 *bguid, oguid; + dd->ipath_guid = dd0->ipath_guid; + bguid = (u8 *) & dd->ipath_guid; + + oguid = bguid[7]; + bguid[7] += t; + if (oguid > bguid[7]) { + if (bguid[6] == 0xff) { + if (bguid[5] == 0xff) { + ipath_dev_err( + dd, + "Can't set %s GUID from " + "base, wraps to OUI!\n", + ipath_get_unit_name(t)); + dd->ipath_guid = 0; + goto bail; + } + bguid[5]++; + } + bguid[6]++; + } + dd->ipath_nguid = 1; + + ipath_dbg("nguid %u, so adding %u to device 0 guid, " + "for %llx\n", + dd0->ipath_nguid, t, + (unsigned long long) be64_to_cpu(dd->ipath_guid)); + goto bail; + } + + len = offsetof(struct ipath_flash, if_future); + buf = vmalloc(len); + if (!buf) { + ipath_dev_err(dd, "Couldn't allocate memory to read %u " + "bytes from eeprom for GUID\n", len); + goto bail; + } + + if (ipath_eeprom_read(dd, 0, buf, len)) { + ipath_dev_err(dd, "Failed reading GUID from eeprom\n"); + goto done; + } + ifp = (struct ipath_flash *)buf; + + csum = flash_csum(ifp, 0); + if (csum != ifp->if_csum) { + dev_info(&dd->pcidev->dev, "Bad I2C flash checksum: " + "0x%x, not 0x%x\n", csum, ifp->if_csum); + goto done; + } + if (*(__be64 *) ifp->if_guid == 0ULL || + *(__be64 *) ifp->if_guid == __constant_cpu_to_be64(-1LL)) { + ipath_dev_err(dd, "Invalid GUID %llx from flash; " + "ignoring\n", + *(unsigned long long *) ifp->if_guid); + /* don't allow GUID if all 0 or all 1's */ + goto done; + } + + /* complain, but allow it */ + if (*(u64 *) ifp->if_guid == 0x100007511000000ULL) + dev_info(&dd->pcidev->dev, "Warning, GUID %llx is " + "default, probably not correct!\n", + *(unsigned long long *) ifp->if_guid); + + bguid = ifp->if_guid; + if (!bguid[0] && !bguid[1] && !bguid[2]) { + /* original incorrect GUID format in flash; fix in + * core copy, by shifting up 2 octets; don't need to + * change top octet, since both it and shifted are + * 0.. */ + bguid[1] = bguid[3]; + bguid[2] = bguid[4]; + bguid[3] = bguid[4] = 0; + guid = *(__be64 *) ifp->if_guid; + ipath_cdbg(VERBOSE, "Old GUID format in flash, top 3 zero, " + "shifting 2 octets\n"); + } else + guid = *(__be64 *) ifp->if_guid; + dd->ipath_guid = guid; + dd->ipath_nguid = ifp->if_numguid; + memcpy(dd->ipath_serial, ifp->if_serial, + sizeof(ifp->if_serial)); + ipath_cdbg(VERBOSE, "Initted GUID to %llx from eeprom\n", + (unsigned long long) be64_to_cpu(dd->ipath_guid)); + +done: + vfree(buf); + +bail:; +} diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c new file mode 100644 index 000000000000..c347191f02bf --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c @@ -0,0 +1,1910 @@ +/* + * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include "ipath_kernel.h" +#include "ips_common.h" +#include "ipath_layer.h" + +static int ipath_open(struct inode *, struct file *); +static int ipath_close(struct inode *, struct file *); +static ssize_t ipath_write(struct file *, const char __user *, size_t, + loff_t *); +static unsigned int ipath_poll(struct file *, struct poll_table_struct *); +static int ipath_mmap(struct file *, struct vm_area_struct *); + +static struct file_operations ipath_file_ops = { + .owner = THIS_MODULE, + .write = ipath_write, + .open = ipath_open, + .release = ipath_close, + .poll = ipath_poll, + .mmap = ipath_mmap +}; + +static int ipath_get_base_info(struct ipath_portdata *pd, + void __user *ubase, size_t ubase_size) +{ + int ret = 0; + struct ipath_base_info *kinfo = NULL; + struct ipath_devdata *dd = pd->port_dd; + + if (ubase_size < sizeof(*kinfo)) { + ipath_cdbg(PROC, + "Base size %lu, need %lu (version mismatch?)\n", + (unsigned long) ubase_size, + (unsigned long) sizeof(*kinfo)); + ret = -EINVAL; + goto bail; + } + + kinfo = kzalloc(sizeof(*kinfo), GFP_KERNEL); + if (kinfo == NULL) { + ret = -ENOMEM; + goto bail; + } + + ret = dd->ipath_f_get_base_info(pd, kinfo); + if (ret < 0) + goto bail; + + kinfo->spi_rcvhdr_cnt = dd->ipath_rcvhdrcnt; + kinfo->spi_rcvhdrent_size = dd->ipath_rcvhdrentsize; + kinfo->spi_tidegrcnt = dd->ipath_rcvegrcnt; + kinfo->spi_rcv_egrbufsize = dd->ipath_rcvegrbufsize; + /* + * have to mmap whole thing + */ + kinfo->spi_rcv_egrbuftotlen = + pd->port_rcvegrbuf_chunks * pd->port_rcvegrbuf_size; + kinfo->spi_rcv_egrperchunk = pd->port_rcvegrbufs_perchunk; + kinfo->spi_rcv_egrchunksize = kinfo->spi_rcv_egrbuftotlen / + pd->port_rcvegrbuf_chunks; + kinfo->spi_tidcnt = dd->ipath_rcvtidcnt; + /* + * for this use, may be ipath_cfgports summed over all chips that + * are are configured and present + */ + kinfo->spi_nports = dd->ipath_cfgports; + /* unit (chip/board) our port is on */ + kinfo->spi_unit = dd->ipath_unit; + /* for now, only a single page */ + kinfo->spi_tid_maxsize = PAGE_SIZE; + + /* + * Doing this per port, and based on the skip value, etc. This has + * to be the actual buffer size, since the protocol code treats it + * as an array. + * + * These have to be set to user addresses in the user code via mmap. + * These values are used on return to user code for the mmap target + * addresses only. For 32 bit, same 44 bit address problem, so use + * the physical address, not virtual. Before 2.6.11, using the + * page_address() macro worked, but in 2.6.11, even that returns the + * full 64 bit address (upper bits all 1's). So far, using the + * physical addresses (or chip offsets, for chip mapping) works, but + * no doubt some future kernel release will chang that, and we'll be + * on to yet another method of dealing with this + */ + kinfo->spi_rcvhdr_base = (u64) pd->port_rcvhdrq_phys; + kinfo->spi_rcv_egrbufs = (u64) pd->port_rcvegr_phys; + kinfo->spi_pioavailaddr = (u64) dd->ipath_pioavailregs_phys; + kinfo->spi_status = (u64) kinfo->spi_pioavailaddr + + (void *) dd->ipath_statusp - + (void *) dd->ipath_pioavailregs_dma; + kinfo->spi_piobufbase = (u64) pd->port_piobufs; + kinfo->__spi_uregbase = + dd->ipath_uregbase + dd->ipath_palign * pd->port_port; + + kinfo->spi_pioindex = dd->ipath_pbufsport * (pd->port_port - 1); + kinfo->spi_piocnt = dd->ipath_pbufsport; + kinfo->spi_pioalign = dd->ipath_palign; + + kinfo->spi_qpair = IPATH_KD_QP; + kinfo->spi_piosize = dd->ipath_ibmaxlen; + kinfo->spi_mtu = dd->ipath_ibmaxlen; /* maxlen, not ibmtu */ + kinfo->spi_port = pd->port_port; + kinfo->spi_sw_version = IPATH_USER_SWVERSION; + kinfo->spi_hw_version = dd->ipath_revision; + + if (copy_to_user(ubase, kinfo, sizeof(*kinfo))) + ret = -EFAULT; + +bail: + kfree(kinfo); + return ret; +} + +/** + * ipath_tid_update - update a port TID + * @pd: the port + * @ti: the TID information + * + * The new implementation as of Oct 2004 is that the driver assigns + * the tid and returns it to the caller. To make it easier to + * catch bugs, and to reduce search time, we keep a cursor for + * each port, walking the shadow tid array to find one that's not + * in use. + * + * For now, if we can't allocate the full list, we fail, although + * in the long run, we'll allocate as many as we can, and the + * caller will deal with that by trying the remaining pages later. + * That means that when we fail, we have to mark the tids as not in + * use again, in our shadow copy. + * + * It's up to the caller to free the tids when they are done. + * We'll unlock the pages as they free them. + * + * Also, right now we are locking one page at a time, but since + * the intended use of this routine is for a single group of + * virtually contiguous pages, that should change to improve + * performance. + */ +static int ipath_tid_update(struct ipath_portdata *pd, + const struct ipath_tid_info *ti) +{ + int ret = 0, ntids; + u32 tid, porttid, cnt, i, tidcnt; + u16 *tidlist; + struct ipath_devdata *dd = pd->port_dd; + u64 physaddr; + unsigned long vaddr; + u64 __iomem *tidbase; + unsigned long tidmap[8]; + struct page **pagep = NULL; + + if (!dd->ipath_pageshadow) { + ret = -ENOMEM; + goto done; + } + + cnt = ti->tidcnt; + if (!cnt) { + ipath_dbg("After copyin, tidcnt 0, tidlist %llx\n", + (unsigned long long) ti->tidlist); + /* + * Should we treat as success? likely a bug + */ + ret = -EFAULT; + goto done; + } + tidcnt = dd->ipath_rcvtidcnt; + if (cnt >= tidcnt) { + /* make sure it all fits in port_tid_pg_list */ + dev_info(&dd->pcidev->dev, "Process tried to allocate %u " + "TIDs, only trying max (%u)\n", cnt, tidcnt); + cnt = tidcnt; + } + pagep = (struct page **)pd->port_tid_pg_list; + tidlist = (u16 *) (&pagep[cnt]); + + memset(tidmap, 0, sizeof(tidmap)); + tid = pd->port_tidcursor; + /* before decrement; chip actual # */ + porttid = pd->port_port * tidcnt; + ntids = tidcnt; + tidbase = (u64 __iomem *) (((char __iomem *) dd->ipath_kregbase) + + dd->ipath_rcvtidbase + + porttid * sizeof(*tidbase)); + + ipath_cdbg(VERBOSE, "Port%u %u tids, cursor %u, tidbase %p\n", + pd->port_port, cnt, tid, tidbase); + + /* virtual address of first page in transfer */ + vaddr = ti->tidvaddr; + if (!access_ok(VERIFY_WRITE, (void __user *) vaddr, + cnt * PAGE_SIZE)) { + ipath_dbg("Fail vaddr %p, %u pages, !access_ok\n", + (void *)vaddr, cnt); + ret = -EFAULT; + goto done; + } + ret = ipath_get_user_pages(vaddr, cnt, pagep); + if (ret) { + if (ret == -EBUSY) { + ipath_dbg("Failed to lock addr %p, %u pages " + "(already locked)\n", + (void *) vaddr, cnt); + /* + * for now, continue, and see what happens but with + * the new implementation, this should never happen, + * unless perhaps the user has mpin'ed the pages + * themselves (something we need to test) + */ + ret = 0; + } else { + dev_info(&dd->pcidev->dev, + "Failed to lock addr %p, %u pages: " + "errno %d\n", (void *) vaddr, cnt, -ret); + goto done; + } + } + for (i = 0; i < cnt; i++, vaddr += PAGE_SIZE) { + for (; ntids--; tid++) { + if (tid == tidcnt) + tid = 0; + if (!dd->ipath_pageshadow[porttid + tid]) + break; + } + if (ntids < 0) { + /* + * oops, wrapped all the way through their TIDs, + * and didn't have enough free; see comments at + * start of routine + */ + ipath_dbg("Not enough free TIDs for %u pages " + "(index %d), failing\n", cnt, i); + i--; /* last tidlist[i] not filled in */ + ret = -ENOMEM; + break; + } + tidlist[i] = tid; + ipath_cdbg(VERBOSE, "Updating idx %u to TID %u, " + "vaddr %lx\n", i, tid, vaddr); + /* we "know" system pages and TID pages are same size */ + dd->ipath_pageshadow[porttid + tid] = pagep[i]; + /* + * don't need atomic or it's overhead + */ + __set_bit(tid, tidmap); + physaddr = page_to_phys(pagep[i]); + ipath_stats.sps_pagelocks++; + ipath_cdbg(VERBOSE, + "TID %u, vaddr %lx, physaddr %llx pgp %p\n", + tid, vaddr, (unsigned long long) physaddr, + pagep[i]); + dd->ipath_f_put_tid(dd, &tidbase[tid], 1, physaddr); + /* + * don't check this tid in ipath_portshadow, since we + * just filled it in; start with the next one. + */ + tid++; + } + + if (ret) { + u32 limit; + cleanup: + /* jump here if copy out of updated info failed... */ + ipath_dbg("After failure (ret=%d), undo %d of %d entries\n", + -ret, i, cnt); + /* same code that's in ipath_free_tid() */ + limit = sizeof(tidmap) * BITS_PER_BYTE; + if (limit > tidcnt) + /* just in case size changes in future */ + limit = tidcnt; + tid = find_first_bit((const unsigned long *)tidmap, limit); + for (; tid < limit; tid++) { + if (!test_bit(tid, tidmap)) + continue; + if (dd->ipath_pageshadow[porttid + tid]) { + ipath_cdbg(VERBOSE, "Freeing TID %u\n", + tid); + dd->ipath_f_put_tid(dd, &tidbase[tid], 1, + dd->ipath_tidinvalid); + dd->ipath_pageshadow[porttid + tid] = NULL; + ipath_stats.sps_pageunlocks++; + } + } + ipath_release_user_pages(pagep, cnt); + } else { + /* + * Copy the updated array, with ipath_tid's filled in, back + * to user. Since we did the copy in already, this "should + * never fail" If it does, we have to clean up... + */ + if (copy_to_user((void __user *) + (unsigned long) ti->tidlist, + tidlist, cnt * sizeof(*tidlist))) { + ret = -EFAULT; + goto cleanup; + } + if (copy_to_user((void __user *) (unsigned long) ti->tidmap, + tidmap, sizeof tidmap)) { + ret = -EFAULT; + goto cleanup; + } + if (tid == tidcnt) + tid = 0; + pd->port_tidcursor = tid; + } + +done: + if (ret) + ipath_dbg("Failed to map %u TID pages, failing with %d\n", + ti->tidcnt, -ret); + return ret; +} + +/** + * ipath_tid_free - free a port TID + * @pd: the port + * @ti: the TID info + * + * right now we are unlocking one page at a time, but since + * the intended use of this routine is for a single group of + * virtually contiguous pages, that should change to improve + * performance. We check that the TID is in range for this port + * but otherwise don't check validity; if user has an error and + * frees the wrong tid, it's only their own data that can thereby + * be corrupted. We do check that the TID was in use, for sanity + * We always use our idea of the saved address, not the address that + * they pass in to us. + */ + +static int ipath_tid_free(struct ipath_portdata *pd, + const struct ipath_tid_info *ti) +{ + int ret = 0; + u32 tid, porttid, cnt, limit, tidcnt; + struct ipath_devdata *dd = pd->port_dd; + u64 __iomem *tidbase; + unsigned long tidmap[8]; + + if (!dd->ipath_pageshadow) { + ret = -ENOMEM; + goto done; + } + + if (copy_from_user(tidmap, (void __user *)(unsigned long)ti->tidmap, + sizeof tidmap)) { + ret = -EFAULT; + goto done; + } + + porttid = pd->port_port * dd->ipath_rcvtidcnt; + tidbase = (u64 __iomem *) ((char __iomem *)(dd->ipath_kregbase) + + dd->ipath_rcvtidbase + + porttid * sizeof(*tidbase)); + + tidcnt = dd->ipath_rcvtidcnt; + limit = sizeof(tidmap) * BITS_PER_BYTE; + if (limit > tidcnt) + /* just in case size changes in future */ + limit = tidcnt; + tid = find_first_bit(tidmap, limit); + ipath_cdbg(VERBOSE, "Port%u free %u tids; first bit (max=%d) " + "set is %d, porttid %u\n", pd->port_port, ti->tidcnt, + limit, tid, porttid); + for (cnt = 0; tid < limit; tid++) { + /* + * small optimization; if we detect a run of 3 or so without + * any set, use find_first_bit again. That's mainly to + * accelerate the case where we wrapped, so we have some at + * the beginning, and some at the end, and a big gap + * in the middle. + */ + if (!test_bit(tid, tidmap)) + continue; + cnt++; + if (dd->ipath_pageshadow[porttid + tid]) { + ipath_cdbg(VERBOSE, "PID %u freeing TID %u\n", + pd->port_pid, tid); + dd->ipath_f_put_tid(dd, &tidbase[tid], 1, + dd->ipath_tidinvalid); + ipath_release_user_pages( + &dd->ipath_pageshadow[porttid + tid], 1); + dd->ipath_pageshadow[porttid + tid] = NULL; + ipath_stats.sps_pageunlocks++; + } else + ipath_dbg("Unused tid %u, ignoring\n", tid); + } + if (cnt != ti->tidcnt) + ipath_dbg("passed in tidcnt %d, only %d bits set in map\n", + ti->tidcnt, cnt); +done: + if (ret) + ipath_dbg("Failed to unmap %u TID pages, failing with %d\n", + ti->tidcnt, -ret); + return ret; +} + +/** + * ipath_set_part_key - set a partition key + * @pd: the port + * @key: the key + * + * We can have up to 4 active at a time (other than the default, which is + * always allowed). This is somewhat tricky, since multiple ports may set + * the same key, so we reference count them, and clean up at exit. All 4 + * partition keys are packed into a single infinipath register. It's an + * error for a process to set the same pkey multiple times. We provide no + * mechanism to de-allocate a pkey at this time, we may eventually need to + * do that. I've used the atomic operations, and no locking, and only make + * a single pass through what's available. This should be more than + * adequate for some time. I'll think about spinlocks or the like if and as + * it's necessary. + */ +static int ipath_set_part_key(struct ipath_portdata *pd, u16 key) +{ + struct ipath_devdata *dd = pd->port_dd; + int i, any = 0, pidx = -1; + u16 lkey = key & 0x7FFF; + int ret; + + if (lkey == (IPS_DEFAULT_P_KEY & 0x7FFF)) { + /* nothing to do; this key always valid */ + ret = 0; + goto bail; + } + + ipath_cdbg(VERBOSE, "p%u try to set pkey %hx, current keys " + "%hx:%x %hx:%x %hx:%x %hx:%x\n", + pd->port_port, key, dd->ipath_pkeys[0], + atomic_read(&dd->ipath_pkeyrefs[0]), dd->ipath_pkeys[1], + atomic_read(&dd->ipath_pkeyrefs[1]), dd->ipath_pkeys[2], + atomic_read(&dd->ipath_pkeyrefs[2]), dd->ipath_pkeys[3], + atomic_read(&dd->ipath_pkeyrefs[3])); + + if (!lkey) { + ipath_cdbg(PROC, "p%u tries to set key 0, not allowed\n", + pd->port_port); + ret = -EINVAL; + goto bail; + } + + /* + * Set the full membership bit, because it has to be + * set in the register or the packet, and it seems + * cleaner to set in the register than to force all + * callers to set it. (see bug 4331) + */ + key |= 0x8000; + + for (i = 0; i < ARRAY_SIZE(pd->port_pkeys); i++) { + if (!pd->port_pkeys[i] && pidx == -1) + pidx = i; + if (pd->port_pkeys[i] == key) { + ipath_cdbg(VERBOSE, "p%u tries to set same pkey " + "(%x) more than once\n", + pd->port_port, key); + ret = -EEXIST; + goto bail; + } + } + if (pidx == -1) { + ipath_dbg("All pkeys for port %u already in use, " + "can't set %x\n", pd->port_port, key); + ret = -EBUSY; + goto bail; + } + for (any = i = 0; i < ARRAY_SIZE(dd->ipath_pkeys); i++) { + if (!dd->ipath_pkeys[i]) { + any++; + continue; + } + if (dd->ipath_pkeys[i] == key) { + atomic_t *pkrefs = &dd->ipath_pkeyrefs[i]; + + if (atomic_inc_return(pkrefs) > 1) { + pd->port_pkeys[pidx] = key; + ipath_cdbg(VERBOSE, "p%u set key %x " + "matches #%d, count now %d\n", + pd->port_port, key, i, + atomic_read(pkrefs)); + ret = 0; + goto bail; + } else { + /* + * lost race, decrement count, catch below + */ + atomic_dec(pkrefs); + ipath_cdbg(VERBOSE, "Lost race, count was " + "0, after dec, it's %d\n", + atomic_read(pkrefs)); + any++; + } + } + if ((dd->ipath_pkeys[i] & 0x7FFF) == lkey) { + /* + * It makes no sense to have both the limited and + * full membership PKEY set at the same time since + * the unlimited one will disable the limited one. + */ + ret = -EEXIST; + goto bail; + } + } + if (!any) { + ipath_dbg("port %u, all pkeys already in use, " + "can't set %x\n", pd->port_port, key); + ret = -EBUSY; + goto bail; + } + for (any = i = 0; i < ARRAY_SIZE(dd->ipath_pkeys); i++) { + if (!dd->ipath_pkeys[i] && + atomic_inc_return(&dd->ipath_pkeyrefs[i]) == 1) { + u64 pkey; + + /* for ipathstats, etc. */ + ipath_stats.sps_pkeys[i] = lkey; + pd->port_pkeys[pidx] = dd->ipath_pkeys[i] = key; + pkey = + (u64) dd->ipath_pkeys[0] | + ((u64) dd->ipath_pkeys[1] << 16) | + ((u64) dd->ipath_pkeys[2] << 32) | + ((u64) dd->ipath_pkeys[3] << 48); + ipath_cdbg(PROC, "p%u set key %x in #%d, " + "portidx %d, new pkey reg %llx\n", + pd->port_port, key, i, pidx, + (unsigned long long) pkey); + ipath_write_kreg( + dd, dd->ipath_kregs->kr_partitionkey, pkey); + + ret = 0; + goto bail; + } + } + ipath_dbg("port %u, all pkeys already in use 2nd pass, " + "can't set %x\n", pd->port_port, key); + ret = -EBUSY; + +bail: + return ret; +} + +/** + * ipath_manage_rcvq - manage a port's receive queue + * @pd: the port + * @start_stop: action to carry out + * + * start_stop == 0 disables receive on the port, for use in queue + * overflow conditions. start_stop==1 re-enables, to be used to + * re-init the software copy of the head register + */ +static int ipath_manage_rcvq(struct ipath_portdata *pd, int start_stop) +{ + struct ipath_devdata *dd = pd->port_dd; + u64 tval; + + ipath_cdbg(PROC, "%sabling rcv for unit %u port %u\n", + start_stop ? "en" : "dis", dd->ipath_unit, + pd->port_port); + /* atomically clear receive enable port. */ + if (start_stop) { + /* + * On enable, force in-memory copy of the tail register to + * 0, so that protocol code doesn't have to worry about + * whether or not the chip has yet updated the in-memory + * copy or not on return from the system call. The chip + * always resets it's tail register back to 0 on a + * transition from disabled to enabled. This could cause a + * problem if software was broken, and did the enable w/o + * the disable, but eventually the in-memory copy will be + * updated and correct itself, even in the face of software + * bugs. + */ + *pd->port_rcvhdrtail_kvaddr = 0; + set_bit(INFINIPATH_R_PORTENABLE_SHIFT + pd->port_port, + &dd->ipath_rcvctrl); + } else + clear_bit(INFINIPATH_R_PORTENABLE_SHIFT + pd->port_port, + &dd->ipath_rcvctrl); + ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, + dd->ipath_rcvctrl); + /* now be sure chip saw it before we return */ + tval = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch); + if (start_stop) { + /* + * And try to be sure that tail reg update has happened too. + * This should in theory interlock with the RXE changes to + * the tail register. Don't assign it to the tail register + * in memory copy, since we could overwrite an update by the + * chip if we did. + */ + tval = ipath_read_ureg32(dd, ur_rcvhdrtail, pd->port_port); + } + /* always; new head should be equal to new tail; see above */ + return 0; +} + +static void ipath_clean_part_key(struct ipath_portdata *pd, + struct ipath_devdata *dd) +{ + int i, j, pchanged = 0; + u64 oldpkey; + + /* for debugging only */ + oldpkey = (u64) dd->ipath_pkeys[0] | + ((u64) dd->ipath_pkeys[1] << 16) | + ((u64) dd->ipath_pkeys[2] << 32) | + ((u64) dd->ipath_pkeys[3] << 48); + + for (i = 0; i < ARRAY_SIZE(pd->port_pkeys); i++) { + if (!pd->port_pkeys[i]) + continue; + ipath_cdbg(VERBOSE, "look for key[%d] %hx in pkeys\n", i, + pd->port_pkeys[i]); + for (j = 0; j < ARRAY_SIZE(dd->ipath_pkeys); j++) { + /* check for match independent of the global bit */ + if ((dd->ipath_pkeys[j] & 0x7fff) != + (pd->port_pkeys[i] & 0x7fff)) + continue; + if (atomic_dec_and_test(&dd->ipath_pkeyrefs[j])) { + ipath_cdbg(VERBOSE, "p%u clear key " + "%x matches #%d\n", + pd->port_port, + pd->port_pkeys[i], j); + ipath_stats.sps_pkeys[j] = + dd->ipath_pkeys[j] = 0; + pchanged++; + } + else ipath_cdbg( + VERBOSE, "p%u key %x matches #%d, " + "but ref still %d\n", pd->port_port, + pd->port_pkeys[i], j, + atomic_read(&dd->ipath_pkeyrefs[j])); + break; + } + pd->port_pkeys[i] = 0; + } + if (pchanged) { + u64 pkey = (u64) dd->ipath_pkeys[0] | + ((u64) dd->ipath_pkeys[1] << 16) | + ((u64) dd->ipath_pkeys[2] << 32) | + ((u64) dd->ipath_pkeys[3] << 48); + ipath_cdbg(VERBOSE, "p%u old pkey reg %llx, " + "new pkey reg %llx\n", pd->port_port, + (unsigned long long) oldpkey, + (unsigned long long) pkey); + ipath_write_kreg(dd, dd->ipath_kregs->kr_partitionkey, + pkey); + } +} + +/** + * ipath_create_user_egr - allocate eager TID buffers + * @pd: the port to allocate TID buffers for + * + * This routine is now quite different for user and kernel, because + * the kernel uses skb's, for the accelerated network performance + * This is the user port version + * + * Allocate the eager TID buffers and program them into infinipath + * They are no longer completely contiguous, we do multiple allocation + * calls. + */ +static int ipath_create_user_egr(struct ipath_portdata *pd) +{ + struct ipath_devdata *dd = pd->port_dd; + unsigned e, egrcnt, alloced, egrperchunk, chunk, egrsize, egroff; + size_t size; + int ret; + + egrcnt = dd->ipath_rcvegrcnt; + /* TID number offset for this port */ + egroff = pd->port_port * egrcnt; + egrsize = dd->ipath_rcvegrbufsize; + ipath_cdbg(VERBOSE, "Allocating %d egr buffers, at egrtid " + "offset %x, egrsize %u\n", egrcnt, egroff, egrsize); + + /* + * to avoid wasting a lot of memory, we allocate 32KB chunks of + * physically contiguous memory, advance through it until used up + * and then allocate more. Of course, we need memory to store those + * extra pointers, now. Started out with 256KB, but under heavy + * memory pressure (creating large files and then copying them over + * NFS while doing lots of MPI jobs), we hit some allocation + * failures, even though we can sleep... (2.6.10) Still get + * failures at 64K. 32K is the lowest we can go without waiting + * more memory again. It seems likely that the coalescing in + * free_pages, etc. still has issues (as it has had previously + * during 2.6.x development). + */ + size = 0x8000; + alloced = ALIGN(egrsize * egrcnt, size); + egrperchunk = size / egrsize; + chunk = (egrcnt + egrperchunk - 1) / egrperchunk; + pd->port_rcvegrbuf_chunks = chunk; + pd->port_rcvegrbufs_perchunk = egrperchunk; + pd->port_rcvegrbuf_size = size; + pd->port_rcvegrbuf = vmalloc(chunk * sizeof(pd->port_rcvegrbuf[0])); + if (!pd->port_rcvegrbuf) { + ret = -ENOMEM; + goto bail; + } + pd->port_rcvegrbuf_phys = + vmalloc(chunk * sizeof(pd->port_rcvegrbuf_phys[0])); + if (!pd->port_rcvegrbuf_phys) { + ret = -ENOMEM; + goto bail_rcvegrbuf; + } + for (e = 0; e < pd->port_rcvegrbuf_chunks; e++) { + /* + * GFP_USER, but without GFP_FS, so buffer cache can be + * coalesced (we hope); otherwise, even at order 4, + * heavy filesystem activity makes these fail + */ + gfp_t gfp_flags = __GFP_WAIT | __GFP_IO | __GFP_COMP; + + pd->port_rcvegrbuf[e] = dma_alloc_coherent( + &dd->pcidev->dev, size, &pd->port_rcvegrbuf_phys[e], + gfp_flags); + + if (!pd->port_rcvegrbuf[e]) { + ret = -ENOMEM; + goto bail_rcvegrbuf_phys; + } + } + + pd->port_rcvegr_phys = pd->port_rcvegrbuf_phys[0]; + + for (e = chunk = 0; chunk < pd->port_rcvegrbuf_chunks; chunk++) { + dma_addr_t pa = pd->port_rcvegrbuf_phys[chunk]; + unsigned i; + + for (i = 0; e < egrcnt && i < egrperchunk; e++, i++) { + dd->ipath_f_put_tid(dd, e + egroff + + (u64 __iomem *) + ((char __iomem *) + dd->ipath_kregbase + + dd->ipath_rcvegrbase), 0, pa); + pa += egrsize; + } + cond_resched(); /* don't hog the cpu */ + } + + ret = 0; + goto bail; + +bail_rcvegrbuf_phys: + for (e = 0; e < pd->port_rcvegrbuf_chunks && + pd->port_rcvegrbuf[e]; e++) + dma_free_coherent(&dd->pcidev->dev, size, + pd->port_rcvegrbuf[e], + pd->port_rcvegrbuf_phys[e]); + + vfree(pd->port_rcvegrbuf_phys); + pd->port_rcvegrbuf_phys = NULL; +bail_rcvegrbuf: + vfree(pd->port_rcvegrbuf); + pd->port_rcvegrbuf = NULL; +bail: + return ret; +} + +static int ipath_do_user_init(struct ipath_portdata *pd, + const struct ipath_user_info *uinfo) +{ + int ret = 0; + struct ipath_devdata *dd = pd->port_dd; + u64 physaddr, uaddr, off, atmp; + struct page *pagep; + u32 head32; + u64 head; + + /* for now, if major version is different, bail */ + if ((uinfo->spu_userversion >> 16) != IPATH_USER_SWMAJOR) { + dev_info(&dd->pcidev->dev, + "User major version %d not same as driver " + "major %d\n", uinfo->spu_userversion >> 16, + IPATH_USER_SWMAJOR); + ret = -ENODEV; + goto done; + } + + if ((uinfo->spu_userversion & 0xffff) != IPATH_USER_SWMINOR) + ipath_dbg("User minor version %d not same as driver " + "minor %d\n", uinfo->spu_userversion & 0xffff, + IPATH_USER_SWMINOR); + + if (uinfo->spu_rcvhdrsize) { + ret = ipath_setrcvhdrsize(dd, uinfo->spu_rcvhdrsize); + if (ret) + goto done; + } + + /* for now we do nothing with rcvhdrcnt: uinfo->spu_rcvhdrcnt */ + + /* set up for the rcvhdr Q tail register writeback to user memory */ + if (!uinfo->spu_rcvhdraddr || + !access_ok(VERIFY_WRITE, (u64 __user *) (unsigned long) + uinfo->spu_rcvhdraddr, sizeof(u64))) { + ipath_dbg("Port %d rcvhdrtail addr %llx not valid\n", + pd->port_port, + (unsigned long long) uinfo->spu_rcvhdraddr); + ret = -EINVAL; + goto done; + } + + off = offset_in_page(uinfo->spu_rcvhdraddr); + uaddr = PAGE_MASK & (unsigned long) uinfo->spu_rcvhdraddr; + ret = ipath_get_user_pages_nocopy(uaddr, &pagep); + if (ret) { + dev_info(&dd->pcidev->dev, "Failed to lookup and lock " + "address %llx for rcvhdrtail: errno %d\n", + (unsigned long long) uinfo->spu_rcvhdraddr, -ret); + goto done; + } + ipath_stats.sps_pagelocks++; + pd->port_rcvhdrtail_uaddr = uaddr; + pd->port_rcvhdrtail_pagep = pagep; + pd->port_rcvhdrtail_kvaddr = + page_address(pagep); + pd->port_rcvhdrtail_kvaddr += off; + physaddr = page_to_phys(pagep) + off; + ipath_cdbg(VERBOSE, "port %d user addr %llx hdrtailaddr, %llx " + "physical (off=%llx)\n", + pd->port_port, + (unsigned long long) uinfo->spu_rcvhdraddr, + (unsigned long long) physaddr, (unsigned long long) off); + ipath_write_kreg_port(dd, dd->ipath_kregs->kr_rcvhdrtailaddr, + pd->port_port, physaddr); + atmp = ipath_read_kreg64_port(dd, + dd->ipath_kregs->kr_rcvhdrtailaddr, + pd->port_port); + if (physaddr != atmp) { + ipath_dev_err(dd, + "Catastrophic software error, " + "RcvHdrTailAddr%u written as %llx, " + "read back as %llx\n", pd->port_port, + (unsigned long long) physaddr, + (unsigned long long) atmp); + ret = -EINVAL; + goto done; + } + + /* for right now, kernel piobufs are at end, so port 1 is at 0 */ + pd->port_piobufs = dd->ipath_piobufbase + + dd->ipath_pbufsport * (pd->port_port - + 1) * dd->ipath_palign; + ipath_cdbg(VERBOSE, "Set base of piobufs for port %u to 0x%x\n", + pd->port_port, pd->port_piobufs); + + /* + * Now allocate the rcvhdr Q and eager TIDs; skip the TID + * array for time being. If pd->port_port > chip-supported, + * we need to do extra stuff here to handle by handling overflow + * through port 0, someday + */ + ret = ipath_create_rcvhdrq(dd, pd); + if (!ret) + ret = ipath_create_user_egr(pd); + if (ret) + goto done; + /* enable receives now */ + /* atomically set enable bit for this port */ + set_bit(INFINIPATH_R_PORTENABLE_SHIFT + pd->port_port, + &dd->ipath_rcvctrl); + + /* + * set the head registers for this port to the current values + * of the tail pointers, since we don't know if they were + * updated on last use of the port. + */ + head32 = ipath_read_ureg32(dd, ur_rcvhdrtail, pd->port_port); + head = (u64) head32; + ipath_write_ureg(dd, ur_rcvhdrhead, head, pd->port_port); + head32 = ipath_read_ureg32(dd, ur_rcvegrindextail, pd->port_port); + ipath_write_ureg(dd, ur_rcvegrindexhead, head32, pd->port_port); + dd->ipath_lastegrheads[pd->port_port] = -1; + dd->ipath_lastrcvhdrqtails[pd->port_port] = -1; + ipath_cdbg(VERBOSE, "Wrote port%d head %llx, egrhead %x from " + "tail regs\n", pd->port_port, + (unsigned long long) head, head32); + pd->port_tidcursor = 0; /* start at beginning after open */ + /* + * now enable the port; the tail registers will be written to memory + * by the chip as soon as it sees the write to + * dd->ipath_kregs->kr_rcvctrl. The update only happens on + * transition from 0 to 1, so clear it first, then set it as part of + * enabling the port. This will (very briefly) affect any other + * open ports, but it shouldn't be long enough to be an issue. + */ + ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, + dd->ipath_rcvctrl & ~INFINIPATH_R_TAILUPD); + ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, + dd->ipath_rcvctrl); + +done: + return ret; +} + +static int mmap_ureg(struct vm_area_struct *vma, struct ipath_devdata *dd, + u64 ureg) +{ + unsigned long phys; + int ret; + + /* it's the real hardware, so io_remap works */ + + if ((vma->vm_end - vma->vm_start) > PAGE_SIZE) { + dev_info(&dd->pcidev->dev, "FAIL mmap userreg: reqlen " + "%lx > PAGE\n", vma->vm_end - vma->vm_start); + ret = -EFAULT; + } else { + phys = dd->ipath_physaddr + ureg; + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND; + ret = io_remap_pfn_range(vma, vma->vm_start, + phys >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); + } + return ret; +} + +static int mmap_piobufs(struct vm_area_struct *vma, + struct ipath_devdata *dd, + struct ipath_portdata *pd) +{ + unsigned long phys; + int ret; + + /* + * When we map the PIO buffers, we want to map them as writeonly, no + * read possible. + */ + + if ((vma->vm_end - vma->vm_start) > + (dd->ipath_pbufsport * dd->ipath_palign)) { + dev_info(&dd->pcidev->dev, "FAIL mmap piobufs: " + "reqlen %lx > PAGE\n", + vma->vm_end - vma->vm_start); + ret = -EFAULT; + goto bail; + } + + phys = dd->ipath_physaddr + pd->port_piobufs; + /* + * Do *NOT* mark this as non-cached (PWT bit), or we don't get the + * write combining behavior we want on the PIO buffers! + * vma->vm_page_prot = + * pgprot_noncached(vma->vm_page_prot); + */ + + if (vma->vm_flags & VM_READ) { + dev_info(&dd->pcidev->dev, + "Can't map piobufs as readable (flags=%lx)\n", + vma->vm_flags); + ret = -EPERM; + goto bail; + } + + /* don't allow them to later change to readable with mprotect */ + + vma->vm_flags &= ~VM_MAYWRITE; + vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND; + + ret = io_remap_pfn_range(vma, vma->vm_start, phys >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); +bail: + return ret; +} + +static int mmap_rcvegrbufs(struct vm_area_struct *vma, + struct ipath_portdata *pd) +{ + struct ipath_devdata *dd = pd->port_dd; + unsigned long start, size; + size_t total_size, i; + dma_addr_t *phys; + int ret; + + if (!pd->port_rcvegrbuf) { + ret = -EFAULT; + goto bail; + } + + size = pd->port_rcvegrbuf_size; + total_size = pd->port_rcvegrbuf_chunks * size; + if ((vma->vm_end - vma->vm_start) > total_size) { + dev_info(&dd->pcidev->dev, "FAIL on egr bufs: " + "reqlen %lx > actual %lx\n", + vma->vm_end - vma->vm_start, + (unsigned long) total_size); + ret = -EFAULT; + goto bail; + } + + if (vma->vm_flags & VM_WRITE) { + dev_info(&dd->pcidev->dev, "Can't map eager buffers as " + "writable (flags=%lx)\n", vma->vm_flags); + ret = -EPERM; + goto bail; + } + + start = vma->vm_start; + phys = pd->port_rcvegrbuf_phys; + + /* don't allow them to later change to writeable with mprotect */ + vma->vm_flags &= ~VM_MAYWRITE; + + for (i = 0; i < pd->port_rcvegrbuf_chunks; i++, start += size) { + ret = remap_pfn_range(vma, start, phys[i] >> PAGE_SHIFT, + size, vma->vm_page_prot); + if (ret < 0) + goto bail; + } + ret = 0; + +bail: + return ret; +} + +static int mmap_rcvhdrq(struct vm_area_struct *vma, + struct ipath_portdata *pd) +{ + struct ipath_devdata *dd = pd->port_dd; + size_t total_size; + int ret; + + /* + * kmalloc'ed memory, physically contiguous; this is from + * spi_rcvhdr_base; we allow user to map read-write so they can + * write hdrq entries to allow protocol code to directly poll + * whether a hdrq entry has been written. + */ + total_size = ALIGN(dd->ipath_rcvhdrcnt * dd->ipath_rcvhdrentsize * + sizeof(u32), PAGE_SIZE); + if ((vma->vm_end - vma->vm_start) > total_size) { + dev_info(&dd->pcidev->dev, + "FAIL on rcvhdrq: reqlen %lx > actual %lx\n", + vma->vm_end - vma->vm_start, + (unsigned long) total_size); + ret = -EFAULT; + goto bail; + } + + ret = remap_pfn_range(vma, vma->vm_start, + pd->port_rcvhdrq_phys >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); +bail: + return ret; +} + +static int mmap_pioavailregs(struct vm_area_struct *vma, + struct ipath_portdata *pd) +{ + struct ipath_devdata *dd = pd->port_dd; + int ret; + + /* + * when we map the PIO bufferavail registers, we want to map them as + * readonly, no write possible. + * + * kmalloc'ed memory, physically contiguous, one page only, readonly + */ + + if ((vma->vm_end - vma->vm_start) > PAGE_SIZE) { + dev_info(&dd->pcidev->dev, "FAIL on pioavailregs_dma: " + "reqlen %lx > actual %lx\n", + vma->vm_end - vma->vm_start, + (unsigned long) PAGE_SIZE); + ret = -EFAULT; + goto bail; + } + + if (vma->vm_flags & VM_WRITE) { + dev_info(&dd->pcidev->dev, + "Can't map pioavailregs as writable (flags=%lx)\n", + vma->vm_flags); + ret = -EPERM; + goto bail; + } + + /* don't allow them to later change with mprotect */ + vma->vm_flags &= ~VM_MAYWRITE; + + ret = remap_pfn_range(vma, vma->vm_start, + dd->ipath_pioavailregs_phys >> PAGE_SHIFT, + PAGE_SIZE, vma->vm_page_prot); +bail: + return ret; +} + +/** + * ipath_mmap - mmap various structures into user space + * @fp: the file pointer + * @vma: the VM area + * + * We use this to have a shared buffer between the kernel and the user code + * for the rcvhdr queue, egr buffers, and the per-port user regs and pio + * buffers in the chip. We have the open and close entries so we can bump + * the ref count and keep the driver from being unloaded while still mapped. + */ +static int ipath_mmap(struct file *fp, struct vm_area_struct *vma) +{ + struct ipath_portdata *pd; + struct ipath_devdata *dd; + u64 pgaddr, ureg; + int ret; + + pd = port_fp(fp); + dd = pd->port_dd; + /* + * This is the ipath_do_user_init() code, mapping the shared buffers + * into the user process. The address referred to by vm_pgoff is the + * virtual, not physical, address; we only do one mmap for each + * space mapped. + */ + pgaddr = vma->vm_pgoff << PAGE_SHIFT; + + /* + * note that ureg does *NOT* have the kregvirt as part of it, to be + * sure that for 32 bit programs, we don't end up trying to map a > + * 44 address. Has to match ipath_get_base_info() code that sets + * __spi_uregbase + */ + + ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port; + + ipath_cdbg(MM, "ushare: pgaddr %llx vm_start=%lx, vmlen %lx\n", + (unsigned long long) pgaddr, vma->vm_start, + vma->vm_end - vma->vm_start); + + if (pgaddr == ureg) + ret = mmap_ureg(vma, dd, ureg); + else if (pgaddr == pd->port_piobufs) + ret = mmap_piobufs(vma, dd, pd); + else if (pgaddr == (u64) pd->port_rcvegr_phys) + ret = mmap_rcvegrbufs(vma, pd); + else if (pgaddr == (u64) pd->port_rcvhdrq_phys) + ret = mmap_rcvhdrq(vma, pd); + else if (pgaddr == dd->ipath_pioavailregs_phys) + ret = mmap_pioavailregs(vma, pd); + else + ret = -EINVAL; + + vma->vm_private_data = NULL; + + if (ret < 0) + dev_info(&dd->pcidev->dev, + "Failure %d on addr %lx, off %lx\n", + -ret, vma->vm_start, vma->vm_pgoff); + + return ret; +} + +static unsigned int ipath_poll(struct file *fp, + struct poll_table_struct *pt) +{ + struct ipath_portdata *pd; + u32 head, tail; + int bit; + struct ipath_devdata *dd; + + pd = port_fp(fp); + dd = pd->port_dd; + + bit = pd->port_port + INFINIPATH_R_INTRAVAIL_SHIFT; + set_bit(bit, &dd->ipath_rcvctrl); + + /* + * Before blocking, make sure that head is still == tail, + * reading from the chip, so we can be sure the interrupt + * enable has made it to the chip. If not equal, disable + * interrupt again and return immediately. This avoids races, + * and the overhead of the chip read doesn't matter much at + * this point, since we are waiting for something anyway. + */ + + ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, + dd->ipath_rcvctrl); + + head = ipath_read_ureg32(dd, ur_rcvhdrhead, pd->port_port); + tail = ipath_read_ureg32(dd, ur_rcvhdrtail, pd->port_port); + + if (tail == head) { + set_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag); + poll_wait(fp, &pd->port_wait, pt); + + if (test_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag)) { + /* timed out, no packets received */ + clear_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag); + pd->port_rcvwait_to++; + } + } + else { + /* it's already happened; don't do wait_event overhead */ + pd->port_rcvnowait++; + } + + clear_bit(bit, &dd->ipath_rcvctrl); + ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, + dd->ipath_rcvctrl); + + return 0; +} + +static int try_alloc_port(struct ipath_devdata *dd, int port, + struct file *fp) +{ + int ret; + + if (!dd->ipath_pd[port]) { + void *p, *ptmp; + + p = kzalloc(sizeof(struct ipath_portdata), GFP_KERNEL); + + /* + * Allocate memory for use in ipath_tid_update() just once + * at open, not per call. Reduces cost of expected send + * setup. + */ + ptmp = kmalloc(dd->ipath_rcvtidcnt * sizeof(u16) + + dd->ipath_rcvtidcnt * sizeof(struct page **), + GFP_KERNEL); + if (!p || !ptmp) { + ipath_dev_err(dd, "Unable to allocate portdata " + "memory, failing open\n"); + ret = -ENOMEM; + kfree(p); + kfree(ptmp); + goto bail; + } + dd->ipath_pd[port] = p; + dd->ipath_pd[port]->port_port = port; + dd->ipath_pd[port]->port_dd = dd; + dd->ipath_pd[port]->port_tid_pg_list = ptmp; + init_waitqueue_head(&dd->ipath_pd[port]->port_wait); + } + if (!dd->ipath_pd[port]->port_cnt) { + dd->ipath_pd[port]->port_cnt = 1; + fp->private_data = (void *) dd->ipath_pd[port]; + ipath_cdbg(PROC, "%s[%u] opened unit:port %u:%u\n", + current->comm, current->pid, dd->ipath_unit, + port); + dd->ipath_pd[port]->port_pid = current->pid; + strncpy(dd->ipath_pd[port]->port_comm, current->comm, + sizeof(dd->ipath_pd[port]->port_comm)); + ipath_stats.sps_ports++; + ret = 0; + goto bail; + } + ret = -EBUSY; + +bail: + return ret; +} + +static inline int usable(struct ipath_devdata *dd) +{ + return dd && + (dd->ipath_flags & IPATH_PRESENT) && + dd->ipath_kregbase && + dd->ipath_lid && + !(dd->ipath_flags & (IPATH_LINKDOWN | IPATH_DISABLED + | IPATH_LINKUNK)); +} + +static int find_free_port(int unit, struct file *fp) +{ + struct ipath_devdata *dd = ipath_lookup(unit); + int ret, i; + + if (!dd) { + ret = -ENODEV; + goto bail; + } + + if (!usable(dd)) { + ret = -ENETDOWN; + goto bail; + } + + for (i = 0; i < dd->ipath_cfgports; i++) { + ret = try_alloc_port(dd, i, fp); + if (ret != -EBUSY) + goto bail; + } + ret = -EBUSY; + +bail: + return ret; +} + +static int find_best_unit(struct file *fp) +{ + int ret = 0, i, prefunit = -1, devmax; + int maxofallports, npresent, nup; + int ndev; + + (void) ipath_count_units(&npresent, &nup, &maxofallports); + + /* + * This code is present to allow a knowledgeable person to + * specify the layout of processes to processors before opening + * this driver, and then we'll assign the process to the "closest" + * HT-400 to that processor (we assume reasonable connectivity, + * for now). This code assumes that if affinity has been set + * before this point, that at most one cpu is set; for now this + * is reasonable. I check for both cpus_empty() and cpus_full(), + * in case some kernel variant sets none of the bits when no + * affinity is set. 2.6.11 and 12 kernels have all present + * cpus set. Some day we'll have to fix it up further to handle + * a cpu subset. This algorithm fails for two HT-400's connected + * in tunnel fashion. Eventually this needs real topology + * information. There may be some issues with dual core numbering + * as well. This needs more work prior to release. + */ + if (!cpus_empty(current->cpus_allowed) && + !cpus_full(current->cpus_allowed)) { + int ncpus = num_online_cpus(), curcpu = -1; + for (i = 0; i < ncpus; i++) + if (cpu_isset(i, current->cpus_allowed)) { + ipath_cdbg(PROC, "%s[%u] affinity set for " + "cpu %d\n", current->comm, + current->pid, i); + curcpu = i; + } + if (curcpu != -1) { + if (npresent) { + prefunit = curcpu / (ncpus / npresent); + ipath_dbg("%s[%u] %d chips, %d cpus, " + "%d cpus/chip, select unit %d\n", + current->comm, current->pid, + npresent, ncpus, ncpus / npresent, + prefunit); + } + } + } + + /* + * user ports start at 1, kernel port is 0 + * For now, we do round-robin access across all chips + */ + + if (prefunit != -1) + devmax = prefunit + 1; + else + devmax = ipath_count_units(NULL, NULL, NULL); +recheck: + for (i = 1; i < maxofallports; i++) { + for (ndev = prefunit != -1 ? prefunit : 0; ndev < devmax; + ndev++) { + struct ipath_devdata *dd = ipath_lookup(ndev); + + if (!usable(dd)) + continue; /* can't use this unit */ + if (i >= dd->ipath_cfgports) + /* + * Maxed out on users of this unit. Try + * next. + */ + continue; + ret = try_alloc_port(dd, i, fp); + if (!ret) + goto done; + } + } + + if (npresent) { + if (nup == 0) { + ret = -ENETDOWN; + ipath_dbg("No ports available (none initialized " + "and ready)\n"); + } else { + if (prefunit > 0) { + /* if started above 0, retry from 0 */ + ipath_cdbg(PROC, + "%s[%u] no ports on prefunit " + "%d, clear and re-check\n", + current->comm, current->pid, + prefunit); + devmax = ipath_count_units(NULL, NULL, + NULL); + prefunit = -1; + goto recheck; + } + ret = -EBUSY; + ipath_dbg("No ports available\n"); + } + } else { + ret = -ENXIO; + ipath_dbg("No boards found\n"); + } + +done: + return ret; +} + +static int ipath_open(struct inode *in, struct file *fp) +{ + int ret, minor; + + mutex_lock(&ipath_mutex); + + minor = iminor(in); + ipath_cdbg(VERBOSE, "open on dev %lx (minor %d)\n", + (long)in->i_rdev, minor); + + if (minor) + ret = find_free_port(minor - 1, fp); + else + ret = find_best_unit(fp); + + mutex_unlock(&ipath_mutex); + return ret; +} + +/** + * unlock_exptid - unlock any expected TID entries port still had in use + * @pd: port + * + * We don't actually update the chip here, because we do a bulk update + * below, using ipath_f_clear_tids. + */ +static void unlock_expected_tids(struct ipath_portdata *pd) +{ + struct ipath_devdata *dd = pd->port_dd; + int port_tidbase = pd->port_port * dd->ipath_rcvtidcnt; + int i, cnt = 0, maxtid = port_tidbase + dd->ipath_rcvtidcnt; + + ipath_cdbg(VERBOSE, "Port %u unlocking any locked expTID pages\n", + pd->port_port); + for (i = port_tidbase; i < maxtid; i++) { + if (!dd->ipath_pageshadow[i]) + continue; + + ipath_release_user_pages_on_close(&dd->ipath_pageshadow[i], + 1); + dd->ipath_pageshadow[i] = NULL; + cnt++; + ipath_stats.sps_pageunlocks++; + } + if (cnt) + ipath_cdbg(VERBOSE, "Port %u locked %u expTID entries\n", + pd->port_port, cnt); + + if (ipath_stats.sps_pagelocks || ipath_stats.sps_pageunlocks) + ipath_cdbg(VERBOSE, "%llu pages locked, %llu unlocked\n", + (unsigned long long) ipath_stats.sps_pagelocks, + (unsigned long long) + ipath_stats.sps_pageunlocks); +} + +static int ipath_close(struct inode *in, struct file *fp) +{ + int ret = 0; + struct ipath_portdata *pd; + struct ipath_devdata *dd; + unsigned port; + + ipath_cdbg(VERBOSE, "close on dev %lx, private data %p\n", + (long)in->i_rdev, fp->private_data); + + mutex_lock(&ipath_mutex); + + pd = port_fp(fp); + port = pd->port_port; + fp->private_data = NULL; + dd = pd->port_dd; + + if (pd->port_hdrqfull) { + ipath_cdbg(PROC, "%s[%u] had %u rcvhdrqfull errors " + "during run\n", pd->port_comm, pd->port_pid, + pd->port_hdrqfull); + pd->port_hdrqfull = 0; + } + + if (pd->port_rcvwait_to || pd->port_piowait_to + || pd->port_rcvnowait || pd->port_pionowait) { + ipath_cdbg(VERBOSE, "port%u, %u rcv, %u pio wait timeo; " + "%u rcv %u, pio already\n", + pd->port_port, pd->port_rcvwait_to, + pd->port_piowait_to, pd->port_rcvnowait, + pd->port_pionowait); + pd->port_rcvwait_to = pd->port_piowait_to = + pd->port_rcvnowait = pd->port_pionowait = 0; + } + if (pd->port_flag) { + ipath_dbg("port %u port_flag still set to 0x%lx\n", + pd->port_port, pd->port_flag); + pd->port_flag = 0; + } + + if (dd->ipath_kregbase) { + if (pd->port_rcvhdrtail_uaddr) { + pd->port_rcvhdrtail_uaddr = 0; + pd->port_rcvhdrtail_kvaddr = NULL; + ipath_release_user_pages_on_close( + &pd->port_rcvhdrtail_pagep, 1); + pd->port_rcvhdrtail_pagep = NULL; + ipath_stats.sps_pageunlocks++; + } + ipath_write_kreg_port( + dd, dd->ipath_kregs->kr_rcvhdrtailaddr, + port, 0ULL); + ipath_write_kreg_port( + dd, dd->ipath_kregs->kr_rcvhdraddr, + pd->port_port, 0); + + /* clean up the pkeys for this port user */ + ipath_clean_part_key(pd, dd); + + if (port < dd->ipath_cfgports) { + int i = dd->ipath_pbufsport * (port - 1); + ipath_disarm_piobufs(dd, i, dd->ipath_pbufsport); + + /* atomically clear receive enable port. */ + clear_bit(INFINIPATH_R_PORTENABLE_SHIFT + port, + &dd->ipath_rcvctrl); + ipath_write_kreg( + dd, + dd->ipath_kregs->kr_rcvctrl, + dd->ipath_rcvctrl); + + if (dd->ipath_pageshadow) + unlock_expected_tids(pd); + ipath_stats.sps_ports--; + ipath_cdbg(PROC, "%s[%u] closed port %u:%u\n", + pd->port_comm, pd->port_pid, + dd->ipath_unit, port); + } + } + + pd->port_cnt = 0; + pd->port_pid = 0; + + dd->ipath_f_clear_tids(dd, pd->port_port); + + ipath_free_pddata(dd, pd->port_port, 0); + + mutex_unlock(&ipath_mutex); + + return ret; +} + +static int ipath_port_info(struct ipath_portdata *pd, + struct ipath_port_info __user *uinfo) +{ + struct ipath_port_info info; + int nup; + int ret; + + (void) ipath_count_units(NULL, &nup, NULL); + info.num_active = nup; + info.unit = pd->port_dd->ipath_unit; + info.port = pd->port_port; + + if (copy_to_user(uinfo, &info, sizeof(info))) { + ret = -EFAULT; + goto bail; + } + ret = 0; + +bail: + return ret; +} + +static ssize_t ipath_write(struct file *fp, const char __user *data, + size_t count, loff_t *off) +{ + const struct ipath_cmd __user *ucmd; + struct ipath_portdata *pd; + const void __user *src; + size_t consumed, copy; + struct ipath_cmd cmd; + ssize_t ret = 0; + void *dest; + + if (count < sizeof(cmd.type)) { + ret = -EINVAL; + goto bail; + } + + ucmd = (const struct ipath_cmd __user *) data; + + if (copy_from_user(&cmd.type, &ucmd->type, sizeof(cmd.type))) { + ret = -EFAULT; + goto bail; + } + + consumed = sizeof(cmd.type); + + switch (cmd.type) { + case IPATH_CMD_USER_INIT: + copy = sizeof(cmd.cmd.user_info); + dest = &cmd.cmd.user_info; + src = &ucmd->cmd.user_info; + break; + case IPATH_CMD_RECV_CTRL: + copy = sizeof(cmd.cmd.recv_ctrl); + dest = &cmd.cmd.recv_ctrl; + src = &ucmd->cmd.recv_ctrl; + break; + case IPATH_CMD_PORT_INFO: + copy = sizeof(cmd.cmd.port_info); + dest = &cmd.cmd.port_info; + src = &ucmd->cmd.port_info; + break; + case IPATH_CMD_TID_UPDATE: + case IPATH_CMD_TID_FREE: + copy = sizeof(cmd.cmd.tid_info); + dest = &cmd.cmd.tid_info; + src = &ucmd->cmd.tid_info; + break; + case IPATH_CMD_SET_PART_KEY: + copy = sizeof(cmd.cmd.part_key); + dest = &cmd.cmd.part_key; + src = &ucmd->cmd.part_key; + break; + default: + ret = -EINVAL; + goto bail; + } + + if ((count - consumed) < copy) { + ret = -EINVAL; + goto bail; + } + + if (copy_from_user(dest, src, copy)) { + ret = -EFAULT; + goto bail; + } + + consumed += copy; + pd = port_fp(fp); + + switch (cmd.type) { + case IPATH_CMD_USER_INIT: + ret = ipath_do_user_init(pd, &cmd.cmd.user_info); + if (ret < 0) + goto bail; + ret = ipath_get_base_info( + pd, (void __user *) (unsigned long) + cmd.cmd.user_info.spu_base_info, + cmd.cmd.user_info.spu_base_info_size); + break; + case IPATH_CMD_RECV_CTRL: + ret = ipath_manage_rcvq(pd, cmd.cmd.recv_ctrl); + break; + case IPATH_CMD_PORT_INFO: + ret = ipath_port_info(pd, + (struct ipath_port_info __user *) + (unsigned long) cmd.cmd.port_info); + break; + case IPATH_CMD_TID_UPDATE: + ret = ipath_tid_update(pd, &cmd.cmd.tid_info); + break; + case IPATH_CMD_TID_FREE: + ret = ipath_tid_free(pd, &cmd.cmd.tid_info); + break; + case IPATH_CMD_SET_PART_KEY: + ret = ipath_set_part_key(pd, cmd.cmd.part_key); + break; + } + + if (ret >= 0) + ret = consumed; + +bail: + return ret; +} + +static struct class *ipath_class; + +static int init_cdev(int minor, char *name, struct file_operations *fops, + struct cdev **cdevp, struct class_device **class_devp) +{ + const dev_t dev = MKDEV(IPATH_MAJOR, minor); + struct cdev *cdev = NULL; + struct class_device *class_dev = NULL; + int ret; + + cdev = cdev_alloc(); + if (!cdev) { + printk(KERN_ERR IPATH_DRV_NAME + ": Could not allocate cdev for minor %d, %s\n", + minor, name); + ret = -ENOMEM; + goto done; + } + + cdev->owner = THIS_MODULE; + cdev->ops = fops; + kobject_set_name(&cdev->kobj, name); + + ret = cdev_add(cdev, dev, 1); + if (ret < 0) { + printk(KERN_ERR IPATH_DRV_NAME + ": Could not add cdev for minor %d, %s (err %d)\n", + minor, name, -ret); + goto err_cdev; + } + + class_dev = class_device_create(ipath_class, NULL, dev, NULL, name); + + if (IS_ERR(class_dev)) { + ret = PTR_ERR(class_dev); + printk(KERN_ERR IPATH_DRV_NAME ": Could not create " + "class_dev for minor %d, %s (err %d)\n", + minor, name, -ret); + goto err_cdev; + } + + goto done; + +err_cdev: + cdev_del(cdev); + cdev = NULL; + +done: + if (ret >= 0) { + *cdevp = cdev; + *class_devp = class_dev; + } else { + *cdevp = NULL; + *class_devp = NULL; + } + + return ret; +} + +int ipath_cdev_init(int minor, char *name, struct file_operations *fops, + struct cdev **cdevp, struct class_device **class_devp) +{ + return init_cdev(minor, name, fops, cdevp, class_devp); +} + +static void cleanup_cdev(struct cdev **cdevp, + struct class_device **class_devp) +{ + struct class_device *class_dev = *class_devp; + + if (class_dev) { + class_device_unregister(class_dev); + *class_devp = NULL; + } + + if (*cdevp) { + cdev_del(*cdevp); + *cdevp = NULL; + } +} + +void ipath_cdev_cleanup(struct cdev **cdevp, + struct class_device **class_devp) +{ + cleanup_cdev(cdevp, class_devp); +} + +static struct cdev *wildcard_cdev; +static struct class_device *wildcard_class_dev; + +static const dev_t dev = MKDEV(IPATH_MAJOR, 0); + +static int user_init(void) +{ + int ret; + + ret = register_chrdev_region(dev, IPATH_NMINORS, IPATH_DRV_NAME); + if (ret < 0) { + printk(KERN_ERR IPATH_DRV_NAME ": Could not register " + "chrdev region (err %d)\n", -ret); + goto done; + } + + ipath_class = class_create(THIS_MODULE, IPATH_DRV_NAME); + + if (IS_ERR(ipath_class)) { + ret = PTR_ERR(ipath_class); + printk(KERN_ERR IPATH_DRV_NAME ": Could not create " + "device class (err %d)\n", -ret); + goto bail; + } + + goto done; +bail: + unregister_chrdev_region(dev, IPATH_NMINORS); +done: + return ret; +} + +static void user_cleanup(void) +{ + if (ipath_class) { + class_destroy(ipath_class); + ipath_class = NULL; + } + + unregister_chrdev_region(dev, IPATH_NMINORS); +} + +static atomic_t user_count = ATOMIC_INIT(0); +static atomic_t user_setup = ATOMIC_INIT(0); + +int ipath_user_add(struct ipath_devdata *dd) +{ + char name[10]; + int ret; + + if (atomic_inc_return(&user_count) == 1) { + ret = user_init(); + if (ret < 0) { + ipath_dev_err(dd, "Unable to set up user support: " + "error %d\n", -ret); + goto bail; + } + ret = ipath_diag_init(); + if (ret < 0) { + ipath_dev_err(dd, "Unable to set up diag support: " + "error %d\n", -ret); + goto bail_sma; + } + + ret = init_cdev(0, "ipath", &ipath_file_ops, &wildcard_cdev, + &wildcard_class_dev); + if (ret < 0) { + ipath_dev_err(dd, "Could not create wildcard " + "minor: error %d\n", -ret); + goto bail_diag; + } + + atomic_set(&user_setup, 1); + } + + snprintf(name, sizeof(name), "ipath%d", dd->ipath_unit); + + ret = init_cdev(dd->ipath_unit + 1, name, &ipath_file_ops, + &dd->cdev, &dd->class_dev); + if (ret < 0) + ipath_dev_err(dd, "Could not create user minor %d, %s\n", + dd->ipath_unit + 1, name); + + goto bail; + +bail_diag: + ipath_diag_cleanup(); +bail_sma: + user_cleanup(); +bail: + return ret; +} + +void ipath_user_del(struct ipath_devdata *dd) +{ + cleanup_cdev(&dd->cdev, &dd->class_dev); + + if (atomic_dec_return(&user_count) == 0) { + if (atomic_read(&user_setup) == 0) + goto bail; + + cleanup_cdev(&wildcard_cdev, &wildcard_class_dev); + ipath_diag_cleanup(); + user_cleanup(); + + atomic_set(&user_setup, 0); + } +bail: + return; +} diff --git a/drivers/infiniband/hw/ipath/ipath_fs.c b/drivers/infiniband/hw/ipath/ipath_fs.c new file mode 100644 index 000000000000..e274120567e1 --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_fs.c @@ -0,0 +1,605 @@ +/* + * Copyright (c) 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ipath_kernel.h" + +#define IPATHFS_MAGIC 0x726a77 + +static struct super_block *ipath_super; + +static int ipathfs_mknod(struct inode *dir, struct dentry *dentry, + int mode, struct file_operations *fops, + void *data) +{ + int error; + struct inode *inode = new_inode(dir->i_sb); + + if (!inode) { + error = -EPERM; + goto bail; + } + + inode->i_mode = mode; + inode->i_uid = 0; + inode->i_gid = 0; + inode->i_blksize = PAGE_CACHE_SIZE; + inode->i_blocks = 0; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->u.generic_ip = data; + if ((mode & S_IFMT) == S_IFDIR) { + inode->i_op = &simple_dir_inode_operations; + inode->i_nlink++; + dir->i_nlink++; + } + + inode->i_fop = fops; + + d_instantiate(dentry, inode); + error = 0; + +bail: + return error; +} + +static int create_file(const char *name, mode_t mode, + struct dentry *parent, struct dentry **dentry, + struct file_operations *fops, void *data) +{ + int error; + + *dentry = NULL; + mutex_lock(&parent->d_inode->i_mutex); + *dentry = lookup_one_len(name, parent, strlen(name)); + if (!IS_ERR(dentry)) + error = ipathfs_mknod(parent->d_inode, *dentry, + mode, fops, data); + else + error = PTR_ERR(dentry); + mutex_unlock(&parent->d_inode->i_mutex); + + return error; +} + +static ssize_t atomic_stats_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + return simple_read_from_buffer(buf, count, ppos, &ipath_stats, + sizeof ipath_stats); +} + +static struct file_operations atomic_stats_ops = { + .read = atomic_stats_read, +}; + +#define NUM_COUNTERS sizeof(struct infinipath_counters) / sizeof(u64) + +static ssize_t atomic_counters_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + u64 counters[NUM_COUNTERS]; + u16 i; + struct ipath_devdata *dd; + + dd = file->f_dentry->d_inode->u.generic_ip; + + for (i = 0; i < NUM_COUNTERS; i++) + counters[i] = ipath_snap_cntr(dd, i); + + return simple_read_from_buffer(buf, count, ppos, counters, + sizeof counters); +} + +static struct file_operations atomic_counters_ops = { + .read = atomic_counters_read, +}; + +static ssize_t atomic_node_info_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + u32 nodeinfo[10]; + struct ipath_devdata *dd; + u64 guid; + + dd = file->f_dentry->d_inode->u.generic_ip; + + guid = be64_to_cpu(dd->ipath_guid); + + nodeinfo[0] = /* BaseVersion is SMA */ + /* ClassVersion is SMA */ + (1 << 8) /* NodeType */ + | (1 << 0); /* NumPorts */ + nodeinfo[1] = (u32) (guid >> 32); + nodeinfo[2] = (u32) (guid & 0xffffffff); + /* PortGUID == SystemImageGUID for us */ + nodeinfo[3] = nodeinfo[1]; + /* PortGUID == SystemImageGUID for us */ + nodeinfo[4] = nodeinfo[2]; + /* PortGUID == NodeGUID for us */ + nodeinfo[5] = nodeinfo[3]; + /* PortGUID == NodeGUID for us */ + nodeinfo[6] = nodeinfo[4]; + nodeinfo[7] = (4 << 16) /* we support 4 pkeys */ + | (dd->ipath_deviceid << 0); + /* our chip version as 16 bits major, 16 bits minor */ + nodeinfo[8] = dd->ipath_minrev | (dd->ipath_majrev << 16); + nodeinfo[9] = (dd->ipath_unit << 24) | (dd->ipath_vendorid << 0); + + return simple_read_from_buffer(buf, count, ppos, nodeinfo, + sizeof nodeinfo); +} + +static struct file_operations atomic_node_info_ops = { + .read = atomic_node_info_read, +}; + +static ssize_t atomic_port_info_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + u32 portinfo[13]; + u32 tmp, tmp2; + struct ipath_devdata *dd; + + dd = file->f_dentry->d_inode->u.generic_ip; + + /* so we only initialize non-zero fields. */ + memset(portinfo, 0, sizeof portinfo); + + /* + * Notimpl yet M_Key (64) + * Notimpl yet GID (64) + */ + + portinfo[4] = (dd->ipath_lid << 16); + + /* + * Notimpl yet SMLID (should we store this in the driver, in case + * SMA dies?) CapabilityMask is 0, we don't support any of these + * DiagCode is 0; we don't store any diag info for now Notimpl yet + * M_KeyLeasePeriod (we don't support M_Key) + */ + + /* LocalPortNum is whichever port number they ask for */ + portinfo[7] = (dd->ipath_unit << 24) + /* LinkWidthEnabled */ + | (2 << 16) + /* LinkWidthSupported (really 2, but not IB valid) */ + | (3 << 8) + /* LinkWidthActive */ + | (2 << 0); + tmp = dd->ipath_lastibcstat & IPATH_IBSTATE_MASK; + tmp2 = 5; + if (tmp == IPATH_IBSTATE_INIT) + tmp = 2; + else if (tmp == IPATH_IBSTATE_ARM) + tmp = 3; + else if (tmp == IPATH_IBSTATE_ACTIVE) + tmp = 4; + else { + tmp = 0; /* down */ + tmp2 = tmp & 0xf; + } + + portinfo[8] = (1 << 28) /* LinkSpeedSupported */ + | (tmp << 24) /* PortState */ + | (tmp2 << 20) /* PortPhysicalState */ + | (2 << 16) + + /* LinkDownDefaultState */ + /* M_KeyProtectBits == 0 */ + /* NotImpl yet LMC == 0 (we can support all values) */ + | (1 << 4) /* LinkSpeedActive */ + | (1 << 0); /* LinkSpeedEnabled */ + switch (dd->ipath_ibmtu) { + case 4096: + tmp = 5; + break; + case 2048: + tmp = 4; + break; + case 1024: + tmp = 3; + break; + case 512: + tmp = 2; + break; + case 256: + tmp = 1; + break; + default: /* oops, something is wrong */ + ipath_dbg("Problem, ipath_ibmtu 0x%x not a valid IB MTU, " + "treat as 2048\n", dd->ipath_ibmtu); + tmp = 4; + break; + } + portinfo[9] = (tmp << 28) + /* NeighborMTU */ + /* Notimpl MasterSMSL */ + | (1 << 20) + + /* VLCap */ + /* Notimpl InitType (actually, an SMA decision) */ + /* VLHighLimit is 0 (only one VL) */ + ; /* VLArbitrationHighCap is 0 (only one VL) */ + portinfo[10] = /* VLArbitrationLowCap is 0 (only one VL) */ + /* InitTypeReply is SMA decision */ + (5 << 16) /* MTUCap 4096 */ + | (7 << 13) /* VLStallCount */ + | (0x1f << 8) /* HOQLife */ + | (1 << 4) + + /* OperationalVLs 0 */ + /* PartitionEnforcementInbound */ + /* PartitionEnforcementOutbound not enforced */ + /* FilterRawinbound not enforced */ + ; /* FilterRawOutbound not enforced */ + /* M_KeyViolations are not counted by hardware, SMA can count */ + tmp = ipath_read_creg32(dd, dd->ipath_cregs->cr_errpkey); + /* P_KeyViolations are counted by hardware. */ + portinfo[11] = ((tmp & 0xffff) << 0); + portinfo[12] = + /* Q_KeyViolations are not counted by hardware */ + (1 << 8) + + /* GUIDCap */ + /* SubnetTimeOut handled by SMA */ + /* RespTimeValue handled by SMA */ + ; + /* LocalPhyErrors are programmed to max */ + portinfo[12] |= (0xf << 20) + | (0xf << 16) /* OverRunErrors are programmed to max */ + ; + + return simple_read_from_buffer(buf, count, ppos, portinfo, + sizeof portinfo); +} + +static struct file_operations atomic_port_info_ops = { + .read = atomic_port_info_read, +}; + +static ssize_t flash_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct ipath_devdata *dd; + ssize_t ret; + loff_t pos; + char *tmp; + + pos = *ppos; + + if ( pos < 0) { + ret = -EINVAL; + goto bail; + } + + if (pos >= sizeof(struct ipath_flash)) { + ret = 0; + goto bail; + } + + if (count > sizeof(struct ipath_flash) - pos) + count = sizeof(struct ipath_flash) - pos; + + tmp = kmalloc(count, GFP_KERNEL); + if (!tmp) { + ret = -ENOMEM; + goto bail; + } + + dd = file->f_dentry->d_inode->u.generic_ip; + if (ipath_eeprom_read(dd, pos, tmp, count)) { + ipath_dev_err(dd, "failed to read from flash\n"); + ret = -ENXIO; + goto bail_tmp; + } + + if (copy_to_user(buf, tmp, count)) { + ret = -EFAULT; + goto bail_tmp; + } + + *ppos = pos + count; + ret = count; + +bail_tmp: + kfree(tmp); + +bail: + return ret; +} + +static ssize_t flash_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct ipath_devdata *dd; + ssize_t ret; + loff_t pos; + char *tmp; + + pos = *ppos; + + if ( pos < 0) { + ret = -EINVAL; + goto bail; + } + + if (pos >= sizeof(struct ipath_flash)) { + ret = 0; + goto bail; + } + + if (count > sizeof(struct ipath_flash) - pos) + count = sizeof(struct ipath_flash) - pos; + + tmp = kmalloc(count, GFP_KERNEL); + if (!tmp) { + ret = -ENOMEM; + goto bail; + } + + if (copy_from_user(tmp, buf, count)) { + ret = -EFAULT; + goto bail_tmp; + } + + dd = file->f_dentry->d_inode->u.generic_ip; + if (ipath_eeprom_write(dd, pos, tmp, count)) { + ret = -ENXIO; + ipath_dev_err(dd, "failed to write to flash\n"); + goto bail_tmp; + } + + *ppos = pos + count; + ret = count; + +bail_tmp: + kfree(tmp); + +bail: + return ret; +} + +static struct file_operations flash_ops = { + .read = flash_read, + .write = flash_write, +}; + +static int create_device_files(struct super_block *sb, + struct ipath_devdata *dd) +{ + struct dentry *dir, *tmp; + char unit[10]; + int ret; + + snprintf(unit, sizeof unit, "%02d", dd->ipath_unit); + ret = create_file(unit, S_IFDIR|S_IRUGO|S_IXUGO, sb->s_root, &dir, + (struct file_operations *) &simple_dir_operations, + dd); + if (ret) { + printk(KERN_ERR "create_file(%s) failed: %d\n", unit, ret); + goto bail; + } + + ret = create_file("atomic_counters", S_IFREG|S_IRUGO, dir, &tmp, + &atomic_counters_ops, dd); + if (ret) { + printk(KERN_ERR "create_file(%s/atomic_counters) " + "failed: %d\n", unit, ret); + goto bail; + } + + ret = create_file("node_info", S_IFREG|S_IRUGO, dir, &tmp, + &atomic_node_info_ops, dd); + if (ret) { + printk(KERN_ERR "create_file(%s/node_info) " + "failed: %d\n", unit, ret); + goto bail; + } + + ret = create_file("port_info", S_IFREG|S_IRUGO, dir, &tmp, + &atomic_port_info_ops, dd); + if (ret) { + printk(KERN_ERR "create_file(%s/port_info) " + "failed: %d\n", unit, ret); + goto bail; + } + + ret = create_file("flash", S_IFREG|S_IWUSR|S_IRUGO, dir, &tmp, + &flash_ops, dd); + if (ret) { + printk(KERN_ERR "create_file(%s/flash) " + "failed: %d\n", unit, ret); + goto bail; + } + +bail: + return ret; +} + +static void remove_file(struct dentry *parent, char *name) +{ + struct dentry *tmp; + + tmp = lookup_one_len(name, parent, strlen(name)); + + spin_lock(&dcache_lock); + spin_lock(&tmp->d_lock); + if (!(d_unhashed(tmp) && tmp->d_inode)) { + dget_locked(tmp); + __d_drop(tmp); + spin_unlock(&tmp->d_lock); + spin_unlock(&dcache_lock); + simple_unlink(parent->d_inode, tmp); + } else { + spin_unlock(&tmp->d_lock); + spin_unlock(&dcache_lock); + } +} + +static int remove_device_files(struct super_block *sb, + struct ipath_devdata *dd) +{ + struct dentry *dir, *root; + char unit[10]; + int ret; + + root = dget(sb->s_root); + mutex_lock(&root->d_inode->i_mutex); + snprintf(unit, sizeof unit, "%02d", dd->ipath_unit); + dir = lookup_one_len(unit, root, strlen(unit)); + + if (IS_ERR(dir)) { + ret = PTR_ERR(dir); + printk(KERN_ERR "Lookup of %s failed\n", unit); + goto bail; + } + + remove_file(dir, "flash"); + remove_file(dir, "port_info"); + remove_file(dir, "node_info"); + remove_file(dir, "atomic_counters"); + d_delete(dir); + ret = simple_rmdir(root->d_inode, dir); + +bail: + mutex_unlock(&root->d_inode->i_mutex); + dput(root); + return ret; +} + +static int ipathfs_fill_super(struct super_block *sb, void *data, + int silent) +{ + struct ipath_devdata *dd, *tmp; + unsigned long flags; + int ret; + + static struct tree_descr files[] = { + [1] = {"atomic_stats", &atomic_stats_ops, S_IRUGO}, + {""}, + }; + + ret = simple_fill_super(sb, IPATHFS_MAGIC, files); + if (ret) { + printk(KERN_ERR "simple_fill_super failed: %d\n", ret); + goto bail; + } + + spin_lock_irqsave(&ipath_devs_lock, flags); + + list_for_each_entry_safe(dd, tmp, &ipath_dev_list, ipath_list) { + spin_unlock_irqrestore(&ipath_devs_lock, flags); + ret = create_device_files(sb, dd); + if (ret) { + deactivate_super(sb); + goto bail; + } + spin_lock_irqsave(&ipath_devs_lock, flags); + } + + spin_unlock_irqrestore(&ipath_devs_lock, flags); + +bail: + return ret; +} + +static struct super_block *ipathfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data) +{ + ipath_super = get_sb_single(fs_type, flags, data, + ipathfs_fill_super); + return ipath_super; +} + +static void ipathfs_kill_super(struct super_block *s) +{ + kill_litter_super(s); + ipath_super = NULL; +} + +int ipathfs_add_device(struct ipath_devdata *dd) +{ + int ret; + + if (ipath_super == NULL) { + ret = 0; + goto bail; + } + + ret = create_device_files(ipath_super, dd); + +bail: + return ret; +} + +int ipathfs_remove_device(struct ipath_devdata *dd) +{ + int ret; + + if (ipath_super == NULL) { + ret = 0; + goto bail; + } + + ret = remove_device_files(ipath_super, dd); + +bail: + return ret; +} + +static struct file_system_type ipathfs_fs_type = { + .owner = THIS_MODULE, + .name = "ipathfs", + .get_sb = ipathfs_get_sb, + .kill_sb = ipathfs_kill_super, +}; + +int __init ipath_init_ipathfs(void) +{ + return register_filesystem(&ipathfs_fs_type); +} + +void __exit ipath_exit_ipathfs(void) +{ + unregister_filesystem(&ipathfs_fs_type); +} diff --git a/drivers/infiniband/hw/ipath/ipath_ht400.c b/drivers/infiniband/hw/ipath/ipath_ht400.c new file mode 100644 index 000000000000..4652435998f3 --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_ht400.c @@ -0,0 +1,1586 @@ +/* + * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * This file contains all of the code that is specific to the InfiniPath + * HT-400 chip. + */ + +#include +#include + +#include "ipath_kernel.h" +#include "ipath_registers.h" + +/* + * This lists the InfiniPath HT400 registers, in the actual chip layout. + * This structure should never be directly accessed. + * + * The names are in InterCap form because they're taken straight from + * the chip specification. Since they're only used in this file, they + * don't pollute the rest of the source. +*/ + +struct _infinipath_do_not_use_kernel_regs { + unsigned long long Revision; + unsigned long long Control; + unsigned long long PageAlign; + unsigned long long PortCnt; + unsigned long long DebugPortSelect; + unsigned long long DebugPort; + unsigned long long SendRegBase; + unsigned long long UserRegBase; + unsigned long long CounterRegBase; + unsigned long long Scratch; + unsigned long long ReservedMisc1; + unsigned long long InterruptConfig; + unsigned long long IntBlocked; + unsigned long long IntMask; + unsigned long long IntStatus; + unsigned long long IntClear; + unsigned long long ErrorMask; + unsigned long long ErrorStatus; + unsigned long long ErrorClear; + unsigned long long HwErrMask; + unsigned long long HwErrStatus; + unsigned long long HwErrClear; + unsigned long long HwDiagCtrl; + unsigned long long MDIO; + unsigned long long IBCStatus; + unsigned long long IBCCtrl; + unsigned long long ExtStatus; + unsigned long long ExtCtrl; + unsigned long long GPIOOut; + unsigned long long GPIOMask; + unsigned long long GPIOStatus; + unsigned long long GPIOClear; + unsigned long long RcvCtrl; + unsigned long long RcvBTHQP; + unsigned long long RcvHdrSize; + unsigned long long RcvHdrCnt; + unsigned long long RcvHdrEntSize; + unsigned long long RcvTIDBase; + unsigned long long RcvTIDCnt; + unsigned long long RcvEgrBase; + unsigned long long RcvEgrCnt; + unsigned long long RcvBufBase; + unsigned long long RcvBufSize; + unsigned long long RxIntMemBase; + unsigned long long RxIntMemSize; + unsigned long long RcvPartitionKey; + unsigned long long ReservedRcv[10]; + unsigned long long SendCtrl; + unsigned long long SendPIOBufBase; + unsigned long long SendPIOSize; + unsigned long long SendPIOBufCnt; + unsigned long long SendPIOAvailAddr; + unsigned long long TxIntMemBase; + unsigned long long TxIntMemSize; + unsigned long long ReservedSend[9]; + unsigned long long SendBufferError; + unsigned long long SendBufferErrorCONT1; + unsigned long long SendBufferErrorCONT2; + unsigned long long SendBufferErrorCONT3; + unsigned long long ReservedSBE[4]; + unsigned long long RcvHdrAddr0; + unsigned long long RcvHdrAddr1; + unsigned long long RcvHdrAddr2; + unsigned long long RcvHdrAddr3; + unsigned long long RcvHdrAddr4; + unsigned long long RcvHdrAddr5; + unsigned long long RcvHdrAddr6; + unsigned long long RcvHdrAddr7; + unsigned long long RcvHdrAddr8; + unsigned long long ReservedRHA[7]; + unsigned long long RcvHdrTailAddr0; + unsigned long long RcvHdrTailAddr1; + unsigned long long RcvHdrTailAddr2; + unsigned long long RcvHdrTailAddr3; + unsigned long long RcvHdrTailAddr4; + unsigned long long RcvHdrTailAddr5; + unsigned long long RcvHdrTailAddr6; + unsigned long long RcvHdrTailAddr7; + unsigned long long RcvHdrTailAddr8; + unsigned long long ReservedRHTA[7]; + unsigned long long Sync; /* Software only */ + unsigned long long Dump; /* Software only */ + unsigned long long SimVer; /* Software only */ + unsigned long long ReservedSW[5]; + unsigned long long SerdesConfig0; + unsigned long long SerdesConfig1; + unsigned long long SerdesStatus; + unsigned long long XGXSConfig; + unsigned long long ReservedSW2[4]; +}; + +#define IPATH_KREG_OFFSET(field) (offsetof(struct \ + _infinipath_do_not_use_kernel_regs, field) / sizeof(u64)) +#define IPATH_CREG_OFFSET(field) (offsetof( \ + struct infinipath_counters, field) / sizeof(u64)) + +static const struct ipath_kregs ipath_ht_kregs = { + .kr_control = IPATH_KREG_OFFSET(Control), + .kr_counterregbase = IPATH_KREG_OFFSET(CounterRegBase), + .kr_debugport = IPATH_KREG_OFFSET(DebugPort), + .kr_debugportselect = IPATH_KREG_OFFSET(DebugPortSelect), + .kr_errorclear = IPATH_KREG_OFFSET(ErrorClear), + .kr_errormask = IPATH_KREG_OFFSET(ErrorMask), + .kr_errorstatus = IPATH_KREG_OFFSET(ErrorStatus), + .kr_extctrl = IPATH_KREG_OFFSET(ExtCtrl), + .kr_extstatus = IPATH_KREG_OFFSET(ExtStatus), + .kr_gpio_clear = IPATH_KREG_OFFSET(GPIOClear), + .kr_gpio_mask = IPATH_KREG_OFFSET(GPIOMask), + .kr_gpio_out = IPATH_KREG_OFFSET(GPIOOut), + .kr_gpio_status = IPATH_KREG_OFFSET(GPIOStatus), + .kr_hwdiagctrl = IPATH_KREG_OFFSET(HwDiagCtrl), + .kr_hwerrclear = IPATH_KREG_OFFSET(HwErrClear), + .kr_hwerrmask = IPATH_KREG_OFFSET(HwErrMask), + .kr_hwerrstatus = IPATH_KREG_OFFSET(HwErrStatus), + .kr_ibcctrl = IPATH_KREG_OFFSET(IBCCtrl), + .kr_ibcstatus = IPATH_KREG_OFFSET(IBCStatus), + .kr_intblocked = IPATH_KREG_OFFSET(IntBlocked), + .kr_intclear = IPATH_KREG_OFFSET(IntClear), + .kr_interruptconfig = IPATH_KREG_OFFSET(InterruptConfig), + .kr_intmask = IPATH_KREG_OFFSET(IntMask), + .kr_intstatus = IPATH_KREG_OFFSET(IntStatus), + .kr_mdio = IPATH_KREG_OFFSET(MDIO), + .kr_pagealign = IPATH_KREG_OFFSET(PageAlign), + .kr_partitionkey = IPATH_KREG_OFFSET(RcvPartitionKey), + .kr_portcnt = IPATH_KREG_OFFSET(PortCnt), + .kr_rcvbthqp = IPATH_KREG_OFFSET(RcvBTHQP), + .kr_rcvbufbase = IPATH_KREG_OFFSET(RcvBufBase), + .kr_rcvbufsize = IPATH_KREG_OFFSET(RcvBufSize), + .kr_rcvctrl = IPATH_KREG_OFFSET(RcvCtrl), + .kr_rcvegrbase = IPATH_KREG_OFFSET(RcvEgrBase), + .kr_rcvegrcnt = IPATH_KREG_OFFSET(RcvEgrCnt), + .kr_rcvhdrcnt = IPATH_KREG_OFFSET(RcvHdrCnt), + .kr_rcvhdrentsize = IPATH_KREG_OFFSET(RcvHdrEntSize), + .kr_rcvhdrsize = IPATH_KREG_OFFSET(RcvHdrSize), + .kr_rcvintmembase = IPATH_KREG_OFFSET(RxIntMemBase), + .kr_rcvintmemsize = IPATH_KREG_OFFSET(RxIntMemSize), + .kr_rcvtidbase = IPATH_KREG_OFFSET(RcvTIDBase), + .kr_rcvtidcnt = IPATH_KREG_OFFSET(RcvTIDCnt), + .kr_revision = IPATH_KREG_OFFSET(Revision), + .kr_scratch = IPATH_KREG_OFFSET(Scratch), + .kr_sendbuffererror = IPATH_KREG_OFFSET(SendBufferError), + .kr_sendctrl = IPATH_KREG_OFFSET(SendCtrl), + .kr_sendpioavailaddr = IPATH_KREG_OFFSET(SendPIOAvailAddr), + .kr_sendpiobufbase = IPATH_KREG_OFFSET(SendPIOBufBase), + .kr_sendpiobufcnt = IPATH_KREG_OFFSET(SendPIOBufCnt), + .kr_sendpiosize = IPATH_KREG_OFFSET(SendPIOSize), + .kr_sendregbase = IPATH_KREG_OFFSET(SendRegBase), + .kr_txintmembase = IPATH_KREG_OFFSET(TxIntMemBase), + .kr_txintmemsize = IPATH_KREG_OFFSET(TxIntMemSize), + .kr_userregbase = IPATH_KREG_OFFSET(UserRegBase), + .kr_serdesconfig0 = IPATH_KREG_OFFSET(SerdesConfig0), + .kr_serdesconfig1 = IPATH_KREG_OFFSET(SerdesConfig1), + .kr_serdesstatus = IPATH_KREG_OFFSET(SerdesStatus), + .kr_xgxsconfig = IPATH_KREG_OFFSET(XGXSConfig), + /* + * These should not be used directly via ipath_read_kreg64(), + * use them with ipath_read_kreg64_port(), + */ + .kr_rcvhdraddr = IPATH_KREG_OFFSET(RcvHdrAddr0), + .kr_rcvhdrtailaddr = IPATH_KREG_OFFSET(RcvHdrTailAddr0) +}; + +static const struct ipath_cregs ipath_ht_cregs = { + .cr_badformatcnt = IPATH_CREG_OFFSET(RxBadFormatCnt), + .cr_erricrccnt = IPATH_CREG_OFFSET(RxICRCErrCnt), + .cr_errlinkcnt = IPATH_CREG_OFFSET(RxLinkProblemCnt), + .cr_errlpcrccnt = IPATH_CREG_OFFSET(RxLPCRCErrCnt), + .cr_errpkey = IPATH_CREG_OFFSET(RxPKeyMismatchCnt), + .cr_errrcvflowctrlcnt = IPATH_CREG_OFFSET(RxFlowCtrlErrCnt), + .cr_err_rlencnt = IPATH_CREG_OFFSET(RxLenErrCnt), + .cr_errslencnt = IPATH_CREG_OFFSET(TxLenErrCnt), + .cr_errtidfull = IPATH_CREG_OFFSET(RxTIDFullErrCnt), + .cr_errtidvalid = IPATH_CREG_OFFSET(RxTIDValidErrCnt), + .cr_errvcrccnt = IPATH_CREG_OFFSET(RxVCRCErrCnt), + .cr_ibstatuschange = IPATH_CREG_OFFSET(IBStatusChangeCnt), + /* calc from Reg_CounterRegBase + offset */ + .cr_intcnt = IPATH_CREG_OFFSET(LBIntCnt), + .cr_invalidrlencnt = IPATH_CREG_OFFSET(RxMaxMinLenErrCnt), + .cr_invalidslencnt = IPATH_CREG_OFFSET(TxMaxMinLenErrCnt), + .cr_lbflowstallcnt = IPATH_CREG_OFFSET(LBFlowStallCnt), + .cr_pktrcvcnt = IPATH_CREG_OFFSET(RxDataPktCnt), + .cr_pktrcvflowctrlcnt = IPATH_CREG_OFFSET(RxFlowPktCnt), + .cr_pktsendcnt = IPATH_CREG_OFFSET(TxDataPktCnt), + .cr_pktsendflowcnt = IPATH_CREG_OFFSET(TxFlowPktCnt), + .cr_portovflcnt = IPATH_CREG_OFFSET(RxP0HdrEgrOvflCnt), + .cr_rcvebpcnt = IPATH_CREG_OFFSET(RxEBPCnt), + .cr_rcvovflcnt = IPATH_CREG_OFFSET(RxBufOvflCnt), + .cr_senddropped = IPATH_CREG_OFFSET(TxDroppedPktCnt), + .cr_sendstallcnt = IPATH_CREG_OFFSET(TxFlowStallCnt), + .cr_sendunderruncnt = IPATH_CREG_OFFSET(TxUnderrunCnt), + .cr_wordrcvcnt = IPATH_CREG_OFFSET(RxDwordCnt), + .cr_wordsendcnt = IPATH_CREG_OFFSET(TxDwordCnt), + .cr_unsupvlcnt = IPATH_CREG_OFFSET(TxUnsupVLErrCnt), + .cr_rxdroppktcnt = IPATH_CREG_OFFSET(RxDroppedPktCnt), + .cr_iblinkerrrecovcnt = IPATH_CREG_OFFSET(IBLinkErrRecoveryCnt), + .cr_iblinkdowncnt = IPATH_CREG_OFFSET(IBLinkDownedCnt), + .cr_ibsymbolerrcnt = IPATH_CREG_OFFSET(IBSymbolErrCnt) +}; + +/* kr_intstatus, kr_intclear, kr_intmask bits */ +#define INFINIPATH_I_RCVURG_MASK 0x1FF +#define INFINIPATH_I_RCVAVAIL_MASK 0x1FF + +/* kr_hwerrclear, kr_hwerrmask, kr_hwerrstatus, bits */ +#define INFINIPATH_HWE_HTCMEMPARITYERR_SHIFT 0 +#define INFINIPATH_HWE_HTCMEMPARITYERR_MASK 0x3FFFFFULL +#define INFINIPATH_HWE_HTCLNKABYTE0CRCERR 0x0000000000800000ULL +#define INFINIPATH_HWE_HTCLNKABYTE1CRCERR 0x0000000001000000ULL +#define INFINIPATH_HWE_HTCLNKBBYTE0CRCERR 0x0000000002000000ULL +#define INFINIPATH_HWE_HTCLNKBBYTE1CRCERR 0x0000000004000000ULL +#define INFINIPATH_HWE_HTCMISCERR4 0x0000000008000000ULL +#define INFINIPATH_HWE_HTCMISCERR5 0x0000000010000000ULL +#define INFINIPATH_HWE_HTCMISCERR6 0x0000000020000000ULL +#define INFINIPATH_HWE_HTCMISCERR7 0x0000000040000000ULL +#define INFINIPATH_HWE_HTCBUSTREQPARITYERR 0x0000000080000000ULL +#define INFINIPATH_HWE_HTCBUSTRESPPARITYERR 0x0000000100000000ULL +#define INFINIPATH_HWE_HTCBUSIREQPARITYERR 0x0000000200000000ULL +#define INFINIPATH_HWE_COREPLL_FBSLIP 0x0080000000000000ULL +#define INFINIPATH_HWE_COREPLL_RFSLIP 0x0100000000000000ULL +#define INFINIPATH_HWE_HTBPLL_FBSLIP 0x0200000000000000ULL +#define INFINIPATH_HWE_HTBPLL_RFSLIP 0x0400000000000000ULL +#define INFINIPATH_HWE_HTAPLL_FBSLIP 0x0800000000000000ULL +#define INFINIPATH_HWE_HTAPLL_RFSLIP 0x1000000000000000ULL +#define INFINIPATH_HWE_SERDESPLLFAILED 0x2000000000000000ULL + +/* kr_extstatus bits */ +#define INFINIPATH_EXTS_FREQSEL 0x2 +#define INFINIPATH_EXTS_SERDESSEL 0x4 +#define INFINIPATH_EXTS_MEMBIST_ENDTEST 0x0000000000004000 +#define INFINIPATH_EXTS_MEMBIST_CORRECT 0x0000000000008000 + +/* + * masks and bits that are different in different chips, or present only + * in one + */ +static const ipath_err_t infinipath_hwe_htcmemparityerr_mask = + INFINIPATH_HWE_HTCMEMPARITYERR_MASK; +static const ipath_err_t infinipath_hwe_htcmemparityerr_shift = + INFINIPATH_HWE_HTCMEMPARITYERR_SHIFT; + +static const ipath_err_t infinipath_hwe_htclnkabyte0crcerr = + INFINIPATH_HWE_HTCLNKABYTE0CRCERR; +static const ipath_err_t infinipath_hwe_htclnkabyte1crcerr = + INFINIPATH_HWE_HTCLNKABYTE1CRCERR; +static const ipath_err_t infinipath_hwe_htclnkbbyte0crcerr = + INFINIPATH_HWE_HTCLNKBBYTE0CRCERR; +static const ipath_err_t infinipath_hwe_htclnkbbyte1crcerr = + INFINIPATH_HWE_HTCLNKBBYTE1CRCERR; + +#define _IPATH_GPIO_SDA_NUM 1 +#define _IPATH_GPIO_SCL_NUM 0 + +#define IPATH_GPIO_SDA \ + (1ULL << (_IPATH_GPIO_SDA_NUM+INFINIPATH_EXTC_GPIOOE_SHIFT)) +#define IPATH_GPIO_SCL \ + (1ULL << (_IPATH_GPIO_SCL_NUM+INFINIPATH_EXTC_GPIOOE_SHIFT)) + +/* keep the code below somewhat more readonable; not used elsewhere */ +#define _IPATH_HTLINK0_CRCBITS (infinipath_hwe_htclnkabyte0crcerr | \ + infinipath_hwe_htclnkabyte1crcerr) +#define _IPATH_HTLINK1_CRCBITS (infinipath_hwe_htclnkbbyte0crcerr | \ + infinipath_hwe_htclnkbbyte1crcerr) +#define _IPATH_HTLANE0_CRCBITS (infinipath_hwe_htclnkabyte0crcerr | \ + infinipath_hwe_htclnkbbyte0crcerr) +#define _IPATH_HTLANE1_CRCBITS (infinipath_hwe_htclnkabyte1crcerr | \ + infinipath_hwe_htclnkbbyte1crcerr) + +static void hwerr_crcbits(struct ipath_devdata *dd, ipath_err_t hwerrs, + char *msg, size_t msgl) +{ + char bitsmsg[64]; + ipath_err_t crcbits = hwerrs & + (_IPATH_HTLINK0_CRCBITS | _IPATH_HTLINK1_CRCBITS); + /* don't check if 8bit HT */ + if (dd->ipath_flags & IPATH_8BIT_IN_HT0) + crcbits &= ~infinipath_hwe_htclnkabyte1crcerr; + /* don't check if 8bit HT */ + if (dd->ipath_flags & IPATH_8BIT_IN_HT1) + crcbits &= ~infinipath_hwe_htclnkbbyte1crcerr; + /* + * we'll want to ignore link errors on link that is + * not in use, if any. For now, complain about both + */ + if (crcbits) { + u16 ctrl0, ctrl1; + snprintf(bitsmsg, sizeof bitsmsg, + "[HT%s lane %s CRC (%llx); ignore till reload]", + !(crcbits & _IPATH_HTLINK1_CRCBITS) ? + "0 (A)" : (!(crcbits & _IPATH_HTLINK0_CRCBITS) + ? "1 (B)" : "0+1 (A+B)"), + !(crcbits & _IPATH_HTLANE1_CRCBITS) ? "0" + : (!(crcbits & _IPATH_HTLANE0_CRCBITS) ? "1" : + "0+1"), (unsigned long long) crcbits); + strlcat(msg, bitsmsg, msgl); + + /* + * print extra info for debugging. slave/primary + * config word 4, 8 (link control 0, 1) + */ + + if (pci_read_config_word(dd->pcidev, + dd->ipath_ht_slave_off + 0x4, + &ctrl0)) + dev_info(&dd->pcidev->dev, "Couldn't read " + "linkctrl0 of slave/primary " + "config block\n"); + else if (!(ctrl0 & 1 << 6)) + /* not if EOC bit set */ + ipath_dbg("HT linkctrl0 0x%x%s%s\n", ctrl0, + ((ctrl0 >> 8) & 7) ? " CRC" : "", + ((ctrl0 >> 4) & 1) ? "linkfail" : + ""); + if (pci_read_config_word(dd->pcidev, + dd->ipath_ht_slave_off + 0x8, + &ctrl1)) + dev_info(&dd->pcidev->dev, "Couldn't read " + "linkctrl1 of slave/primary " + "config block\n"); + else if (!(ctrl1 & 1 << 6)) + /* not if EOC bit set */ + ipath_dbg("HT linkctrl1 0x%x%s%s\n", ctrl1, + ((ctrl1 >> 8) & 7) ? " CRC" : "", + ((ctrl1 >> 4) & 1) ? "linkfail" : + ""); + + /* disable until driver reloaded */ + dd->ipath_hwerrmask &= ~crcbits; + ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrmask, + dd->ipath_hwerrmask); + ipath_dbg("HT crc errs: %s\n", msg); + } else + ipath_dbg("ignoring HT crc errors 0x%llx, " + "not in use\n", (unsigned long long) + (hwerrs & (_IPATH_HTLINK0_CRCBITS | + _IPATH_HTLINK1_CRCBITS))); +} + +/** + * ipath_ht_handle_hwerrors - display hardware errors + * @dd: the infinipath device + * @msg: the output buffer + * @msgl: the size of the output buffer + * + * Use same msg buffer as regular errors to avoid + * excessive stack use. Most hardware errors are catastrophic, but for + * right now, we'll print them and continue. + * We reuse the same message buffer as ipath_handle_errors() to avoid + * excessive stack usage. + */ +static void ipath_ht_handle_hwerrors(struct ipath_devdata *dd, char *msg, + size_t msgl) +{ + ipath_err_t hwerrs; + u32 bits, ctrl; + int isfatal = 0; + char bitsmsg[64]; + + hwerrs = ipath_read_kreg64(dd, dd->ipath_kregs->kr_hwerrstatus); + + if (!hwerrs) { + ipath_cdbg(VERBOSE, "Called but no hardware errors set\n"); + /* + * better than printing cofusing messages + * This seems to be related to clearing the crc error, or + * the pll error during init. + */ + goto bail; + } else if (hwerrs == -1LL) { + ipath_dev_err(dd, "Read of hardware error status failed " + "(all bits set); ignoring\n"); + goto bail; + } + ipath_stats.sps_hwerrs++; + + /* Always clear the error status register, except MEMBISTFAIL, + * regardless of whether we continue or stop using the chip. + * We want that set so we know it failed, even across driver reload. + * We'll still ignore it in the hwerrmask. We do this partly for + * diagnostics, but also for support */ + ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrclear, + hwerrs&~INFINIPATH_HWE_MEMBISTFAILED); + + hwerrs &= dd->ipath_hwerrmask; + + /* + * make sure we get this much out, unless told to be quiet, + * or it's occurred within the last 5 seconds + */ + if ((hwerrs & ~dd->ipath_lasthwerror) || + (ipath_debug & __IPATH_VERBDBG)) + dev_info(&dd->pcidev->dev, "Hardware error: hwerr=0x%llx " + "(cleared)\n", (unsigned long long) hwerrs); + dd->ipath_lasthwerror |= hwerrs; + + if (hwerrs & ~infinipath_hwe_bitsextant) + ipath_dev_err(dd, "hwerror interrupt with unknown errors " + "%llx set\n", (unsigned long long) + (hwerrs & ~infinipath_hwe_bitsextant)); + + ctrl = ipath_read_kreg32(dd, dd->ipath_kregs->kr_control); + if (ctrl & INFINIPATH_C_FREEZEMODE) { + if (hwerrs) { + /* + * if any set that we aren't ignoring; only + * make the complaint once, in case it's stuck + * or recurring, and we get here multiple + * times. + */ + if (dd->ipath_flags & IPATH_INITTED) { + ipath_dev_err(dd, "Fatal Error (freeze " + "mode), no longer usable\n"); + isfatal = 1; + } + *dd->ipath_statusp &= ~IPATH_STATUS_IB_READY; + /* mark as having had error */ + *dd->ipath_statusp |= IPATH_STATUS_HWERROR; + /* + * mark as not usable, at a minimum until driver + * is reloaded, probably until reboot, since no + * other reset is possible. + */ + dd->ipath_flags &= ~IPATH_INITTED; + } else { + ipath_dbg("Clearing freezemode on ignored hardware " + "error\n"); + ctrl &= ~INFINIPATH_C_FREEZEMODE; + ipath_write_kreg(dd, dd->ipath_kregs->kr_control, + ctrl); + } + } + + *msg = '\0'; + + /* + * may someday want to decode into which bits are which + * functional area for parity errors, etc. + */ + if (hwerrs & (infinipath_hwe_htcmemparityerr_mask + << INFINIPATH_HWE_HTCMEMPARITYERR_SHIFT)) { + bits = (u32) ((hwerrs >> + INFINIPATH_HWE_HTCMEMPARITYERR_SHIFT) & + INFINIPATH_HWE_HTCMEMPARITYERR_MASK); + snprintf(bitsmsg, sizeof bitsmsg, "[HTC Parity Errs %x] ", + bits); + strlcat(msg, bitsmsg, msgl); + } + if (hwerrs & (INFINIPATH_HWE_RXEMEMPARITYERR_MASK + << INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT)) { + bits = (u32) ((hwerrs >> + INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) & + INFINIPATH_HWE_RXEMEMPARITYERR_MASK); + snprintf(bitsmsg, sizeof bitsmsg, "[RXE Parity Errs %x] ", + bits); + strlcat(msg, bitsmsg, msgl); + } + if (hwerrs & (INFINIPATH_HWE_TXEMEMPARITYERR_MASK + << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)) { + bits = (u32) ((hwerrs >> + INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT) & + INFINIPATH_HWE_TXEMEMPARITYERR_MASK); + snprintf(bitsmsg, sizeof bitsmsg, "[TXE Parity Errs %x] ", + bits); + strlcat(msg, bitsmsg, msgl); + } + if (hwerrs & INFINIPATH_HWE_IBCBUSTOSPCPARITYERR) + strlcat(msg, "[IB2IPATH Parity]", msgl); + if (hwerrs & INFINIPATH_HWE_IBCBUSFRSPCPARITYERR) + strlcat(msg, "[IPATH2IB Parity]", msgl); + if (hwerrs & INFINIPATH_HWE_HTCBUSIREQPARITYERR) + strlcat(msg, "[HTC Ireq Parity]", msgl); + if (hwerrs & INFINIPATH_HWE_HTCBUSTREQPARITYERR) + strlcat(msg, "[HTC Treq Parity]", msgl); + if (hwerrs & INFINIPATH_HWE_HTCBUSTRESPPARITYERR) + strlcat(msg, "[HTC Tresp Parity]", msgl); + + if (hwerrs & (_IPATH_HTLINK0_CRCBITS | _IPATH_HTLINK1_CRCBITS)) + hwerr_crcbits(dd, hwerrs, msg, msgl); + + if (hwerrs & INFINIPATH_HWE_HTCMISCERR5) + strlcat(msg, "[HT core Misc5]", msgl); + if (hwerrs & INFINIPATH_HWE_HTCMISCERR6) + strlcat(msg, "[HT core Misc6]", msgl); + if (hwerrs & INFINIPATH_HWE_HTCMISCERR7) + strlcat(msg, "[HT core Misc7]", msgl); + if (hwerrs & INFINIPATH_HWE_MEMBISTFAILED) { + strlcat(msg, "[Memory BIST test failed, HT-400 unusable]", + msgl); + /* ignore from now on, so disable until driver reloaded */ + dd->ipath_hwerrmask &= ~INFINIPATH_HWE_MEMBISTFAILED; + ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrmask, + dd->ipath_hwerrmask); + } +#define _IPATH_PLL_FAIL (INFINIPATH_HWE_COREPLL_FBSLIP | \ + INFINIPATH_HWE_COREPLL_RFSLIP | \ + INFINIPATH_HWE_HTBPLL_FBSLIP | \ + INFINIPATH_HWE_HTBPLL_RFSLIP | \ + INFINIPATH_HWE_HTAPLL_FBSLIP | \ + INFINIPATH_HWE_HTAPLL_RFSLIP) + + if (hwerrs & _IPATH_PLL_FAIL) { + snprintf(bitsmsg, sizeof bitsmsg, + "[PLL failed (%llx), HT-400 unusable]", + (unsigned long long) (hwerrs & _IPATH_PLL_FAIL)); + strlcat(msg, bitsmsg, msgl); + /* ignore from now on, so disable until driver reloaded */ + dd->ipath_hwerrmask &= ~(hwerrs & _IPATH_PLL_FAIL); + ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrmask, + dd->ipath_hwerrmask); + } + + if (hwerrs & INFINIPATH_HWE_SERDESPLLFAILED) { + /* + * If it occurs, it is left masked since the eternal + * interface is unused + */ + dd->ipath_hwerrmask &= ~INFINIPATH_HWE_SERDESPLLFAILED; + ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrmask, + dd->ipath_hwerrmask); + } + + if (hwerrs & INFINIPATH_HWE_RXDSYNCMEMPARITYERR) + strlcat(msg, "[Rx Dsync]", msgl); + if (hwerrs & INFINIPATH_HWE_SERDESPLLFAILED) + strlcat(msg, "[SerDes PLL]", msgl); + + ipath_dev_err(dd, "%s hardware error\n", msg); + if (isfatal && !ipath_diag_inuse && dd->ipath_freezemsg) + /* + * for status file; if no trailing brace is copied, + * we'll know it was truncated. + */ + snprintf(dd->ipath_freezemsg, + dd->ipath_freezelen, "{%s}", msg); + +bail:; +} + +/** + * ipath_ht_boardname - fill in the board name + * @dd: the infinipath device + * @name: the output buffer + * @namelen: the size of the output buffer + * + * fill in the board name, based on the board revision register + */ +static int ipath_ht_boardname(struct ipath_devdata *dd, char *name, + size_t namelen) +{ + char *n = NULL; + u8 boardrev = dd->ipath_boardrev; + int ret; + + switch (boardrev) { + case 4: /* Ponderosa is one of the bringup boards */ + n = "Ponderosa"; + break; + case 5: /* HT-460 original production board */ + n = "InfiniPath_HT-460"; + break; + case 6: + n = "OEM_Board_3"; + break; + case 7: + /* HT-460 small form factor production board */ + n = "InfiniPath_HT-465"; + break; + case 8: + n = "LS/X-1"; + break; + case 9: /* Comstock bringup test board */ + n = "Comstock"; + break; + case 10: + n = "OEM_Board_2"; + break; + case 11: + n = "InfiniPath_HT-470"; + break; + case 12: + n = "OEM_Board_4"; + break; + default: /* don't know, just print the number */ + ipath_dev_err(dd, "Don't yet know about board " + "with ID %u\n", boardrev); + snprintf(name, namelen, "Unknown_InfiniPath_HT-4xx_%u", + boardrev); + break; + } + if (n) + snprintf(name, namelen, "%s", n); + + if (dd->ipath_majrev != 3 || dd->ipath_minrev != 2) { + /* + * This version of the driver only supports the HT-400 + * Rev 3.2 + */ + ipath_dev_err(dd, + "Unsupported HT-400 revision %u.%u!\n", + dd->ipath_majrev, dd->ipath_minrev); + ret = 1; + goto bail; + } + /* + * pkt/word counters are 32 bit, and therefore wrap fast enough + * that we snapshot them from a timer, and maintain 64 bit shadow + * copies + */ + dd->ipath_flags |= IPATH_32BITCOUNTERS; + if (dd->ipath_htspeed != 800) + ipath_dev_err(dd, + "Incorrectly configured for HT @ %uMHz\n", + dd->ipath_htspeed); + if (dd->ipath_boardrev == 7 || dd->ipath_boardrev == 11 || + dd->ipath_boardrev == 6) + dd->ipath_flags |= IPATH_GPIO_INTR; + else + dd->ipath_flags |= IPATH_POLL_RX_INTR; + if (dd->ipath_boardrev == 8) { /* LS/X-1 */ + u64 val; + val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extstatus); + if (val & INFINIPATH_EXTS_SERDESSEL) { + /* + * hardware disabled + * + * This means that the chip is hardware disabled, + * and will not be able to bring up the link, + * in any case. We special case this and abort + * early, to avoid later messages. We also set + * the DISABLED status bit + */ + ipath_dbg("Unit %u is hardware-disabled\n", + dd->ipath_unit); + *dd->ipath_statusp |= IPATH_STATUS_DISABLED; + /* this value is handled differently */ + ret = 2; + goto bail; + } + } + ret = 0; + +bail: + return ret; +} + +static void ipath_check_htlink(struct ipath_devdata *dd) +{ + u8 linkerr, link_off, i; + + for (i = 0; i < 2; i++) { + link_off = dd->ipath_ht_slave_off + i * 4 + 0xd; + if (pci_read_config_byte(dd->pcidev, link_off, &linkerr)) + dev_info(&dd->pcidev->dev, "Couldn't read " + "linkerror%d of HT slave/primary block\n", + i); + else if (linkerr & 0xf0) { + ipath_cdbg(VERBOSE, "HT linkerr%d bits 0x%x set, " + "clearing\n", linkerr >> 4, i); + /* + * writing the linkerr bits that are set should + * clear them + */ + if (pci_write_config_byte(dd->pcidev, link_off, + linkerr)) + ipath_dbg("Failed write to clear HT " + "linkerror%d\n", i); + if (pci_read_config_byte(dd->pcidev, link_off, + &linkerr)) + dev_info(&dd->pcidev->dev, + "Couldn't reread linkerror%d of " + "HT slave/primary block\n", i); + else if (linkerr & 0xf0) + dev_info(&dd->pcidev->dev, + "HT linkerror%d bits 0x%x " + "couldn't be cleared\n", + i, linkerr >> 4); + } + } +} + +static int ipath_setup_ht_reset(struct ipath_devdata *dd) +{ + ipath_dbg("No reset possible for HT-400\n"); + return 0; +} + +#define HT_CAPABILITY_ID 0x08 /* HT capabilities not defined in kernel */ +#define HT_INTR_DISC_CONFIG 0x80 /* HT interrupt and discovery cap */ +#define HT_INTR_REG_INDEX 2 /* intconfig requires indirect accesses */ + +/* + * Bits 13-15 of command==0 is slave/primary block. Clear any HT CRC + * errors. We only bother to do this at load time, because it's OK if + * it happened before we were loaded (first time after boot/reset), + * but any time after that, it's fatal anyway. Also need to not check + * for for upper byte errors if we are in 8 bit mode, so figure out + * our width. For now, at least, also complain if it's 8 bit. + */ +static void slave_or_pri_blk(struct ipath_devdata *dd, struct pci_dev *pdev, + int pos, u8 cap_type) +{ + u8 linkwidth = 0, linkerr, link_a_b_off, link_off; + u16 linkctrl = 0; + int i; + + dd->ipath_ht_slave_off = pos; + /* command word, master_host bit */ + /* master host || slave */ + if ((cap_type >> 2) & 1) + link_a_b_off = 4; + else + link_a_b_off = 0; + ipath_cdbg(VERBOSE, "HT%u (Link %c) connected to processor\n", + link_a_b_off ? 1 : 0, + link_a_b_off ? 'B' : 'A'); + + link_a_b_off += pos; + + /* + * check both link control registers; clear both HT CRC sets if + * necessary. + */ + for (i = 0; i < 2; i++) { + link_off = pos + i * 4 + 0x4; + if (pci_read_config_word(pdev, link_off, &linkctrl)) + ipath_dev_err(dd, "Couldn't read HT link control%d " + "register\n", i); + else if (linkctrl & (0xf << 8)) { + ipath_cdbg(VERBOSE, "Clear linkctrl%d CRC Error " + "bits %x\n", i, linkctrl & (0xf << 8)); + /* + * now write them back to clear the error. + */ + pci_write_config_byte(pdev, link_off, + linkctrl & (0xf << 8)); + } + } + + /* + * As with HT CRC bits, same for protocol errors that might occur + * during boot. + */ + for (i = 0; i < 2; i++) { + link_off = pos + i * 4 + 0xd; + if (pci_read_config_byte(pdev, link_off, &linkerr)) + dev_info(&pdev->dev, "Couldn't read linkerror%d " + "of HT slave/primary block\n", i); + else if (linkerr & 0xf0) { + ipath_cdbg(VERBOSE, "HT linkerr%d bits 0x%x set, " + "clearing\n", linkerr >> 4, i); + /* + * writing the linkerr bits that are set will clear + * them + */ + if (pci_write_config_byte + (pdev, link_off, linkerr)) + ipath_dbg("Failed write to clear HT " + "linkerror%d\n", i); + if (pci_read_config_byte(pdev, link_off, &linkerr)) + dev_info(&pdev->dev, "Couldn't reread " + "linkerror%d of HT slave/primary " + "block\n", i); + else if (linkerr & 0xf0) + dev_info(&pdev->dev, "HT linkerror%d bits " + "0x%x couldn't be cleared\n", + i, linkerr >> 4); + } + } + + /* + * this is just for our link to the host, not devices connected + * through tunnel. + */ + + if (pci_read_config_byte(pdev, link_a_b_off + 7, &linkwidth)) + ipath_dev_err(dd, "Couldn't read HT link width " + "config register\n"); + else { + u32 width; + switch (linkwidth & 7) { + case 5: + width = 4; + break; + case 4: + width = 2; + break; + case 3: + width = 32; + break; + case 1: + width = 16; + break; + case 0: + default: /* if wrong, assume 8 bit */ + width = 8; + break; + } + + dd->ipath_htwidth = width; + + if (linkwidth != 0x11) { + ipath_dev_err(dd, "Not configured for 16 bit HT " + "(%x)\n", linkwidth); + if (!(linkwidth & 0xf)) { + ipath_dbg("Will ignore HT lane1 errors\n"); + dd->ipath_flags |= IPATH_8BIT_IN_HT0; + } + } + } + + /* + * this is just for our link to the host, not devices connected + * through tunnel. + */ + if (pci_read_config_byte(pdev, link_a_b_off + 0xd, &linkwidth)) + ipath_dev_err(dd, "Couldn't read HT link frequency " + "config register\n"); + else { + u32 speed; + switch (linkwidth & 0xf) { + case 6: + speed = 1000; + break; + case 5: + speed = 800; + break; + case 4: + speed = 600; + break; + case 3: + speed = 500; + break; + case 2: + speed = 400; + break; + case 1: + speed = 300; + break; + default: + /* + * assume reserved and vendor-specific are 200... + */ + case 0: + speed = 200; + break; + } + dd->ipath_htspeed = speed; + } +} + +static int set_int_handler(struct ipath_devdata *dd, struct pci_dev *pdev, + int pos) +{ + u32 int_handler_addr_lower; + u32 int_handler_addr_upper; + u64 ihandler; + u32 intvec; + + /* use indirection register to get the intr handler */ + pci_write_config_byte(pdev, pos + HT_INTR_REG_INDEX, 0x10); + pci_read_config_dword(pdev, pos + 4, &int_handler_addr_lower); + pci_write_config_byte(pdev, pos + HT_INTR_REG_INDEX, 0x11); + pci_read_config_dword(pdev, pos + 4, &int_handler_addr_upper); + + ihandler = (u64) int_handler_addr_lower | + ((u64) int_handler_addr_upper << 32); + + /* + * kernels with CONFIG_PCI_MSI set the vector in the irq field of + * struct pci_device, so we use that to program the HT-400 internal + * interrupt register (not config space) with that value. The BIOS + * must still have done the basic MSI setup. + */ + intvec = pdev->irq; + /* + * clear any vector bits there; normally not set but we'll overload + * this for some debug purposes (setting the HTC debug register + * value from software, rather than GPIOs), so it might be set on a + * driver reload. + */ + ihandler &= ~0xff0000; + /* x86 vector goes in intrinfo[23:16] */ + ihandler |= intvec << 16; + ipath_cdbg(VERBOSE, "ihandler lower %x, upper %x, intvec %x, " + "interruptconfig %llx\n", int_handler_addr_lower, + int_handler_addr_upper, intvec, + (unsigned long long) ihandler); + + /* can't program yet, so save for interrupt setup */ + dd->ipath_intconfig = ihandler; + /* keep going, so we find link control stuff also */ + + return ihandler != 0; +} + +/** + * ipath_setup_ht_config - setup the interruptconfig register + * @dd: the infinipath device + * @pdev: the PCI device + * + * setup the interruptconfig register from the HT config info. + * Also clear CRC errors in HT linkcontrol, if necessary. + * This is done only for the real hardware. It is done before + * chip address space is initted, so can't touch infinipath registers + */ +static int ipath_setup_ht_config(struct ipath_devdata *dd, + struct pci_dev *pdev) +{ + int pos, ret = 0; + int ihandler = 0; + + /* + * Read the capability info to find the interrupt info, and also + * handle clearing CRC errors in linkctrl register if necessary. We + * do this early, before we ever enable errors or hardware errors, + * mostly to avoid causing the chip to enter freeze mode. + */ + pos = pci_find_capability(pdev, HT_CAPABILITY_ID); + if (!pos) { + ipath_dev_err(dd, "Couldn't find HyperTransport " + "capability; no interrupts\n"); + ret = -ENODEV; + goto bail; + } + do { + u8 cap_type; + + /* the HT capability type byte is 3 bytes after the + * capability byte. + */ + if (pci_read_config_byte(pdev, pos + 3, &cap_type)) { + dev_info(&pdev->dev, "Couldn't read config " + "command @ %d\n", pos); + continue; + } + if (!(cap_type & 0xE0)) + slave_or_pri_blk(dd, pdev, pos, cap_type); + else if (cap_type == HT_INTR_DISC_CONFIG) + ihandler = set_int_handler(dd, pdev, pos); + } while ((pos = pci_find_next_capability(pdev, pos, + HT_CAPABILITY_ID))); + + if (!ihandler) { + ipath_dev_err(dd, "Couldn't find interrupt handler in " + "config space\n"); + ret = -ENODEV; + } + +bail: + return ret; +} + +/** + * ipath_setup_ht_cleanup - clean up any per-chip chip-specific stuff + * @dd: the infinipath device + * + * Called during driver unload. + * This is currently a nop for the HT-400, not for all chips + */ +static void ipath_setup_ht_cleanup(struct ipath_devdata *dd) +{ +} + +/** + * ipath_setup_ht_setextled - set the state of the two external LEDs + * @dd: the infinipath device + * @lst: the L state + * @ltst: the LT state + * + * Set the state of the two external LEDs, to indicate physical and + * logical state of IB link. For this chip (at least with recommended + * board pinouts), LED1 is Green (physical state), and LED2 is Yellow + * (logical state) + * + * Note: We try to match the Mellanox HCA LED behavior as best + * we can. Green indicates physical link state is OK (something is + * plugged in, and we can train). + * Amber indicates the link is logically up (ACTIVE). + * Mellanox further blinks the amber LED to indicate data packet + * activity, but we have no hardware support for that, so it would + * require waking up every 10-20 msecs and checking the counters + * on the chip, and then turning the LED off if appropriate. That's + * visible overhead, so not something we will do. + * + */ +static void ipath_setup_ht_setextled(struct ipath_devdata *dd, + u64 lst, u64 ltst) +{ + u64 extctl; + + /* the diags use the LED to indicate diag info, so we leave + * the external LED alone when the diags are running */ + if (ipath_diag_inuse) + return; + + /* + * start by setting both LED control bits to off, then turn + * on the appropriate bit(s). + */ + if (dd->ipath_boardrev == 8) { /* LS/X-1 uses different pins */ + /* + * major difference is that INFINIPATH_EXTC_LEDGBLERR_OFF + * is inverted, because it is normally used to indicate + * a hardware fault at reset, if there were errors + */ + extctl = (dd->ipath_extctrl & ~INFINIPATH_EXTC_LEDGBLOK_ON) + | INFINIPATH_EXTC_LEDGBLERR_OFF; + if (ltst == INFINIPATH_IBCS_LT_STATE_LINKUP) + extctl &= ~INFINIPATH_EXTC_LEDGBLERR_OFF; + if (lst == INFINIPATH_IBCS_L_STATE_ACTIVE) + extctl |= INFINIPATH_EXTC_LEDGBLOK_ON; + } + else { + extctl = dd->ipath_extctrl & + ~(INFINIPATH_EXTC_LED1PRIPORT_ON | + INFINIPATH_EXTC_LED2PRIPORT_ON); + if (ltst == INFINIPATH_IBCS_LT_STATE_LINKUP) + extctl |= INFINIPATH_EXTC_LED1PRIPORT_ON; + if (lst == INFINIPATH_IBCS_L_STATE_ACTIVE) + extctl |= INFINIPATH_EXTC_LED2PRIPORT_ON; + } + dd->ipath_extctrl = extctl; + ipath_write_kreg(dd, dd->ipath_kregs->kr_extctrl, extctl); +} + +static void ipath_init_ht_variables(void) +{ + ipath_gpio_sda_num = _IPATH_GPIO_SDA_NUM; + ipath_gpio_scl_num = _IPATH_GPIO_SCL_NUM; + ipath_gpio_sda = IPATH_GPIO_SDA; + ipath_gpio_scl = IPATH_GPIO_SCL; + + infinipath_i_bitsextant = + (INFINIPATH_I_RCVURG_MASK << INFINIPATH_I_RCVURG_SHIFT) | + (INFINIPATH_I_RCVAVAIL_MASK << + INFINIPATH_I_RCVAVAIL_SHIFT) | + INFINIPATH_I_ERROR | INFINIPATH_I_SPIOSENT | + INFINIPATH_I_SPIOBUFAVAIL | INFINIPATH_I_GPIO; + + infinipath_e_bitsextant = + INFINIPATH_E_RFORMATERR | INFINIPATH_E_RVCRC | + INFINIPATH_E_RICRC | INFINIPATH_E_RMINPKTLEN | + INFINIPATH_E_RMAXPKTLEN | INFINIPATH_E_RLONGPKTLEN | + INFINIPATH_E_RSHORTPKTLEN | INFINIPATH_E_RUNEXPCHAR | + INFINIPATH_E_RUNSUPVL | INFINIPATH_E_REBP | + INFINIPATH_E_RIBFLOW | INFINIPATH_E_RBADVERSION | + INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL | + INFINIPATH_E_RBADTID | INFINIPATH_E_RHDRLEN | + INFINIPATH_E_RHDR | INFINIPATH_E_RIBLOSTLINK | + INFINIPATH_E_SMINPKTLEN | INFINIPATH_E_SMAXPKTLEN | + INFINIPATH_E_SUNDERRUN | INFINIPATH_E_SPKTLEN | + INFINIPATH_E_SDROPPEDSMPPKT | INFINIPATH_E_SDROPPEDDATAPKT | + INFINIPATH_E_SPIOARMLAUNCH | INFINIPATH_E_SUNEXPERRPKTNUM | + INFINIPATH_E_SUNSUPVL | INFINIPATH_E_IBSTATUSCHANGED | + INFINIPATH_E_INVALIDADDR | INFINIPATH_E_RESET | + INFINIPATH_E_HARDWARE; + + infinipath_hwe_bitsextant = + (INFINIPATH_HWE_HTCMEMPARITYERR_MASK << + INFINIPATH_HWE_HTCMEMPARITYERR_SHIFT) | + (INFINIPATH_HWE_TXEMEMPARITYERR_MASK << + INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT) | + (INFINIPATH_HWE_RXEMEMPARITYERR_MASK << + INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) | + INFINIPATH_HWE_HTCLNKABYTE0CRCERR | + INFINIPATH_HWE_HTCLNKABYTE1CRCERR | + INFINIPATH_HWE_HTCLNKBBYTE0CRCERR | + INFINIPATH_HWE_HTCLNKBBYTE1CRCERR | + INFINIPATH_HWE_HTCMISCERR4 | + INFINIPATH_HWE_HTCMISCERR5 | INFINIPATH_HWE_HTCMISCERR6 | + INFINIPATH_HWE_HTCMISCERR7 | + INFINIPATH_HWE_HTCBUSTREQPARITYERR | + INFINIPATH_HWE_HTCBUSTRESPPARITYERR | + INFINIPATH_HWE_HTCBUSIREQPARITYERR | + INFINIPATH_HWE_RXDSYNCMEMPARITYERR | + INFINIPATH_HWE_MEMBISTFAILED | + INFINIPATH_HWE_COREPLL_FBSLIP | + INFINIPATH_HWE_COREPLL_RFSLIP | + INFINIPATH_HWE_HTBPLL_FBSLIP | + INFINIPATH_HWE_HTBPLL_RFSLIP | + INFINIPATH_HWE_HTAPLL_FBSLIP | + INFINIPATH_HWE_HTAPLL_RFSLIP | + INFINIPATH_HWE_SERDESPLLFAILED | + INFINIPATH_HWE_IBCBUSTOSPCPARITYERR | + INFINIPATH_HWE_IBCBUSFRSPCPARITYERR; + + infinipath_i_rcvavail_mask = INFINIPATH_I_RCVAVAIL_MASK; + infinipath_i_rcvurg_mask = INFINIPATH_I_RCVURG_MASK; +} + +/** + * ipath_ht_init_hwerrors - enable hardware errors + * @dd: the infinipath device + * + * now that we have finished initializing everything that might reasonably + * cause a hardware error, and cleared those errors bits as they occur, + * we can enable hardware errors in the mask (potentially enabling + * freeze mode), and enable hardware errors as errors (along with + * everything else) in errormask + */ +static void ipath_ht_init_hwerrors(struct ipath_devdata *dd) +{ + ipath_err_t val; + u64 extsval; + + extsval = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extstatus); + + if (!(extsval & INFINIPATH_EXTS_MEMBIST_ENDTEST)) + ipath_dev_err(dd, "MemBIST did not complete!\n"); + + ipath_check_htlink(dd); + + /* barring bugs, all hwerrors become interrupts, which can */ + val = -1LL; + /* don't look at crc lane1 if 8 bit */ + if (dd->ipath_flags & IPATH_8BIT_IN_HT0) + val &= ~infinipath_hwe_htclnkabyte1crcerr; + /* don't look at crc lane1 if 8 bit */ + if (dd->ipath_flags & IPATH_8BIT_IN_HT1) + val &= ~infinipath_hwe_htclnkbbyte1crcerr; + + /* + * disable RXDSYNCMEMPARITY because external serdes is unused, + * and therefore the logic will never be used or initialized, + * and uninitialized state will normally result in this error + * being asserted. Similarly for the external serdess pll + * lock signal. + */ + val &= ~(INFINIPATH_HWE_SERDESPLLFAILED | + INFINIPATH_HWE_RXDSYNCMEMPARITYERR); + + /* + * Disable MISCERR4 because of an inversion in the HT core + * logic checking for errors that cause this bit to be set. + * The errata can also cause the protocol error bit to be set + * in the HT config space linkerror register(s). + */ + val &= ~INFINIPATH_HWE_HTCMISCERR4; + + /* + * PLL ignored because MDIO interface has a logic problem + * for reads, on Comstock and Ponderosa. BRINGUP + */ + if (dd->ipath_boardrev == 4 || dd->ipath_boardrev == 9) + val &= ~INFINIPATH_HWE_SERDESPLLFAILED; + dd->ipath_hwerrmask = val; +} + +/** + * ipath_ht_bringup_serdes - bring up the serdes + * @dd: the infinipath device + */ +static int ipath_ht_bringup_serdes(struct ipath_devdata *dd) +{ + u64 val, config1; + int ret = 0, change = 0; + + ipath_dbg("Trying to bringup serdes\n"); + + if (ipath_read_kreg64(dd, dd->ipath_kregs->kr_hwerrstatus) & + INFINIPATH_HWE_SERDESPLLFAILED) + { + ipath_dbg("At start, serdes PLL failed bit set in " + "hwerrstatus, clearing and continuing\n"); + ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrclear, + INFINIPATH_HWE_SERDESPLLFAILED); + } + + val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesconfig0); + config1 = ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesconfig1); + + ipath_cdbg(VERBOSE, "Initial serdes status is config0=%llx " + "config1=%llx, sstatus=%llx xgxs %llx\n", + (unsigned long long) val, (unsigned long long) config1, + (unsigned long long) + ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesstatus), + (unsigned long long) + ipath_read_kreg64(dd, dd->ipath_kregs->kr_xgxsconfig)); + + /* force reset on */ + val |= INFINIPATH_SERDC0_RESET_PLL + /* | INFINIPATH_SERDC0_RESET_MASK */ + ; + ipath_write_kreg(dd, dd->ipath_kregs->kr_serdesconfig0, val); + udelay(15); /* need pll reset set at least for a bit */ + + if (val & INFINIPATH_SERDC0_RESET_PLL) { + u64 val2 = val &= ~INFINIPATH_SERDC0_RESET_PLL; + /* set lane resets, and tx idle, during pll reset */ + val2 |= INFINIPATH_SERDC0_RESET_MASK | + INFINIPATH_SERDC0_TXIDLE; + ipath_cdbg(VERBOSE, "Clearing serdes PLL reset (writing " + "%llx)\n", (unsigned long long) val2); + ipath_write_kreg(dd, dd->ipath_kregs->kr_serdesconfig0, + val2); + /* + * be sure chip saw it + */ + val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch); + /* + * need pll reset clear at least 11 usec before lane + * resets cleared; give it a few more + */ + udelay(15); + val = val2; /* for check below */ + } + + if (val & (INFINIPATH_SERDC0_RESET_PLL | + INFINIPATH_SERDC0_RESET_MASK | + INFINIPATH_SERDC0_TXIDLE)) { + val &= ~(INFINIPATH_SERDC0_RESET_PLL | + INFINIPATH_SERDC0_RESET_MASK | + INFINIPATH_SERDC0_TXIDLE); + /* clear them */ + ipath_write_kreg(dd, dd->ipath_kregs->kr_serdesconfig0, + val); + } + + val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_xgxsconfig); + if (((val >> INFINIPATH_XGXS_MDIOADDR_SHIFT) & + INFINIPATH_XGXS_MDIOADDR_MASK) != 3) { + val &= ~(INFINIPATH_XGXS_MDIOADDR_MASK << + INFINIPATH_XGXS_MDIOADDR_SHIFT); + /* + * we use address 3 + */ + val |= 3ULL << INFINIPATH_XGXS_MDIOADDR_SHIFT; + change = 1; + } + if (val & INFINIPATH_XGXS_RESET) { + /* normally true after boot */ + val &= ~INFINIPATH_XGXS_RESET; + change = 1; + } + if (change) + ipath_write_kreg(dd, dd->ipath_kregs->kr_xgxsconfig, val); + + val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesconfig0); + + /* clear current and de-emphasis bits */ + config1 &= ~0x0ffffffff00ULL; + /* set current to 20ma */ + config1 |= 0x00000000000ULL; + /* set de-emphasis to -5.68dB */ + config1 |= 0x0cccc000000ULL; + ipath_write_kreg(dd, dd->ipath_kregs->kr_serdesconfig1, config1); + + ipath_cdbg(VERBOSE, "After setup: serdes status is config0=%llx " + "config1=%llx, sstatus=%llx xgxs %llx\n", + (unsigned long long) val, (unsigned long long) config1, + (unsigned long long) + ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesstatus), + (unsigned long long) + ipath_read_kreg64(dd, dd->ipath_kregs->kr_xgxsconfig)); + + if (!ipath_waitfor_mdio_cmdready(dd)) { + ipath_write_kreg(dd, dd->ipath_kregs->kr_mdio, + ipath_mdio_req(IPATH_MDIO_CMD_READ, 31, + IPATH_MDIO_CTRL_XGXS_REG_8, + 0)); + if (ipath_waitfor_complete(dd, dd->ipath_kregs->kr_mdio, + IPATH_MDIO_DATAVALID, &val)) + ipath_dbg("Never got MDIO data for XGXS status " + "read\n"); + else + ipath_cdbg(VERBOSE, "MDIO Read reg8, " + "'bank' 31 %x\n", (u32) val); + } else + ipath_dbg("Never got MDIO cmdready for XGXS status read\n"); + + return ret; /* for now, say we always succeeded */ +} + +/** + * ipath_ht_quiet_serdes - set serdes to txidle + * @dd: the infinipath device + * driver is being unloaded + */ +static void ipath_ht_quiet_serdes(struct ipath_devdata *dd) +{ + u64 val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesconfig0); + + val |= INFINIPATH_SERDC0_TXIDLE; + ipath_dbg("Setting TxIdleEn on serdes (config0 = %llx)\n", + (unsigned long long) val); + ipath_write_kreg(dd, dd->ipath_kregs->kr_serdesconfig0, val); +} + +static int ipath_ht_intconfig(struct ipath_devdata *dd) +{ + int ret; + + if (!dd->ipath_intconfig) { + ipath_dev_err(dd, "No interrupts enabled, couldn't setup " + "interrupt address\n"); + ret = 1; + goto bail; + } + + ipath_write_kreg(dd, dd->ipath_kregs->kr_interruptconfig, + dd->ipath_intconfig); /* interrupt address */ + ret = 0; + +bail: + return ret; +} + +/** + * ipath_pe_put_tid - write a TID in chip + * @dd: the infinipath device + * @tidptr: pointer to the expected TID (in chip) to udpate + * @tidtype: 0 for eager, 1 for expected + * @pa: physical address of in memory buffer; ipath_tidinvalid if freeing + * + * This exists as a separate routine to allow for special locking etc. + * It's used for both the full cleanup on exit, as well as the normal + * setup and teardown. + */ +static void ipath_ht_put_tid(struct ipath_devdata *dd, + u64 __iomem *tidptr, u32 type, + unsigned long pa) +{ + if (pa != dd->ipath_tidinvalid) { + if (unlikely((pa & ~INFINIPATH_RT_ADDR_MASK))) { + dev_info(&dd->pcidev->dev, + "physaddr %lx has more than " + "40 bits, using only 40!!!\n", pa); + pa &= INFINIPATH_RT_ADDR_MASK; + } + if (type == 0) + pa |= dd->ipath_tidtemplate; + else { + /* in words (fixed, full page). */ + u64 lenvalid = PAGE_SIZE >> 2; + lenvalid <<= INFINIPATH_RT_BUFSIZE_SHIFT; + pa |= lenvalid | INFINIPATH_RT_VALID; + } + } + if (dd->ipath_kregbase) + writeq(pa, tidptr); +} + +/** + * ipath_ht_clear_tid - clear all TID entries for a port, expected and eager + * @dd: the infinipath device + * @port: the port + * + * Used from ipath_close(), and at chip initialization. + */ +static void ipath_ht_clear_tids(struct ipath_devdata *dd, unsigned port) +{ + u64 __iomem *tidbase; + int i; + + if (!dd->ipath_kregbase) + return; + + ipath_cdbg(VERBOSE, "Invalidate TIDs for port %u\n", port); + + /* + * need to invalidate all of the expected TID entries for this + * port, so we don't have valid entries that might somehow get + * used (early in next use of this port, or through some bug) + */ + tidbase = (u64 __iomem *) ((char __iomem *)(dd->ipath_kregbase) + + dd->ipath_rcvtidbase + + port * dd->ipath_rcvtidcnt * + sizeof(*tidbase)); + for (i = 0; i < dd->ipath_rcvtidcnt; i++) + ipath_ht_put_tid(dd, &tidbase[i], 1, dd->ipath_tidinvalid); + + tidbase = (u64 __iomem *) ((char __iomem *)(dd->ipath_kregbase) + + dd->ipath_rcvegrbase + + port * dd->ipath_rcvegrcnt * + sizeof(*tidbase)); + + for (i = 0; i < dd->ipath_rcvegrcnt; i++) + ipath_ht_put_tid(dd, &tidbase[i], 0, dd->ipath_tidinvalid); +} + +/** + * ipath_ht_tidtemplate - setup constants for TID updates + * @dd: the infinipath device + * + * We setup stuff that we use a lot, to avoid calculating each time + */ +static void ipath_ht_tidtemplate(struct ipath_devdata *dd) +{ + dd->ipath_tidtemplate = dd->ipath_ibmaxlen >> 2; + dd->ipath_tidtemplate <<= INFINIPATH_RT_BUFSIZE_SHIFT; + dd->ipath_tidtemplate |= INFINIPATH_RT_VALID; + + /* + * work around chip errata bug 7358, by marking invalid tids + * as having max length + */ + dd->ipath_tidinvalid = (-1LL & INFINIPATH_RT_BUFSIZE_MASK) << + INFINIPATH_RT_BUFSIZE_SHIFT; +} + +static int ipath_ht_early_init(struct ipath_devdata *dd) +{ + u32 __iomem *piobuf; + u32 pioincr, val32, egrsize; + int i; + + /* + * one cache line; long IB headers will spill over into received + * buffer + */ + dd->ipath_rcvhdrentsize = 16; + dd->ipath_rcvhdrsize = IPATH_DFLT_RCVHDRSIZE; + + /* + * For HT-400, we allocate a somewhat overly large eager buffer, + * such that we can guarantee that we can receive the largest + * packet that we can send out. To truly support a 4KB MTU, + * we need to bump this to a large value. To date, other than + * testing, we have never encountered an HCA that can really + * send 4KB MTU packets, so we do not handle that (we'll get + * errors interrupts if we ever see one). + */ + dd->ipath_rcvegrbufsize = dd->ipath_piosize2k; + egrsize = dd->ipath_rcvegrbufsize; + + /* + * the min() check here is currently a nop, but it may not + * always be, depending on just how we do ipath_rcvegrbufsize + */ + dd->ipath_ibmaxlen = min(dd->ipath_piosize2k, + dd->ipath_rcvegrbufsize); + dd->ipath_init_ibmaxlen = dd->ipath_ibmaxlen; + ipath_ht_tidtemplate(dd); + + /* + * zero all the TID entries at startup. We do this for sanity, + * in case of a previous driver crash of some kind, and also + * because the chip powers up with these memories in an unknown + * state. Use portcnt, not cfgports, since this is for the + * full chip, not for current (possibly different) configuration + * value. + * Chip Errata bug 6447 + */ + for (val32 = 0; val32 < dd->ipath_portcnt; val32++) + ipath_ht_clear_tids(dd, val32); + + /* + * write the pbc of each buffer, to be sure it's initialized, then + * cancel all the buffers, and also abort any packets that might + * have been in flight for some reason (the latter is for driver + * unload/reload, but isn't a bad idea at first init). PIO send + * isn't enabled at this point, so there is no danger of sending + * these out on the wire. + * Chip Errata bug 6610 + */ + piobuf = (u32 __iomem *) (((char __iomem *)(dd->ipath_kregbase)) + + dd->ipath_piobufbase); + pioincr = dd->ipath_palign / sizeof(*piobuf); + for (i = 0; i < dd->ipath_piobcnt2k; i++) { + /* + * reasonable word count, just to init pbc + */ + writel(16, piobuf); + piobuf += pioincr; + } + /* + * self-clearing + */ + ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, + INFINIPATH_S_ABORT); + return 0; +} + +/** + * ipath_init_ht_get_base_info - set chip-specific flags for user code + * @dd: the infinipath device + * @kbase: ipath_base_info pointer + * + * We set the PCIE flag because the lower bandwidth on PCIe vs + * HyperTransport can affect some user packet algorithims. + */ +static int ipath_ht_get_base_info(struct ipath_portdata *pd, void *kbase) +{ + struct ipath_base_info *kinfo = kbase; + + kinfo->spi_runtime_flags |= IPATH_RUNTIME_HT | + IPATH_RUNTIME_RCVHDR_COPY; + + return 0; +} + +/** + * ipath_init_ht400_funcs - set up the chip-specific function pointers + * @dd: the infinipath device + * + * This is global, and is called directly at init to set up the + * chip-specific function pointers for later use. + */ +void ipath_init_ht400_funcs(struct ipath_devdata *dd) +{ + dd->ipath_f_intrsetup = ipath_ht_intconfig; + dd->ipath_f_bus = ipath_setup_ht_config; + dd->ipath_f_reset = ipath_setup_ht_reset; + dd->ipath_f_get_boardname = ipath_ht_boardname; + dd->ipath_f_init_hwerrors = ipath_ht_init_hwerrors; + dd->ipath_f_init_hwerrors = ipath_ht_init_hwerrors; + dd->ipath_f_early_init = ipath_ht_early_init; + dd->ipath_f_handle_hwerrors = ipath_ht_handle_hwerrors; + dd->ipath_f_quiet_serdes = ipath_ht_quiet_serdes; + dd->ipath_f_bringup_serdes = ipath_ht_bringup_serdes; + dd->ipath_f_clear_tids = ipath_ht_clear_tids; + dd->ipath_f_put_tid = ipath_ht_put_tid; + dd->ipath_f_cleanup = ipath_setup_ht_cleanup; + dd->ipath_f_setextled = ipath_setup_ht_setextled; + dd->ipath_f_get_base_info = ipath_ht_get_base_info; + + /* + * initialize chip-specific variables + */ + dd->ipath_f_tidtemplate = ipath_ht_tidtemplate; + + /* + * setup the register offsets, since they are different for each + * chip + */ + dd->ipath_kregs = &ipath_ht_kregs; + dd->ipath_cregs = &ipath_ht_cregs; + + /* + * do very early init that is needed before ipath_f_bus is + * called + */ + ipath_init_ht_variables(); +} diff --git a/drivers/infiniband/hw/ipath/ipath_init_chip.c b/drivers/infiniband/hw/ipath/ipath_init_chip.c new file mode 100644 index 000000000000..2823ff9c0c62 --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_init_chip.c @@ -0,0 +1,951 @@ +/* + * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + +#include "ipath_kernel.h" +#include "ips_common.h" + +/* + * min buffers we want to have per port, after driver + */ +#define IPATH_MIN_USER_PORT_BUFCNT 8 + +/* + * Number of ports we are configured to use (to allow for more pio + * buffers per port, etc.) Zero means use chip value. + */ +static ushort ipath_cfgports; + +module_param_named(cfgports, ipath_cfgports, ushort, S_IRUGO); +MODULE_PARM_DESC(cfgports, "Set max number of ports to use"); + +/* + * Number of buffers reserved for driver (layered drivers and SMA + * send). Reserved at end of buffer list. + */ +static ushort ipath_kpiobufs = 32; + +static int ipath_set_kpiobufs(const char *val, struct kernel_param *kp); + +module_param_call(kpiobufs, ipath_set_kpiobufs, param_get_uint, + &ipath_kpiobufs, S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(kpiobufs, "Set number of PIO buffers for driver"); + +/** + * create_port0_egr - allocate the eager TID buffers + * @dd: the infinipath device + * + * This code is now quite different for user and kernel, because + * the kernel uses skb's, for the accelerated network performance. + * This is the kernel (port0) version. + * + * Allocate the eager TID buffers and program them into infinipath. + * We use the network layer alloc_skb() allocator to allocate the + * memory, and either use the buffers as is for things like SMA + * packets, or pass the buffers up to the ipath layered driver and + * thence the network layer, replacing them as we do so (see + * ipath_rcv_layer()). + */ +static int create_port0_egr(struct ipath_devdata *dd) +{ + unsigned e, egrcnt; + struct sk_buff **skbs; + int ret; + + egrcnt = dd->ipath_rcvegrcnt; + + skbs = vmalloc(sizeof(*dd->ipath_port0_skbs) * egrcnt); + if (skbs == NULL) { + ipath_dev_err(dd, "allocation error for eager TID " + "skb array\n"); + ret = -ENOMEM; + goto bail; + } + for (e = 0; e < egrcnt; e++) { + /* + * This is a bit tricky in that we allocate extra + * space for 2 bytes of the 14 byte ethernet header. + * These two bytes are passed in the ipath header so + * the rest of the data is word aligned. We allocate + * 4 bytes so that the data buffer stays word aligned. + * See ipath_kreceive() for more details. + */ + skbs[e] = ipath_alloc_skb(dd, GFP_KERNEL); + if (!skbs[e]) { + ipath_dev_err(dd, "SKB allocation error for " + "eager TID %u\n", e); + while (e != 0) + dev_kfree_skb(skbs[--e]); + ret = -ENOMEM; + goto bail; + } + } + /* + * After loop above, so we can test non-NULL to see if ready + * to use at receive, etc. + */ + dd->ipath_port0_skbs = skbs; + + for (e = 0; e < egrcnt; e++) { + unsigned long phys = + virt_to_phys(dd->ipath_port0_skbs[e]->data); + dd->ipath_f_put_tid(dd, e + (u64 __iomem *) + ((char __iomem *) dd->ipath_kregbase + + dd->ipath_rcvegrbase), 0, phys); + } + + ret = 0; + +bail: + return ret; +} + +static int bringup_link(struct ipath_devdata *dd) +{ + u64 val, ibc; + int ret = 0; + + /* hold IBC in reset */ + dd->ipath_control &= ~INFINIPATH_C_LINKENABLE; + ipath_write_kreg(dd, dd->ipath_kregs->kr_control, + dd->ipath_control); + + /* + * Note that prior to try 14 or 15 of IB, the credit scaling + * wasn't working, because it was swapped for writes with the + * 1 bit default linkstate field + */ + + /* ignore pbc and align word */ + val = dd->ipath_piosize2k - 2 * sizeof(u32); + /* + * for ICRC, which we only send in diag test pkt mode, and we + * don't need to worry about that for mtu + */ + val += 1; + /* + * Set the IBC maxpktlength to the size of our pio buffers the + * maxpktlength is in words. This is *not* the IB data MTU. + */ + ibc = (val / sizeof(u32)) << INFINIPATH_IBCC_MAXPKTLEN_SHIFT; + /* in KB */ + ibc |= 0x5ULL << INFINIPATH_IBCC_FLOWCTRLWATERMARK_SHIFT; + /* + * How often flowctrl sent. More or less in usecs; balance against + * watermark value, so that in theory senders always get a flow + * control update in time to not let the IB link go idle. + */ + ibc |= 0x3ULL << INFINIPATH_IBCC_FLOWCTRLPERIOD_SHIFT; + /* max error tolerance */ + ibc |= 0xfULL << INFINIPATH_IBCC_PHYERRTHRESHOLD_SHIFT; + /* use "real" buffer space for */ + ibc |= 4ULL << INFINIPATH_IBCC_CREDITSCALE_SHIFT; + /* IB credit flow control. */ + ibc |= 0xfULL << INFINIPATH_IBCC_OVERRUNTHRESHOLD_SHIFT; + /* initially come up waiting for TS1, without sending anything. */ + dd->ipath_ibcctrl = ibc; + /* + * Want to start out with both LINKCMD and LINKINITCMD in NOP + * (0 and 0). Don't put linkinitcmd in ipath_ibcctrl, want that + * to stay a NOP + */ + ibc |= INFINIPATH_IBCC_LINKINITCMD_DISABLE << + INFINIPATH_IBCC_LINKINITCMD_SHIFT; + ipath_cdbg(VERBOSE, "Writing 0x%llx to ibcctrl\n", + (unsigned long long) ibc); + ipath_write_kreg(dd, dd->ipath_kregs->kr_ibcctrl, ibc); + + // be sure chip saw it + val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch); + + ret = dd->ipath_f_bringup_serdes(dd); + + if (ret) + dev_info(&dd->pcidev->dev, "Could not initialize SerDes, " + "not usable\n"); + else { + /* enable IBC */ + dd->ipath_control |= INFINIPATH_C_LINKENABLE; + ipath_write_kreg(dd, dd->ipath_kregs->kr_control, + dd->ipath_control); + } + + return ret; +} + +static int init_chip_first(struct ipath_devdata *dd, + struct ipath_portdata **pdp) +{ + struct ipath_portdata *pd = NULL; + int ret = 0; + u64 val; + + /* + * skip cfgports stuff because we are not allocating memory, + * and we don't want problems if the portcnt changed due to + * cfgports. We do still check and report a difference, if + * not same (should be impossible). + */ + dd->ipath_portcnt = + ipath_read_kreg32(dd, dd->ipath_kregs->kr_portcnt); + if (!ipath_cfgports) + dd->ipath_cfgports = dd->ipath_portcnt; + else if (ipath_cfgports <= dd->ipath_portcnt) { + dd->ipath_cfgports = ipath_cfgports; + ipath_dbg("Configured to use %u ports out of %u in chip\n", + dd->ipath_cfgports, dd->ipath_portcnt); + } else { + dd->ipath_cfgports = dd->ipath_portcnt; + ipath_dbg("Tried to configured to use %u ports; chip " + "only supports %u\n", ipath_cfgports, + dd->ipath_portcnt); + } + dd->ipath_pd = kzalloc(sizeof(*dd->ipath_pd) * dd->ipath_cfgports, + GFP_KERNEL); + + if (!dd->ipath_pd) { + ipath_dev_err(dd, "Unable to allocate portdata array, " + "failing\n"); + ret = -ENOMEM; + goto done; + } + + dd->ipath_lastegrheads = kzalloc(sizeof(*dd->ipath_lastegrheads) + * dd->ipath_cfgports, + GFP_KERNEL); + dd->ipath_lastrcvhdrqtails = + kzalloc(sizeof(*dd->ipath_lastrcvhdrqtails) + * dd->ipath_cfgports, GFP_KERNEL); + + if (!dd->ipath_lastegrheads || !dd->ipath_lastrcvhdrqtails) { + ipath_dev_err(dd, "Unable to allocate head arrays, " + "failing\n"); + ret = -ENOMEM; + goto done; + } + + dd->ipath_pd[0] = kzalloc(sizeof(*pd), GFP_KERNEL); + + if (!dd->ipath_pd[0]) { + ipath_dev_err(dd, "Unable to allocate portdata for port " + "0, failing\n"); + ret = -ENOMEM; + goto done; + } + pd = dd->ipath_pd[0]; + pd->port_dd = dd; + pd->port_port = 0; + pd->port_cnt = 1; + /* The port 0 pkey table is used by the layer interface. */ + pd->port_pkeys[0] = IPS_DEFAULT_P_KEY; + dd->ipath_rcvtidcnt = + ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvtidcnt); + dd->ipath_rcvtidbase = + ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvtidbase); + dd->ipath_rcvegrcnt = + ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvegrcnt); + dd->ipath_rcvegrbase = + ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvegrbase); + dd->ipath_palign = + ipath_read_kreg32(dd, dd->ipath_kregs->kr_pagealign); + dd->ipath_piobufbase = + ipath_read_kreg64(dd, dd->ipath_kregs->kr_sendpiobufbase); + val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_sendpiosize); + dd->ipath_piosize2k = val & ~0U; + dd->ipath_piosize4k = val >> 32; + dd->ipath_ibmtu = 4096; /* default to largest legal MTU */ + val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_sendpiobufcnt); + dd->ipath_piobcnt2k = val & ~0U; + dd->ipath_piobcnt4k = val >> 32; + dd->ipath_pio2kbase = + (u32 __iomem *) (((char __iomem *) dd->ipath_kregbase) + + (dd->ipath_piobufbase & 0xffffffff)); + if (dd->ipath_piobcnt4k) { + dd->ipath_pio4kbase = (u32 __iomem *) + (((char __iomem *) dd->ipath_kregbase) + + (dd->ipath_piobufbase >> 32)); + /* + * 4K buffers take 2 pages; we use roundup just to be + * paranoid; we calculate it once here, rather than on + * ever buf allocate + */ + dd->ipath_4kalign = ALIGN(dd->ipath_piosize4k, + dd->ipath_palign); + ipath_dbg("%u 2k(%x) piobufs @ %p, %u 4k(%x) @ %p " + "(%x aligned)\n", + dd->ipath_piobcnt2k, dd->ipath_piosize2k, + dd->ipath_pio2kbase, dd->ipath_piobcnt4k, + dd->ipath_piosize4k, dd->ipath_pio4kbase, + dd->ipath_4kalign); + } + else ipath_dbg("%u 2k piobufs @ %p\n", + dd->ipath_piobcnt2k, dd->ipath_pio2kbase); + + spin_lock_init(&dd->ipath_tid_lock); + +done: + *pdp = pd; + return ret; +} + +/** + * init_chip_reset - re-initialize after a reset, or enable + * @dd: the infinipath device + * @pdp: output for port data + * + * sanity check at least some of the values after reset, and + * ensure no receive or transmit (explictly, in case reset + * failed + */ +static int init_chip_reset(struct ipath_devdata *dd, + struct ipath_portdata **pdp) +{ + struct ipath_portdata *pd; + u32 rtmp; + + *pdp = pd = dd->ipath_pd[0]; + /* ensure chip does no sends or receives while we re-initialize */ + dd->ipath_control = dd->ipath_sendctrl = dd->ipath_rcvctrl = 0U; + ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, 0); + ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, 0); + ipath_write_kreg(dd, dd->ipath_kregs->kr_control, 0); + + rtmp = ipath_read_kreg32(dd, dd->ipath_kregs->kr_portcnt); + if (dd->ipath_portcnt != rtmp) + dev_info(&dd->pcidev->dev, "portcnt was %u before " + "reset, now %u, using original\n", + dd->ipath_portcnt, rtmp); + rtmp = ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvtidcnt); + if (rtmp != dd->ipath_rcvtidcnt) + dev_info(&dd->pcidev->dev, "tidcnt was %u before " + "reset, now %u, using original\n", + dd->ipath_rcvtidcnt, rtmp); + rtmp = ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvtidbase); + if (rtmp != dd->ipath_rcvtidbase) + dev_info(&dd->pcidev->dev, "tidbase was %u before " + "reset, now %u, using original\n", + dd->ipath_rcvtidbase, rtmp); + rtmp = ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvegrcnt); + if (rtmp != dd->ipath_rcvegrcnt) + dev_info(&dd->pcidev->dev, "egrcnt was %u before " + "reset, now %u, using original\n", + dd->ipath_rcvegrcnt, rtmp); + rtmp = ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvegrbase); + if (rtmp != dd->ipath_rcvegrbase) + dev_info(&dd->pcidev->dev, "egrbase was %u before " + "reset, now %u, using original\n", + dd->ipath_rcvegrbase, rtmp); + + return 0; +} + +static int init_pioavailregs(struct ipath_devdata *dd) +{ + int ret; + + dd->ipath_pioavailregs_dma = dma_alloc_coherent( + &dd->pcidev->dev, PAGE_SIZE, &dd->ipath_pioavailregs_phys, + GFP_KERNEL); + if (!dd->ipath_pioavailregs_dma) { + ipath_dev_err(dd, "failed to allocate PIOavail reg area " + "in memory\n"); + ret = -ENOMEM; + goto done; + } + + /* + * we really want L2 cache aligned, but for current CPUs of + * interest, they are the same. + */ + dd->ipath_statusp = (u64 *) + ((char *)dd->ipath_pioavailregs_dma + + ((2 * L1_CACHE_BYTES + + dd->ipath_pioavregs * sizeof(u64)) & ~L1_CACHE_BYTES)); + /* copy the current value now that it's really allocated */ + *dd->ipath_statusp = dd->_ipath_status; + /* + * setup buffer to hold freeze msg, accessible to apps, + * following statusp + */ + dd->ipath_freezemsg = (char *)&dd->ipath_statusp[1]; + /* and its length */ + dd->ipath_freezelen = L1_CACHE_BYTES - sizeof(dd->ipath_statusp[0]); + + if (dd->ipath_unit * 64 > (IPATH_PORT0_RCVHDRTAIL_SIZE - 64)) { + ipath_dev_err(dd, "unit %u too large for port 0 " + "rcvhdrtail buffer size\n", dd->ipath_unit); + ret = -ENODEV; + } + else + ret = 0; + + /* so we can get current tail in ipath_kreceive(), per chip */ + dd->ipath_hdrqtailptr = &ipath_port0_rcvhdrtail[ + dd->ipath_unit * (64 / sizeof(*ipath_port0_rcvhdrtail))]; +done: + return ret; +} + +/** + * init_shadow_tids - allocate the shadow TID array + * @dd: the infinipath device + * + * allocate the shadow TID array, so we can ipath_munlock previous + * entries. It may make more sense to move the pageshadow to the + * port data structure, so we only allocate memory for ports actually + * in use, since we at 8k per port, now. + */ +static void init_shadow_tids(struct ipath_devdata *dd) +{ + dd->ipath_pageshadow = (struct page **) + vmalloc(dd->ipath_cfgports * dd->ipath_rcvtidcnt * + sizeof(struct page *)); + if (!dd->ipath_pageshadow) + ipath_dev_err(dd, "failed to allocate shadow page * " + "array, no expected sends!\n"); + else + memset(dd->ipath_pageshadow, 0, + dd->ipath_cfgports * dd->ipath_rcvtidcnt * + sizeof(struct page *)); +} + +static void enable_chip(struct ipath_devdata *dd, + struct ipath_portdata *pd, int reinit) +{ + u32 val; + int i; + + if (!reinit) { + init_waitqueue_head(&ipath_sma_state_wait); + } + ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, + dd->ipath_rcvctrl); + + /* Enable PIO send, and update of PIOavail regs to memory. */ + dd->ipath_sendctrl = INFINIPATH_S_PIOENABLE | + INFINIPATH_S_PIOBUFAVAILUPD; + ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, + dd->ipath_sendctrl); + + /* + * enable port 0 receive, and receive interrupt. other ports + * done as user opens and inits them. + */ + dd->ipath_rcvctrl = INFINIPATH_R_TAILUPD | + (1ULL << INFINIPATH_R_PORTENABLE_SHIFT) | + (1ULL << INFINIPATH_R_INTRAVAIL_SHIFT); + ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, + dd->ipath_rcvctrl); + + /* + * now ready for use. this should be cleared whenever we + * detect a reset, or initiate one. + */ + dd->ipath_flags |= IPATH_INITTED; + + /* + * init our shadow copies of head from tail values, and write + * head values to match. + */ + val = ipath_read_ureg32(dd, ur_rcvegrindextail, 0); + (void)ipath_write_ureg(dd, ur_rcvegrindexhead, val, 0); + dd->ipath_port0head = ipath_read_ureg32(dd, ur_rcvhdrtail, 0); + + /* Initialize so we interrupt on next packet received */ + (void)ipath_write_ureg(dd, ur_rcvhdrhead, + dd->ipath_rhdrhead_intr_off | + dd->ipath_port0head, 0); + + /* + * by now pioavail updates to memory should have occurred, so + * copy them into our working/shadow registers; this is in + * case something went wrong with abort, but mostly to get the + * initial values of the generation bit correct. + */ + for (i = 0; i < dd->ipath_pioavregs; i++) { + __le64 val; + + /* + * Chip Errata bug 6641; even and odd qwords>3 are swapped. + */ + if (i > 3) { + if (i & 1) + val = dd->ipath_pioavailregs_dma[i - 1]; + else + val = dd->ipath_pioavailregs_dma[i + 1]; + } + else + val = dd->ipath_pioavailregs_dma[i]; + dd->ipath_pioavailshadow[i] = le64_to_cpu(val); + } + /* can get counters, stats, etc. */ + dd->ipath_flags |= IPATH_PRESENT; +} + +static int init_housekeeping(struct ipath_devdata *dd, + struct ipath_portdata **pdp, int reinit) +{ + char boardn[32]; + int ret = 0; + + /* + * have to clear shadow copies of registers at init that are + * not otherwise set here, or all kinds of bizarre things + * happen with driver on chip reset + */ + dd->ipath_rcvhdrsize = 0; + + /* + * Don't clear ipath_flags as 8bit mode was set before + * entering this func. However, we do set the linkstate to + * unknown, so we can watch for a transition. + */ + dd->ipath_flags |= IPATH_LINKUNK; + dd->ipath_flags &= ~(IPATH_LINKACTIVE | IPATH_LINKARMED | + IPATH_LINKDOWN | IPATH_LINKINIT); + + ipath_cdbg(VERBOSE, "Try to read spc chip revision\n"); + dd->ipath_revision = + ipath_read_kreg64(dd, dd->ipath_kregs->kr_revision); + + /* + * set up fundamental info we need to use the chip; we assume + * if the revision reg and these regs are OK, we don't need to + * special case the rest + */ + dd->ipath_sregbase = + ipath_read_kreg32(dd, dd->ipath_kregs->kr_sendregbase); + dd->ipath_cregbase = + ipath_read_kreg32(dd, dd->ipath_kregs->kr_counterregbase); + dd->ipath_uregbase = + ipath_read_kreg32(dd, dd->ipath_kregs->kr_userregbase); + ipath_cdbg(VERBOSE, "ipath_kregbase %p, sendbase %x usrbase %x, " + "cntrbase %x\n", dd->ipath_kregbase, dd->ipath_sregbase, + dd->ipath_uregbase, dd->ipath_cregbase); + if ((dd->ipath_revision & 0xffffffff) == 0xffffffff + || (dd->ipath_sregbase & 0xffffffff) == 0xffffffff + || (dd->ipath_cregbase & 0xffffffff) == 0xffffffff + || (dd->ipath_uregbase & 0xffffffff) == 0xffffffff) { + ipath_dev_err(dd, "Register read failures from chip, " + "giving up initialization\n"); + ret = -ENODEV; + goto done; + } + + /* clear the initial reset flag, in case first driver load */ + ipath_write_kreg(dd, dd->ipath_kregs->kr_errorclear, + INFINIPATH_E_RESET); + + if (reinit) + ret = init_chip_reset(dd, pdp); + else + ret = init_chip_first(dd, pdp); + + if (ret) + goto done; + + ipath_cdbg(VERBOSE, "Revision %llx (PCI %x), %u ports, %u tids, " + "%u egrtids\n", (unsigned long long) dd->ipath_revision, + dd->ipath_pcirev, dd->ipath_portcnt, dd->ipath_rcvtidcnt, + dd->ipath_rcvegrcnt); + + if (((dd->ipath_revision >> INFINIPATH_R_SOFTWARE_SHIFT) & + INFINIPATH_R_SOFTWARE_MASK) != IPATH_CHIP_SWVERSION) { + ipath_dev_err(dd, "Driver only handles version %d, " + "chip swversion is %d (%llx), failng\n", + IPATH_CHIP_SWVERSION, + (int)(dd->ipath_revision >> + INFINIPATH_R_SOFTWARE_SHIFT) & + INFINIPATH_R_SOFTWARE_MASK, + (unsigned long long) dd->ipath_revision); + ret = -ENOSYS; + goto done; + } + dd->ipath_majrev = (u8) ((dd->ipath_revision >> + INFINIPATH_R_CHIPREVMAJOR_SHIFT) & + INFINIPATH_R_CHIPREVMAJOR_MASK); + dd->ipath_minrev = (u8) ((dd->ipath_revision >> + INFINIPATH_R_CHIPREVMINOR_SHIFT) & + INFINIPATH_R_CHIPREVMINOR_MASK); + dd->ipath_boardrev = (u8) ((dd->ipath_revision >> + INFINIPATH_R_BOARDID_SHIFT) & + INFINIPATH_R_BOARDID_MASK); + + ret = dd->ipath_f_get_boardname(dd, boardn, sizeof boardn); + + snprintf(dd->ipath_boardversion, sizeof(dd->ipath_boardversion), + "Driver %u.%u, %s, InfiniPath%u %u.%u, PCI %u, " + "SW Compat %u\n", + IPATH_CHIP_VERS_MAJ, IPATH_CHIP_VERS_MIN, boardn, + (unsigned)(dd->ipath_revision >> INFINIPATH_R_ARCH_SHIFT) & + INFINIPATH_R_ARCH_MASK, + dd->ipath_majrev, dd->ipath_minrev, dd->ipath_pcirev, + (unsigned)(dd->ipath_revision >> + INFINIPATH_R_SOFTWARE_SHIFT) & + INFINIPATH_R_SOFTWARE_MASK); + + ipath_dbg("%s", dd->ipath_boardversion); + +done: + return ret; +} + + +/** + * ipath_init_chip - do the actual initialization sequence on the chip + * @dd: the infinipath device + * @reinit: reinitializing, so don't allocate new memory + * + * Do the actual initialization sequence on the chip. This is done + * both from the init routine called from the PCI infrastructure, and + * when we reset the chip, or detect that it was reset internally, + * or it's administratively re-enabled. + * + * Memory allocation here and in called routines is only done in + * the first case (reinit == 0). We have to be careful, because even + * without memory allocation, we need to re-write all the chip registers + * TIDs, etc. after the reset or enable has completed. + */ +int ipath_init_chip(struct ipath_devdata *dd, int reinit) +{ + int ret = 0, i; + u32 val32, kpiobufs; + u64 val, atmp; + struct ipath_portdata *pd = NULL; /* keep gcc4 happy */ + + ret = init_housekeeping(dd, &pd, reinit); + if (ret) + goto done; + + /* + * we ignore most issues after reporting them, but have to specially + * handle hardware-disabled chips. + */ + if (ret == 2) { + /* unique error, known to ipath_init_one */ + ret = -EPERM; + goto done; + } + + /* + * We could bump this to allow for full rcvegrcnt + rcvtidcnt, + * but then it no longer nicely fits power of two, and since + * we now use routines that backend onto __get_free_pages, the + * rest would be wasted. + */ + dd->ipath_rcvhdrcnt = dd->ipath_rcvegrcnt; + ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvhdrcnt, + dd->ipath_rcvhdrcnt); + + /* + * Set up the shadow copies of the piobufavail registers, + * which we compare against the chip registers for now, and + * the in memory DMA'ed copies of the registers. This has to + * be done early, before we calculate lastport, etc. + */ + val = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k; + /* + * calc number of pioavail registers, and save it; we have 2 + * bits per buffer. + */ + dd->ipath_pioavregs = ALIGN(val, sizeof(u64) * BITS_PER_BYTE / 2) + / (sizeof(u64) * BITS_PER_BYTE / 2); + if (!ipath_kpiobufs) /* have to have at least 1, for SMA */ + kpiobufs = ipath_kpiobufs = 1; + else if ((dd->ipath_piobcnt2k + dd->ipath_piobcnt4k) < + (dd->ipath_cfgports * IPATH_MIN_USER_PORT_BUFCNT)) { + dev_info(&dd->pcidev->dev, "Too few PIO buffers (%u) " + "for %u ports to have %u each!\n", + dd->ipath_piobcnt2k + dd->ipath_piobcnt4k, + dd->ipath_cfgports, IPATH_MIN_USER_PORT_BUFCNT); + kpiobufs = 1; /* reserve just the minimum for SMA/ether */ + } else + kpiobufs = ipath_kpiobufs; + + if (kpiobufs > + (dd->ipath_piobcnt2k + dd->ipath_piobcnt4k - + (dd->ipath_cfgports * IPATH_MIN_USER_PORT_BUFCNT))) { + i = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k - + (dd->ipath_cfgports * IPATH_MIN_USER_PORT_BUFCNT); + if (i < 0) + i = 0; + dev_info(&dd->pcidev->dev, "Allocating %d PIO bufs for " + "kernel leaves too few for %d user ports " + "(%d each); using %u\n", kpiobufs, + dd->ipath_cfgports - 1, + IPATH_MIN_USER_PORT_BUFCNT, i); + /* + * shouldn't change ipath_kpiobufs, because could be + * different for different devices... + */ + kpiobufs = i; + } + dd->ipath_lastport_piobuf = + dd->ipath_piobcnt2k + dd->ipath_piobcnt4k - kpiobufs; + dd->ipath_pbufsport = dd->ipath_cfgports > 1 + ? dd->ipath_lastport_piobuf / (dd->ipath_cfgports - 1) + : 0; + val32 = dd->ipath_lastport_piobuf - + (dd->ipath_pbufsport * (dd->ipath_cfgports - 1)); + if (val32 > 0) { + ipath_dbg("allocating %u pbufs/port leaves %u unused, " + "add to kernel\n", dd->ipath_pbufsport, val32); + dd->ipath_lastport_piobuf -= val32; + ipath_dbg("%u pbufs/port leaves %u unused, add to kernel\n", + dd->ipath_pbufsport, val32); + } + dd->ipath_lastpioindex = dd->ipath_lastport_piobuf; + ipath_cdbg(VERBOSE, "%d PIO bufs for kernel out of %d total %u " + "each for %u user ports\n", kpiobufs, + dd->ipath_piobcnt2k + dd->ipath_piobcnt4k, + dd->ipath_pbufsport, dd->ipath_cfgports - 1); + + dd->ipath_f_early_init(dd); + + /* early_init sets rcvhdrentsize and rcvhdrsize, so this must be + * done after early_init */ + dd->ipath_hdrqlast = + dd->ipath_rcvhdrentsize * (dd->ipath_rcvhdrcnt - 1); + ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvhdrentsize, + dd->ipath_rcvhdrentsize); + ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvhdrsize, + dd->ipath_rcvhdrsize); + + if (!reinit) { + ret = init_pioavailregs(dd); + init_shadow_tids(dd); + if (ret) + goto done; + } + + (void)ipath_write_kreg(dd, dd->ipath_kregs->kr_sendpioavailaddr, + dd->ipath_pioavailregs_phys); + /* + * this is to detect s/w errors, which the h/w works around by + * ignoring the low 6 bits of address, if it wasn't aligned. + */ + val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_sendpioavailaddr); + if (val != dd->ipath_pioavailregs_phys) { + ipath_dev_err(dd, "Catastrophic software error, " + "SendPIOAvailAddr written as %lx, " + "read back as %llx\n", + (unsigned long) dd->ipath_pioavailregs_phys, + (unsigned long long) val); + ret = -EINVAL; + goto done; + } + + val = ipath_port0_rcvhdrtail_dma + dd->ipath_unit * 64; + + /* verify that the alignment requirement was met */ + ipath_write_kreg_port(dd, dd->ipath_kregs->kr_rcvhdrtailaddr, + 0, val); + atmp = ipath_read_kreg64_port( + dd, dd->ipath_kregs->kr_rcvhdrtailaddr, 0); + if (val != atmp) { + ipath_dev_err(dd, "Catastrophic software error, " + "RcvHdrTailAddr0 written as %llx, " + "read back as %llx from %x\n", + (unsigned long long) val, + (unsigned long long) atmp, + dd->ipath_kregs->kr_rcvhdrtailaddr); + ret = -EINVAL; + goto done; + } + + ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvbthqp, IPATH_KD_QP); + + /* + * make sure we are not in freeze, and PIO send enabled, so + * writes to pbc happen + */ + ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrmask, 0ULL); + ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrclear, + ~0ULL&~INFINIPATH_HWE_MEMBISTFAILED); + ipath_write_kreg(dd, dd->ipath_kregs->kr_control, 0ULL); + ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, + INFINIPATH_S_PIOENABLE); + + /* + * before error clears, since we expect serdes pll errors during + * this, the first time after reset + */ + if (bringup_link(dd)) { + dev_info(&dd->pcidev->dev, "Failed to bringup IB link\n"); + ret = -ENETDOWN; + goto done; + } + + /* + * clear any "expected" hwerrs from reset and/or initialization + * clear any that aren't enabled (at least this once), and then + * set the enable mask + */ + dd->ipath_f_init_hwerrors(dd); + ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrclear, + ~0ULL&~INFINIPATH_HWE_MEMBISTFAILED); + ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrmask, + dd->ipath_hwerrmask); + + dd->ipath_maskederrs = dd->ipath_ignorederrs; + /* clear all */ + ipath_write_kreg(dd, dd->ipath_kregs->kr_errorclear, -1LL); + /* enable errors that are masked, at least this first time. */ + ipath_write_kreg(dd, dd->ipath_kregs->kr_errormask, + ~dd->ipath_maskederrs); + /* clear any interrups up to this point (ints still not enabled) */ + ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, -1LL); + + ipath_stats.sps_lid[dd->ipath_unit] = dd->ipath_lid; + + /* + * Set up the port 0 (kernel) rcvhdr q and egr TIDs. If doing + * re-init, the simplest way to handle this is to free + * existing, and re-allocate. + */ + if (reinit) + ipath_free_pddata(dd, 0, 0); + dd->ipath_f_tidtemplate(dd); + ret = ipath_create_rcvhdrq(dd, pd); + if (!ret) + ret = create_port0_egr(dd); + if (ret) + ipath_dev_err(dd, "failed to allocate port 0 (kernel) " + "rcvhdrq and/or egr bufs\n"); + else + enable_chip(dd, pd, reinit); + + /* + * cause retrigger of pending interrupts ignored during init, + * even if we had errors + */ + ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, 0ULL); + + if(!dd->ipath_stats_timer_active) { + /* + * first init, or after an admin disable/enable + * set up stats retrieval timer, even if we had errors + * in last portion of setup + */ + init_timer(&dd->ipath_stats_timer); + dd->ipath_stats_timer.function = ipath_get_faststats; + dd->ipath_stats_timer.data = (unsigned long) dd; + /* every 5 seconds; */ + dd->ipath_stats_timer.expires = jiffies + 5 * HZ; + /* takes ~16 seconds to overflow at full IB 4x bandwdith */ + add_timer(&dd->ipath_stats_timer); + dd->ipath_stats_timer_active = 1; + } + +done: + if (!ret) { + ipath_get_guid(dd); + *dd->ipath_statusp |= IPATH_STATUS_CHIP_PRESENT; + if (!dd->ipath_f_intrsetup(dd)) { + /* now we can enable all interrupts from the chip */ + ipath_write_kreg(dd, dd->ipath_kregs->kr_intmask, + -1LL); + /* force re-interrupt of any pending interrupts. */ + ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, + 0ULL); + /* chip is usable; mark it as initialized */ + *dd->ipath_statusp |= IPATH_STATUS_INITTED; + } else + ipath_dev_err(dd, "No interrupts enabled, couldn't " + "setup interrupt address\n"); + + if (dd->ipath_cfgports > ipath_stats.sps_nports) + /* + * sps_nports is a global, so, we set it to + * the highest number of ports of any of the + * chips we find; we never decrement it, at + * least for now. Since this might have changed + * over disable/enable or prior to reset, always + * do the check and potentially adjust. + */ + ipath_stats.sps_nports = dd->ipath_cfgports; + } else + ipath_dbg("Failed (%d) to initialize chip\n", ret); + + /* if ret is non-zero, we probably should do some cleanup + here... */ + return ret; +} + +static int ipath_set_kpiobufs(const char *str, struct kernel_param *kp) +{ + struct ipath_devdata *dd; + unsigned long flags; + unsigned short val; + int ret; + + ret = ipath_parse_ushort(str, &val); + + spin_lock_irqsave(&ipath_devs_lock, flags); + + if (ret < 0) + goto bail; + + if (val == 0) { + ret = -EINVAL; + goto bail; + } + + list_for_each_entry(dd, &ipath_dev_list, ipath_list) { + if (dd->ipath_kregbase) + continue; + if (val > (dd->ipath_piobcnt2k + dd->ipath_piobcnt4k - + (dd->ipath_cfgports * + IPATH_MIN_USER_PORT_BUFCNT))) + { + ipath_dev_err( + dd, + "Allocating %d PIO bufs for kernel leaves " + "too few for %d user ports (%d each)\n", + val, dd->ipath_cfgports - 1, + IPATH_MIN_USER_PORT_BUFCNT); + ret = -EINVAL; + goto bail; + } + dd->ipath_lastport_piobuf = + dd->ipath_piobcnt2k + dd->ipath_piobcnt4k - val; + } + + ret = 0; +bail: + spin_unlock_irqrestore(&ipath_devs_lock, flags); + + return ret; +} diff --git a/drivers/infiniband/hw/ipath/ipath_intr.c b/drivers/infiniband/hw/ipath/ipath_intr.c new file mode 100644 index 000000000000..60f5f4108069 --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_intr.c @@ -0,0 +1,841 @@ +/* + * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include "ipath_kernel.h" +#include "ips_common.h" +#include "ipath_layer.h" + +#define E_SUM_PKTERRS \ + (INFINIPATH_E_RHDRLEN | INFINIPATH_E_RBADTID | \ + INFINIPATH_E_RBADVERSION | INFINIPATH_E_RHDR | \ + INFINIPATH_E_RLONGPKTLEN | INFINIPATH_E_RSHORTPKTLEN | \ + INFINIPATH_E_RMAXPKTLEN | INFINIPATH_E_RMINPKTLEN | \ + INFINIPATH_E_RFORMATERR | INFINIPATH_E_RUNSUPVL | \ + INFINIPATH_E_RUNEXPCHAR | INFINIPATH_E_REBP) + +#define E_SUM_ERRS \ + (INFINIPATH_E_SPIOARMLAUNCH | INFINIPATH_E_SUNEXPERRPKTNUM | \ + INFINIPATH_E_SDROPPEDDATAPKT | INFINIPATH_E_SDROPPEDSMPPKT | \ + INFINIPATH_E_SMAXPKTLEN | INFINIPATH_E_SUNSUPVL | \ + INFINIPATH_E_SMINPKTLEN | INFINIPATH_E_SPKTLEN | \ + INFINIPATH_E_INVALIDADDR) + +static u64 handle_e_sum_errs(struct ipath_devdata *dd, ipath_err_t errs) +{ + unsigned long sbuf[4]; + u64 ignore_this_time = 0; + u32 piobcnt; + + /* if possible that sendbuffererror could be valid */ + piobcnt = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k; + /* read these before writing errorclear */ + sbuf[0] = ipath_read_kreg64( + dd, dd->ipath_kregs->kr_sendbuffererror); + sbuf[1] = ipath_read_kreg64( + dd, dd->ipath_kregs->kr_sendbuffererror + 1); + if (piobcnt > 128) { + sbuf[2] = ipath_read_kreg64( + dd, dd->ipath_kregs->kr_sendbuffererror + 2); + sbuf[3] = ipath_read_kreg64( + dd, dd->ipath_kregs->kr_sendbuffererror + 3); + } + + if (sbuf[0] || sbuf[1] || (piobcnt > 128 && (sbuf[2] || sbuf[3]))) { + int i; + + ipath_cdbg(PKT, "SendbufErrs %lx %lx ", sbuf[0], sbuf[1]); + if (ipath_debug & __IPATH_PKTDBG && piobcnt > 128) + printk("%lx %lx ", sbuf[2], sbuf[3]); + for (i = 0; i < piobcnt; i++) { + if (test_bit(i, sbuf)) { + u32 __iomem *piobuf; + if (i < dd->ipath_piobcnt2k) + piobuf = (u32 __iomem *) + (dd->ipath_pio2kbase + + i * dd->ipath_palign); + else + piobuf = (u32 __iomem *) + (dd->ipath_pio4kbase + + (i - dd->ipath_piobcnt2k) * + dd->ipath_4kalign); + + ipath_cdbg(PKT, + "PIObuf[%u] @%p pbc is %x; ", + i, piobuf, readl(piobuf)); + + ipath_disarm_piobufs(dd, i, 1); + } + } + if (ipath_debug & __IPATH_PKTDBG) + printk("\n"); + } + if ((errs & (INFINIPATH_E_SDROPPEDDATAPKT | + INFINIPATH_E_SDROPPEDSMPPKT | + INFINIPATH_E_SMINPKTLEN)) && + !(dd->ipath_flags & IPATH_LINKACTIVE)) { + /* + * This can happen when SMA is trying to bring the link + * up, but the IB link changes state at the "wrong" time. + * The IB logic then complains that the packet isn't + * valid. We don't want to confuse people, so we just + * don't print them, except at debug + */ + ipath_dbg("Ignoring pktsend errors %llx, because not " + "yet active\n", (unsigned long long) errs); + ignore_this_time = INFINIPATH_E_SDROPPEDDATAPKT | + INFINIPATH_E_SDROPPEDSMPPKT | + INFINIPATH_E_SMINPKTLEN; + } + + return ignore_this_time; +} + +/* return the strings for the most common link states */ +static char *ib_linkstate(u32 linkstate) +{ + char *ret; + + switch (linkstate) { + case IPATH_IBSTATE_INIT: + ret = "Init"; + break; + case IPATH_IBSTATE_ARM: + ret = "Arm"; + break; + case IPATH_IBSTATE_ACTIVE: + ret = "Active"; + break; + default: + ret = "Down"; + } + + return ret; +} + +static void handle_e_ibstatuschanged(struct ipath_devdata *dd, + ipath_err_t errs, int noprint) +{ + u64 val; + u32 ltstate, lstate; + + /* + * even if diags are enabled, we want to notice LINKINIT, etc. + * We just don't want to change the LED state, or + * dd->ipath_kregs->kr_ibcctrl + */ + val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_ibcstatus); + lstate = val & IPATH_IBSTATE_MASK; + if (lstate == IPATH_IBSTATE_INIT || lstate == IPATH_IBSTATE_ARM || + lstate == IPATH_IBSTATE_ACTIVE) { + /* + * only print at SMA if there is a change, debug if not + * (sometimes we want to know that, usually not). + */ + if (lstate == ((unsigned) dd->ipath_lastibcstat + & IPATH_IBSTATE_MASK)) { + ipath_dbg("Status change intr but no change (%s)\n", + ib_linkstate(lstate)); + } + else + ipath_cdbg(SMA, "Unit %u link state %s, last " + "was %s\n", dd->ipath_unit, + ib_linkstate(lstate), + ib_linkstate((unsigned) + dd->ipath_lastibcstat + & IPATH_IBSTATE_MASK)); + } + else { + lstate = dd->ipath_lastibcstat & IPATH_IBSTATE_MASK; + if (lstate == IPATH_IBSTATE_INIT || + lstate == IPATH_IBSTATE_ARM || + lstate == IPATH_IBSTATE_ACTIVE) + ipath_cdbg(SMA, "Unit %u link state down" + " (state 0x%x), from %s\n", + dd->ipath_unit, + (u32)val & IPATH_IBSTATE_MASK, + ib_linkstate(lstate)); + else + ipath_cdbg(VERBOSE, "Unit %u link state changed " + "to 0x%x from down (%x)\n", + dd->ipath_unit, (u32) val, lstate); + } + ltstate = (val >> INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT) & + INFINIPATH_IBCS_LINKTRAININGSTATE_MASK; + lstate = (val >> INFINIPATH_IBCS_LINKSTATE_SHIFT) & + INFINIPATH_IBCS_LINKSTATE_MASK; + + if (ltstate == INFINIPATH_IBCS_LT_STATE_POLLACTIVE || + ltstate == INFINIPATH_IBCS_LT_STATE_POLLQUIET) { + u32 last_ltstate; + + /* + * Ignore cycling back and forth from Polling.Active + * to Polling.Quiet while waiting for the other end of + * the link to come up. We will cycle back and forth + * between them if no cable is plugged in, + * the other device is powered off or disabled, etc. + */ + last_ltstate = (dd->ipath_lastibcstat >> + INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT) + & INFINIPATH_IBCS_LINKTRAININGSTATE_MASK; + if (last_ltstate == INFINIPATH_IBCS_LT_STATE_POLLACTIVE + || last_ltstate == + INFINIPATH_IBCS_LT_STATE_POLLQUIET) { + if (dd->ipath_ibpollcnt > 40) { + dd->ipath_flags |= IPATH_NOCABLE; + *dd->ipath_statusp |= + IPATH_STATUS_IB_NOCABLE; + } else + dd->ipath_ibpollcnt++; + goto skip_ibchange; + } + } + dd->ipath_ibpollcnt = 0; /* some state other than 2 or 3 */ + ipath_stats.sps_iblink++; + if (ltstate != INFINIPATH_IBCS_LT_STATE_LINKUP) { + dd->ipath_flags |= IPATH_LINKDOWN; + dd->ipath_flags &= ~(IPATH_LINKUNK | IPATH_LINKINIT + | IPATH_LINKACTIVE | + IPATH_LINKARMED); + *dd->ipath_statusp &= ~IPATH_STATUS_IB_READY; + if (!noprint) { + if (((dd->ipath_lastibcstat >> + INFINIPATH_IBCS_LINKSTATE_SHIFT) & + INFINIPATH_IBCS_LINKSTATE_MASK) + == INFINIPATH_IBCS_L_STATE_ACTIVE) + /* if from up to down be more vocal */ + ipath_cdbg(SMA, + "Unit %u link now down (%s)\n", + dd->ipath_unit, + ipath_ibcstatus_str[ltstate]); + else + ipath_cdbg(VERBOSE, "Unit %u link is " + "down (%s)\n", dd->ipath_unit, + ipath_ibcstatus_str[ltstate]); + } + + dd->ipath_f_setextled(dd, lstate, ltstate); + } else if ((val & IPATH_IBSTATE_MASK) == IPATH_IBSTATE_ACTIVE) { + dd->ipath_flags |= IPATH_LINKACTIVE; + dd->ipath_flags &= + ~(IPATH_LINKUNK | IPATH_LINKINIT | IPATH_LINKDOWN | + IPATH_LINKARMED | IPATH_NOCABLE); + *dd->ipath_statusp &= ~IPATH_STATUS_IB_NOCABLE; + *dd->ipath_statusp |= + IPATH_STATUS_IB_READY | IPATH_STATUS_IB_CONF; + dd->ipath_f_setextled(dd, lstate, ltstate); + + __ipath_layer_intr(dd, IPATH_LAYER_INT_IF_UP); + } else if ((val & IPATH_IBSTATE_MASK) == IPATH_IBSTATE_INIT) { + /* + * set INIT and DOWN. Down is checked by most of the other + * code, but INIT is useful to know in a few places. + */ + dd->ipath_flags |= IPATH_LINKINIT | IPATH_LINKDOWN; + dd->ipath_flags &= + ~(IPATH_LINKUNK | IPATH_LINKACTIVE | IPATH_LINKARMED + | IPATH_NOCABLE); + *dd->ipath_statusp &= ~(IPATH_STATUS_IB_NOCABLE + | IPATH_STATUS_IB_READY); + dd->ipath_f_setextled(dd, lstate, ltstate); + } else if ((val & IPATH_IBSTATE_MASK) == IPATH_IBSTATE_ARM) { + dd->ipath_flags |= IPATH_LINKARMED; + dd->ipath_flags &= + ~(IPATH_LINKUNK | IPATH_LINKDOWN | IPATH_LINKINIT | + IPATH_LINKACTIVE | IPATH_NOCABLE); + *dd->ipath_statusp &= ~(IPATH_STATUS_IB_NOCABLE + | IPATH_STATUS_IB_READY); + dd->ipath_f_setextled(dd, lstate, ltstate); + } else { + if (!noprint) + ipath_dbg("IBstatuschange unit %u: %s (%x)\n", + dd->ipath_unit, + ipath_ibcstatus_str[ltstate], ltstate); + } +skip_ibchange: + dd->ipath_lastibcstat = val; +} + +static void handle_supp_msgs(struct ipath_devdata *dd, + unsigned supp_msgs, char msg[512]) +{ + /* + * Print the message unless it's ibc status change only, which + * happens so often we never want to count it. + */ + if (dd->ipath_lasterror & ~INFINIPATH_E_IBSTATUSCHANGED) { + ipath_decode_err(msg, sizeof msg, dd->ipath_lasterror & + ~INFINIPATH_E_IBSTATUSCHANGED); + if (dd->ipath_lasterror & + ~(INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL)) + ipath_dev_err(dd, "Suppressed %u messages for " + "fast-repeating errors (%s) (%llx)\n", + supp_msgs, msg, + (unsigned long long) + dd->ipath_lasterror); + else { + /* + * rcvegrfull and rcvhdrqfull are "normal", for some + * types of processes (mostly benchmarks) that send + * huge numbers of messages, while not processing + * them. So only complain about these at debug + * level. + */ + ipath_dbg("Suppressed %u messages for %s\n", + supp_msgs, msg); + } + } +} + +static unsigned handle_frequent_errors(struct ipath_devdata *dd, + ipath_err_t errs, char msg[512], + int *noprint) +{ + unsigned long nc; + static unsigned long nextmsg_time; + static unsigned nmsgs, supp_msgs; + + /* + * Throttle back "fast" messages to no more than 10 per 5 seconds. + * This isn't perfect, but it's a reasonable heuristic. If we get + * more than 10, give a 6x longer delay. + */ + nc = jiffies; + if (nmsgs > 10) { + if (time_before(nc, nextmsg_time)) { + *noprint = 1; + if (!supp_msgs++) + nextmsg_time = nc + HZ * 3; + } + else if (supp_msgs) { + handle_supp_msgs(dd, supp_msgs, msg); + supp_msgs = 0; + nmsgs = 0; + } + } + else if (!nmsgs++ || time_after(nc, nextmsg_time)) + nextmsg_time = nc + HZ / 2; + + return supp_msgs; +} + +static void handle_errors(struct ipath_devdata *dd, ipath_err_t errs) +{ + char msg[512]; + u64 ignore_this_time = 0; + int i; + int chkerrpkts = 0, noprint = 0; + unsigned supp_msgs; + + supp_msgs = handle_frequent_errors(dd, errs, msg, &noprint); + + /* + * don't report errors that are masked (includes those always + * ignored) + */ + errs &= ~dd->ipath_maskederrs; + + /* do these first, they are most important */ + if (errs & INFINIPATH_E_HARDWARE) { + /* reuse same msg buf */ + dd->ipath_f_handle_hwerrors(dd, msg, sizeof msg); + } + + if (!noprint && (errs & ~infinipath_e_bitsextant)) + ipath_dev_err(dd, "error interrupt with unknown errors " + "%llx set\n", (unsigned long long) + (errs & ~infinipath_e_bitsextant)); + + if (errs & E_SUM_ERRS) + ignore_this_time = handle_e_sum_errs(dd, errs); + + if (supp_msgs == 250000) { + /* + * It's not entirely reasonable assuming that the errors set + * in the last clear period are all responsible for the + * problem, but the alternative is to assume it's the only + * ones on this particular interrupt, which also isn't great + */ + dd->ipath_maskederrs |= dd->ipath_lasterror | errs; + ipath_write_kreg(dd, dd->ipath_kregs->kr_errormask, + ~dd->ipath_maskederrs); + ipath_decode_err(msg, sizeof msg, + (dd->ipath_maskederrs & ~dd-> + ipath_ignorederrs)); + + if ((dd->ipath_maskederrs & ~dd->ipath_ignorederrs) & + ~(INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL)) + ipath_dev_err(dd, "Disabling error(s) %llx because " + "occuring too frequently (%s)\n", + (unsigned long long) + (dd->ipath_maskederrs & + ~dd->ipath_ignorederrs), msg); + else { + /* + * rcvegrfull and rcvhdrqfull are "normal", + * for some types of processes (mostly benchmarks) + * that send huge numbers of messages, while not + * processing them. So only complain about + * these at debug level. + */ + ipath_dbg("Disabling frequent queue full errors " + "(%s)\n", msg); + } + + /* + * Re-enable the masked errors after around 3 minutes. in + * ipath_get_faststats(). If we have a series of fast + * repeating but different errors, the interval will keep + * stretching out, but that's OK, as that's pretty + * catastrophic. + */ + dd->ipath_unmasktime = jiffies + HZ * 180; + } + + ipath_write_kreg(dd, dd->ipath_kregs->kr_errorclear, errs); + if (ignore_this_time) + errs &= ~ignore_this_time; + if (errs & ~dd->ipath_lasterror) { + errs &= ~dd->ipath_lasterror; + /* never suppress duplicate hwerrors or ibstatuschange */ + dd->ipath_lasterror |= errs & + ~(INFINIPATH_E_HARDWARE | + INFINIPATH_E_IBSTATUSCHANGED); + } + if (!errs) + return; + + if (!noprint) + /* + * the ones we mask off are handled specially below or above + */ + ipath_decode_err(msg, sizeof msg, + errs & ~(INFINIPATH_E_IBSTATUSCHANGED | + INFINIPATH_E_RRCVEGRFULL | + INFINIPATH_E_RRCVHDRFULL | + INFINIPATH_E_HARDWARE)); + else + /* so we don't need if (!noprint) at strlcat's below */ + *msg = 0; + + if (errs & E_SUM_PKTERRS) { + ipath_stats.sps_pkterrs++; + chkerrpkts = 1; + } + if (errs & E_SUM_ERRS) + ipath_stats.sps_errs++; + + if (errs & (INFINIPATH_E_RICRC | INFINIPATH_E_RVCRC)) { + ipath_stats.sps_crcerrs++; + chkerrpkts = 1; + } + + /* + * We don't want to print these two as they happen, or we can make + * the situation even worse, because it takes so long to print + * messages to serial consoles. Kernel ports get printed from + * fast_stats, no more than every 5 seconds, user ports get printed + * on close + */ + if (errs & INFINIPATH_E_RRCVHDRFULL) { + int any; + u32 hd, tl; + ipath_stats.sps_hdrqfull++; + for (any = i = 0; i < dd->ipath_cfgports; i++) { + struct ipath_portdata *pd = dd->ipath_pd[i]; + if (i == 0) { + hd = dd->ipath_port0head; + tl = (u32) le64_to_cpu( + *dd->ipath_hdrqtailptr); + } else if (pd && pd->port_cnt && + pd->port_rcvhdrtail_kvaddr) { + /* + * don't report same point multiple times, + * except kernel + */ + tl = (u32) * pd->port_rcvhdrtail_kvaddr; + if (tl == dd->ipath_lastrcvhdrqtails[i]) + continue; + hd = ipath_read_ureg32(dd, ur_rcvhdrhead, + i); + } else + continue; + if (hd == (tl + 1) || + (!hd && tl == dd->ipath_hdrqlast)) { + dd->ipath_lastrcvhdrqtails[i] = tl; + pd->port_hdrqfull++; + if (i == 0) + chkerrpkts = 1; + } + } + } + if (errs & INFINIPATH_E_RRCVEGRFULL) { + /* + * since this is of less importance and not likely to + * happen without also getting hdrfull, only count + * occurrences; don't check each port (or even the kernel + * vs user) + */ + ipath_stats.sps_etidfull++; + if (dd->ipath_port0head != + (u32) le64_to_cpu(*dd->ipath_hdrqtailptr)) + chkerrpkts = 1; + } + + /* + * do this before IBSTATUSCHANGED, in case both bits set in a single + * interrupt; we want the STATUSCHANGE to "win", so we do our + * internal copy of state machine correctly + */ + if (errs & INFINIPATH_E_RIBLOSTLINK) { + /* + * force through block below + */ + errs |= INFINIPATH_E_IBSTATUSCHANGED; + ipath_stats.sps_iblink++; + dd->ipath_flags |= IPATH_LINKDOWN; + dd->ipath_flags &= ~(IPATH_LINKUNK | IPATH_LINKINIT + | IPATH_LINKARMED | IPATH_LINKACTIVE); + *dd->ipath_statusp &= ~IPATH_STATUS_IB_READY; + if (!noprint) { + u64 st = ipath_read_kreg64( + dd, dd->ipath_kregs->kr_ibcstatus); + + ipath_dbg("Lost link, link now down (%s)\n", + ipath_ibcstatus_str[st & 0xf]); + } + } + if (errs & INFINIPATH_E_IBSTATUSCHANGED) + handle_e_ibstatuschanged(dd, errs, noprint); + + if (errs & INFINIPATH_E_RESET) { + if (!noprint) + ipath_dev_err(dd, "Got reset, requires re-init " + "(unload and reload driver)\n"); + dd->ipath_flags &= ~IPATH_INITTED; /* needs re-init */ + /* mark as having had error */ + *dd->ipath_statusp |= IPATH_STATUS_HWERROR; + *dd->ipath_statusp &= ~IPATH_STATUS_IB_CONF; + } + + if (!noprint && *msg) + ipath_dev_err(dd, "%s error\n", msg); + if (dd->ipath_sma_state_wanted & dd->ipath_flags) { + ipath_cdbg(VERBOSE, "sma wanted state %x, iflags now %x, " + "waking\n", dd->ipath_sma_state_wanted, + dd->ipath_flags); + wake_up_interruptible(&ipath_sma_state_wait); + } + + if (chkerrpkts) + /* process possible error packets in hdrq */ + ipath_kreceive(dd); +} + +/* this is separate to allow for better optimization of ipath_intr() */ + +static void ipath_bad_intr(struct ipath_devdata *dd, u32 * unexpectp) +{ + /* + * sometimes happen during driver init and unload, don't want + * to process any interrupts at that point + */ + + /* this is just a bandaid, not a fix, if something goes badly + * wrong */ + if (++*unexpectp > 100) { + if (++*unexpectp > 105) { + /* + * ok, we must be taking somebody else's interrupts, + * due to a messed up mptable and/or PIRQ table, so + * unregister the interrupt. We've seen this during + * linuxbios development work, and it may happen in + * the future again. + */ + if (dd->pcidev && dd->pcidev->irq) { + ipath_dev_err(dd, "Now %u unexpected " + "interrupts, unregistering " + "interrupt handler\n", + *unexpectp); + ipath_dbg("free_irq of irq %x\n", + dd->pcidev->irq); + free_irq(dd->pcidev->irq, dd); + } + } + if (ipath_read_kreg32(dd, dd->ipath_kregs->kr_intmask)) { + ipath_dev_err(dd, "%u unexpected interrupts, " + "disabling interrupts completely\n", + *unexpectp); + /* + * disable all interrupts, something is very wrong + */ + ipath_write_kreg(dd, dd->ipath_kregs->kr_intmask, + 0ULL); + } + } else if (*unexpectp > 1) + ipath_dbg("Interrupt when not ready, should not happen, " + "ignoring\n"); +} + +static void ipath_bad_regread(struct ipath_devdata *dd) +{ + static int allbits; + + /* separate routine, for better optimization of ipath_intr() */ + + /* + * We print the message and disable interrupts, in hope of + * having a better chance of debugging the problem. + */ + ipath_dev_err(dd, + "Read of interrupt status failed (all bits set)\n"); + if (allbits++) { + /* disable all interrupts, something is very wrong */ + ipath_write_kreg(dd, dd->ipath_kregs->kr_intmask, 0ULL); + if (allbits == 2) { + ipath_dev_err(dd, "Still bad interrupt status, " + "unregistering interrupt\n"); + free_irq(dd->pcidev->irq, dd); + } else if (allbits > 2) { + if ((allbits % 10000) == 0) + printk("."); + } else + ipath_dev_err(dd, "Disabling interrupts, " + "multiple errors\n"); + } +} + +static void handle_port_pioavail(struct ipath_devdata *dd) +{ + u32 i; + /* + * start from port 1, since for now port 0 is never using + * wait_event for PIO + */ + for (i = 1; dd->ipath_portpiowait && i < dd->ipath_cfgports; i++) { + struct ipath_portdata *pd = dd->ipath_pd[i]; + + if (pd && pd->port_cnt && + dd->ipath_portpiowait & (1U << i)) { + clear_bit(i, &dd->ipath_portpiowait); + if (test_bit(IPATH_PORT_WAITING_PIO, + &pd->port_flag)) { + clear_bit(IPATH_PORT_WAITING_PIO, + &pd->port_flag); + wake_up_interruptible(&pd->port_wait); + } + } + } +} + +static void handle_layer_pioavail(struct ipath_devdata *dd) +{ + int ret; + + ret = __ipath_layer_intr(dd, IPATH_LAYER_INT_SEND_CONTINUE); + if (ret > 0) + goto clear; + + ret = __ipath_verbs_piobufavail(dd); + if (ret > 0) + goto clear; + + return; +clear: + set_bit(IPATH_S_PIOINTBUFAVAIL, &dd->ipath_sendctrl); + ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, + dd->ipath_sendctrl); +} + +static void handle_rcv(struct ipath_devdata *dd, u32 istat) +{ + u64 portr; + int i; + int rcvdint = 0; + + portr = ((istat >> INFINIPATH_I_RCVAVAIL_SHIFT) & + infinipath_i_rcvavail_mask) + | ((istat >> INFINIPATH_I_RCVURG_SHIFT) & + infinipath_i_rcvurg_mask); + for (i = 0; i < dd->ipath_cfgports; i++) { + struct ipath_portdata *pd = dd->ipath_pd[i]; + if (portr & (1 << i) && pd && + pd->port_cnt) { + if (i == 0) + ipath_kreceive(dd); + else if (test_bit(IPATH_PORT_WAITING_RCV, + &pd->port_flag)) { + int rcbit; + clear_bit(IPATH_PORT_WAITING_RCV, + &pd->port_flag); + rcbit = i + INFINIPATH_R_INTRAVAIL_SHIFT; + clear_bit(1UL << rcbit, &dd->ipath_rcvctrl); + wake_up_interruptible(&pd->port_wait); + rcvdint = 1; + } + } + } + if (rcvdint) { + /* only want to take one interrupt, so turn off the rcv + * interrupt for all the ports that we did the wakeup on + * (but never for kernel port) + */ + ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, + dd->ipath_rcvctrl); + } +} + +irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs) +{ + struct ipath_devdata *dd = data; + u32 istat = ipath_read_kreg32(dd, dd->ipath_kregs->kr_intstatus); + ipath_err_t estat = 0; + static unsigned unexpected = 0; + irqreturn_t ret; + + if (unlikely(!istat)) { + ipath_stats.sps_nullintr++; + ret = IRQ_NONE; /* not our interrupt, or already handled */ + goto bail; + } + if (unlikely(istat == -1)) { + ipath_bad_regread(dd); + /* don't know if it was our interrupt or not */ + ret = IRQ_NONE; + goto bail; + } + + ipath_stats.sps_ints++; + + /* + * this needs to be flags&initted, not statusp, so we keep + * taking interrupts even after link goes down, etc. + * Also, we *must* clear the interrupt at some point, or we won't + * take it again, which can be real bad for errors, etc... + */ + + if (!(dd->ipath_flags & IPATH_INITTED)) { + ipath_bad_intr(dd, &unexpected); + ret = IRQ_NONE; + goto bail; + } + if (unexpected) + unexpected = 0; + + ipath_cdbg(VERBOSE, "intr stat=0x%x\n", istat); + + if (istat & ~infinipath_i_bitsextant) + ipath_dev_err(dd, + "interrupt with unknown interrupts %x set\n", + istat & (u32) ~ infinipath_i_bitsextant); + + if (istat & INFINIPATH_I_ERROR) { + ipath_stats.sps_errints++; + estat = ipath_read_kreg64(dd, + dd->ipath_kregs->kr_errorstatus); + if (!estat) + dev_info(&dd->pcidev->dev, "error interrupt (%x), " + "but no error bits set!\n", istat); + else if (estat == -1LL) + /* + * should we try clearing all, or hope next read + * works? + */ + ipath_dev_err(dd, "Read of error status failed " + "(all bits set); ignoring\n"); + else + handle_errors(dd, estat); + } + + if (istat & INFINIPATH_I_GPIO) { + if (unlikely(!(dd->ipath_flags & IPATH_GPIO_INTR))) { + u32 gpiostatus; + gpiostatus = ipath_read_kreg32( + dd, dd->ipath_kregs->kr_gpio_status); + ipath_dbg("Unexpected GPIO interrupt bits %x\n", + gpiostatus); + ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear, + gpiostatus); + } + else { + /* Clear GPIO status bit 2 */ + ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear, + (u64) (1 << 2)); + + /* + * Packets are available in the port 0 rcv queue. + * Eventually this needs to be generalized to check + * IPATH_GPIO_INTR, and the specific GPIO bit, if + * GPIO interrupts are used for anything else. + */ + ipath_kreceive(dd); + } + } + + /* + * clear the ones we will deal with on this round + * We clear it early, mostly for receive interrupts, so we + * know the chip will have seen this by the time we process + * the queue, and will re-interrupt if necessary. The processor + * itself won't take the interrupt again until we return. + */ + ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, istat); + + if (istat & INFINIPATH_I_SPIOBUFAVAIL) { + clear_bit(IPATH_S_PIOINTBUFAVAIL, &dd->ipath_sendctrl); + ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, + dd->ipath_sendctrl); + + if (dd->ipath_portpiowait) + handle_port_pioavail(dd); + + handle_layer_pioavail(dd); + } + + /* + * we check for both transition from empty to non-empty, and urgent + * packets (those with the interrupt bit set in the header) + */ + + if (istat & ((infinipath_i_rcvavail_mask << + INFINIPATH_I_RCVAVAIL_SHIFT) + | (infinipath_i_rcvurg_mask << + INFINIPATH_I_RCVURG_SHIFT))) + handle_rcv(dd, istat); + + ret = IRQ_HANDLED; + +bail: + return ret; +} diff --git a/drivers/infiniband/hw/ipath/ipath_kernel.h b/drivers/infiniband/hw/ipath/ipath_kernel.h new file mode 100644 index 000000000000..159d0aed31a5 --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_kernel.h @@ -0,0 +1,884 @@ +#ifndef _IPATH_KERNEL_H +#define _IPATH_KERNEL_H +/* + * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * This header file is the base header file for infinipath kernel code + * ipath_user.h serves a similar purpose for user code. + */ + +#include +#include + +#include "ipath_common.h" +#include "ipath_debug.h" +#include "ipath_registers.h" + +/* only s/w major version of InfiniPath we can handle */ +#define IPATH_CHIP_VERS_MAJ 2U + +/* don't care about this except printing */ +#define IPATH_CHIP_VERS_MIN 0U + +/* temporary, maybe always */ +extern struct infinipath_stats ipath_stats; + +#define IPATH_CHIP_SWVERSION IPATH_CHIP_VERS_MAJ + +struct ipath_portdata { + void **port_rcvegrbuf; + dma_addr_t *port_rcvegrbuf_phys; + /* rcvhdrq base, needs mmap before useful */ + void *port_rcvhdrq; + /* kernel virtual address where hdrqtail is updated */ + u64 *port_rcvhdrtail_kvaddr; + /* page * used for uaddr */ + struct page *port_rcvhdrtail_pagep; + /* + * temp buffer for expected send setup, allocated at open, instead + * of each setup call + */ + void *port_tid_pg_list; + /* when waiting for rcv or pioavail */ + wait_queue_head_t port_wait; + /* + * rcvegr bufs base, physical, must fit + * in 44 bits so 32 bit programs mmap64 44 bit works) + */ + dma_addr_t port_rcvegr_phys; + /* mmap of hdrq, must fit in 44 bits */ + dma_addr_t port_rcvhdrq_phys; + /* + * the actual user address that we ipath_mlock'ed, so we can + * ipath_munlock it at close + */ + unsigned long port_rcvhdrtail_uaddr; + /* + * number of opens on this instance (0 or 1; ignoring forks, dup, + * etc. for now) + */ + int port_cnt; + /* + * how much space to leave at start of eager TID entries for + * protocol use, on each TID + */ + /* instead of calculating it */ + unsigned port_port; + /* chip offset of PIO buffers for this port */ + u32 port_piobufs; + /* how many alloc_pages() chunks in port_rcvegrbuf_pages */ + u32 port_rcvegrbuf_chunks; + /* how many egrbufs per chunk */ + u32 port_rcvegrbufs_perchunk; + /* order for port_rcvegrbuf_pages */ + size_t port_rcvegrbuf_size; + /* rcvhdrq size (for freeing) */ + size_t port_rcvhdrq_size; + /* next expected TID to check when looking for free */ + u32 port_tidcursor; + /* next expected TID to check */ + unsigned long port_flag; + /* WAIT_RCV that timed out, no interrupt */ + u32 port_rcvwait_to; + /* WAIT_PIO that timed out, no interrupt */ + u32 port_piowait_to; + /* WAIT_RCV already happened, no wait */ + u32 port_rcvnowait; + /* WAIT_PIO already happened, no wait */ + u32 port_pionowait; + /* total number of rcvhdrqfull errors */ + u32 port_hdrqfull; + /* pid of process using this port */ + pid_t port_pid; + /* same size as task_struct .comm[] */ + char port_comm[16]; + /* pkeys set by this use of this port */ + u16 port_pkeys[4]; + /* so file ops can get at unit */ + struct ipath_devdata *port_dd; +}; + +struct sk_buff; + +/* + * control information for layered drivers + */ +struct _ipath_layer { + void *l_arg; +}; + +/* Verbs layer interface */ +struct _verbs_layer { + void *l_arg; + struct timer_list l_timer; +}; + +struct ipath_devdata { + struct list_head ipath_list; + + struct ipath_kregs const *ipath_kregs; + struct ipath_cregs const *ipath_cregs; + + /* mem-mapped pointer to base of chip regs */ + u64 __iomem *ipath_kregbase; + /* end of mem-mapped chip space; range checking */ + u64 __iomem *ipath_kregend; + /* physical address of chip for io_remap, etc. */ + unsigned long ipath_physaddr; + /* base of memory alloced for ipath_kregbase, for free */ + u64 *ipath_kregalloc; + /* + * version of kregbase that doesn't have high bits set (for 32 bit + * programs, so mmap64 44 bit works) + */ + u64 __iomem *ipath_kregvirt; + /* + * virtual address where port0 rcvhdrqtail updated for this unit. + * only written to by the chip, not the driver. + */ + volatile __le64 *ipath_hdrqtailptr; + dma_addr_t ipath_dma_addr; + /* ipath_cfgports pointers */ + struct ipath_portdata **ipath_pd; + /* sk_buffs used by port 0 eager receive queue */ + struct sk_buff **ipath_port0_skbs; + /* kvirt address of 1st 2k pio buffer */ + void __iomem *ipath_pio2kbase; + /* kvirt address of 1st 4k pio buffer */ + void __iomem *ipath_pio4kbase; + /* + * points to area where PIOavail registers will be DMA'ed. + * Has to be on a page of it's own, because the page will be + * mapped into user program space. This copy is *ONLY* ever + * written by DMA, not by the driver! Need a copy per device + * when we get to multiple devices + */ + volatile __le64 *ipath_pioavailregs_dma; + /* physical address where updates occur */ + dma_addr_t ipath_pioavailregs_phys; + struct _ipath_layer ipath_layer; + /* setup intr */ + int (*ipath_f_intrsetup)(struct ipath_devdata *); + /* setup on-chip bus config */ + int (*ipath_f_bus)(struct ipath_devdata *, struct pci_dev *); + /* hard reset chip */ + int (*ipath_f_reset)(struct ipath_devdata *); + int (*ipath_f_get_boardname)(struct ipath_devdata *, char *, + size_t); + void (*ipath_f_init_hwerrors)(struct ipath_devdata *); + void (*ipath_f_handle_hwerrors)(struct ipath_devdata *, char *, + size_t); + void (*ipath_f_quiet_serdes)(struct ipath_devdata *); + int (*ipath_f_bringup_serdes)(struct ipath_devdata *); + int (*ipath_f_early_init)(struct ipath_devdata *); + void (*ipath_f_clear_tids)(struct ipath_devdata *, unsigned); + void (*ipath_f_put_tid)(struct ipath_devdata *, u64 __iomem*, + u32, unsigned long); + void (*ipath_f_tidtemplate)(struct ipath_devdata *); + void (*ipath_f_cleanup)(struct ipath_devdata *); + void (*ipath_f_setextled)(struct ipath_devdata *, u64, u64); + /* fill out chip-specific fields */ + int (*ipath_f_get_base_info)(struct ipath_portdata *, void *); + struct _verbs_layer verbs_layer; + /* total dwords sent (summed from counter) */ + u64 ipath_sword; + /* total dwords rcvd (summed from counter) */ + u64 ipath_rword; + /* total packets sent (summed from counter) */ + u64 ipath_spkts; + /* total packets rcvd (summed from counter) */ + u64 ipath_rpkts; + /* ipath_statusp initially points to this. */ + u64 _ipath_status; + /* GUID for this interface, in network order */ + __be64 ipath_guid; + /* + * aggregrate of error bits reported since last cleared, for + * limiting of error reporting + */ + ipath_err_t ipath_lasterror; + /* + * aggregrate of error bits reported since last cleared, for + * limiting of hwerror reporting + */ + ipath_err_t ipath_lasthwerror; + /* + * errors masked because they occur too fast, also includes errors + * that are always ignored (ipath_ignorederrs) + */ + ipath_err_t ipath_maskederrs; + /* time in jiffies at which to re-enable maskederrs */ + unsigned long ipath_unmasktime; + /* + * errors always ignored (masked), at least for a given + * chip/device, because they are wrong or not useful + */ + ipath_err_t ipath_ignorederrs; + /* count of egrfull errors, combined for all ports */ + u64 ipath_last_tidfull; + /* for ipath_qcheck() */ + u64 ipath_lastport0rcv_cnt; + /* template for writing TIDs */ + u64 ipath_tidtemplate; + /* value to write to free TIDs */ + u64 ipath_tidinvalid; + /* PE-800 rcv interrupt setup */ + u64 ipath_rhdrhead_intr_off; + + /* size of memory at ipath_kregbase */ + u32 ipath_kregsize; + /* number of registers used for pioavail */ + u32 ipath_pioavregs; + /* IPATH_POLL, etc. */ + u32 ipath_flags; + /* ipath_flags sma is waiting for */ + u32 ipath_sma_state_wanted; + /* last buffer for user use, first buf for kernel use is this + * index. */ + u32 ipath_lastport_piobuf; + /* is a stats timer active */ + u32 ipath_stats_timer_active; + /* dwords sent read from counter */ + u32 ipath_lastsword; + /* dwords received read from counter */ + u32 ipath_lastrword; + /* sent packets read from counter */ + u32 ipath_lastspkts; + /* received packets read from counter */ + u32 ipath_lastrpkts; + /* pio bufs allocated per port */ + u32 ipath_pbufsport; + /* + * number of ports configured as max; zero is set to number chip + * supports, less gives more pio bufs/port, etc. + */ + u32 ipath_cfgports; + /* port0 rcvhdrq head offset */ + u32 ipath_port0head; + /* count of port 0 hdrqfull errors */ + u32 ipath_p0_hdrqfull; + + /* + * (*cfgports) used to suppress multiple instances of same + * port staying stuck at same point + */ + u32 *ipath_lastrcvhdrqtails; + /* + * (*cfgports) used to suppress multiple instances of same + * port staying stuck at same point + */ + u32 *ipath_lastegrheads; + /* + * index of last piobuffer we used. Speeds up searching, by + * starting at this point. Doesn't matter if multiple cpu's use and + * update, last updater is only write that matters. Whenever it + * wraps, we update shadow copies. Need a copy per device when we + * get to multiple devices + */ + u32 ipath_lastpioindex; + /* max length of freezemsg */ + u32 ipath_freezelen; + /* + * consecutive times we wanted a PIO buffer but were unable to + * get one + */ + u32 ipath_consec_nopiobuf; + /* + * hint that we should update ipath_pioavailshadow before + * looking for a PIO buffer + */ + u32 ipath_upd_pio_shadow; + /* so we can rewrite it after a chip reset */ + u32 ipath_pcibar0; + /* so we can rewrite it after a chip reset */ + u32 ipath_pcibar1; + /* sequential tries for SMA send and no bufs */ + u32 ipath_nosma_bufs; + /* duration (seconds) ipath_nosma_bufs set */ + u32 ipath_nosma_secs; + + /* HT/PCI Vendor ID (here for NodeInfo) */ + u16 ipath_vendorid; + /* HT/PCI Device ID (here for NodeInfo) */ + u16 ipath_deviceid; + /* offset in HT config space of slave/primary interface block */ + u8 ipath_ht_slave_off; + /* for write combining settings */ + unsigned long ipath_wc_cookie; + /* ref count for each pkey */ + atomic_t ipath_pkeyrefs[4]; + /* shadow copy of all exptids physaddr; used only by funcsim */ + u64 *ipath_tidsimshadow; + /* shadow copy of struct page *'s for exp tid pages */ + struct page **ipath_pageshadow; + /* lock to workaround chip bug 9437 */ + spinlock_t ipath_tid_lock; + + /* + * IPATH_STATUS_*, + * this address is mapped readonly into user processes so they can + * get status cheaply, whenever they want. + */ + u64 *ipath_statusp; + /* freeze msg if hw error put chip in freeze */ + char *ipath_freezemsg; + /* pci access data structure */ + struct pci_dev *pcidev; + struct cdev *cdev; + struct class_device *class_dev; + /* timer used to prevent stats overflow, error throttling, etc. */ + struct timer_list ipath_stats_timer; + /* check for stale messages in rcv queue */ + /* only allow one intr at a time. */ + unsigned long ipath_rcv_pending; + + /* + * Shadow copies of registers; size indicates read access size. + * Most of them are readonly, but some are write-only register, + * where we manipulate the bits in the shadow copy, and then write + * the shadow copy to infinipath. + * + * We deliberately make most of these 32 bits, since they have + * restricted range. For any that we read, we won't to generate 32 + * bit accesses, since Opteron will generate 2 separate 32 bit HT + * transactions for a 64 bit read, and we want to avoid unnecessary + * HT transactions. + */ + + /* This is the 64 bit group */ + + /* + * shadow of pioavail, check to be sure it's large enough at + * init time. + */ + unsigned long ipath_pioavailshadow[8]; + /* shadow of kr_gpio_out, for rmw ops */ + u64 ipath_gpio_out; + /* kr_revision shadow */ + u64 ipath_revision; + /* + * shadow of ibcctrl, for interrupt handling of link changes, + * etc. + */ + u64 ipath_ibcctrl; + /* + * last ibcstatus, to suppress "duplicate" status change messages, + * mostly from 2 to 3 + */ + u64 ipath_lastibcstat; + /* hwerrmask shadow */ + ipath_err_t ipath_hwerrmask; + /* interrupt config reg shadow */ + u64 ipath_intconfig; + /* kr_sendpiobufbase value */ + u64 ipath_piobufbase; + + /* these are the "32 bit" regs */ + + /* + * number of GUIDs in the flash for this interface; may need some + * rethinking for setting on other ifaces + */ + u32 ipath_nguid; + /* + * the following two are 32-bit bitmasks, but {test,clear,set}_bit + * all expect bit fields to be "unsigned long" + */ + /* shadow kr_rcvctrl */ + unsigned long ipath_rcvctrl; + /* shadow kr_sendctrl */ + unsigned long ipath_sendctrl; + + /* value we put in kr_rcvhdrcnt */ + u32 ipath_rcvhdrcnt; + /* value we put in kr_rcvhdrsize */ + u32 ipath_rcvhdrsize; + /* value we put in kr_rcvhdrentsize */ + u32 ipath_rcvhdrentsize; + /* offset of last entry in rcvhdrq */ + u32 ipath_hdrqlast; + /* kr_portcnt value */ + u32 ipath_portcnt; + /* kr_pagealign value */ + u32 ipath_palign; + /* number of "2KB" PIO buffers */ + u32 ipath_piobcnt2k; + /* size in bytes of "2KB" PIO buffers */ + u32 ipath_piosize2k; + /* number of "4KB" PIO buffers */ + u32 ipath_piobcnt4k; + /* size in bytes of "4KB" PIO buffers */ + u32 ipath_piosize4k; + /* kr_rcvegrbase value */ + u32 ipath_rcvegrbase; + /* kr_rcvegrcnt value */ + u32 ipath_rcvegrcnt; + /* kr_rcvtidbase value */ + u32 ipath_rcvtidbase; + /* kr_rcvtidcnt value */ + u32 ipath_rcvtidcnt; + /* kr_sendregbase */ + u32 ipath_sregbase; + /* kr_userregbase */ + u32 ipath_uregbase; + /* kr_counterregbase */ + u32 ipath_cregbase; + /* shadow the control register contents */ + u32 ipath_control; + /* shadow the gpio output contents */ + u32 ipath_extctrl; + /* PCI revision register (HTC rev on FPGA) */ + u32 ipath_pcirev; + + /* chip address space used by 4k pio buffers */ + u32 ipath_4kalign; + /* The MTU programmed for this unit */ + u32 ipath_ibmtu; + /* + * The max size IB packet, included IB headers that we can send. + * Starts same as ipath_piosize, but is affected when ibmtu is + * changed, or by size of eager buffers + */ + u32 ipath_ibmaxlen; + /* + * ibmaxlen at init time, limited by chip and by receive buffer + * size. Not changed after init. + */ + u32 ipath_init_ibmaxlen; + /* size of each rcvegrbuffer */ + u32 ipath_rcvegrbufsize; + /* width (2,4,8,16,32) from HT config reg */ + u32 ipath_htwidth; + /* HT speed (200,400,800,1000) from HT config */ + u32 ipath_htspeed; + /* ports waiting for PIOavail intr */ + unsigned long ipath_portpiowait; + /* + * number of sequential ibcstatus change for polling active/quiet + * (i.e., link not coming up). + */ + u32 ipath_ibpollcnt; + /* low and high portions of MSI capability/vector */ + u32 ipath_msi_lo; + /* saved after PCIe init for restore after reset */ + u32 ipath_msi_hi; + /* MSI data (vector) saved for restore */ + u16 ipath_msi_data; + /* MLID programmed for this instance */ + u16 ipath_mlid; + /* LID programmed for this instance */ + u16 ipath_lid; + /* list of pkeys programmed; 0 if not set */ + u16 ipath_pkeys[4]; + /* ASCII serial number, from flash */ + u8 ipath_serial[12]; + /* human readable board version */ + u8 ipath_boardversion[80]; + /* chip major rev, from ipath_revision */ + u8 ipath_majrev; + /* chip minor rev, from ipath_revision */ + u8 ipath_minrev; + /* board rev, from ipath_revision */ + u8 ipath_boardrev; + /* unit # of this chip, if present */ + int ipath_unit; + /* saved for restore after reset */ + u8 ipath_pci_cacheline; + /* LID mask control */ + u8 ipath_lmc; +}; + +extern volatile __le64 *ipath_port0_rcvhdrtail; +extern dma_addr_t ipath_port0_rcvhdrtail_dma; + +#define IPATH_PORT0_RCVHDRTAIL_SIZE PAGE_SIZE + +extern struct list_head ipath_dev_list; +extern spinlock_t ipath_devs_lock; +extern struct ipath_devdata *ipath_lookup(int unit); + +extern u16 ipath_layer_rcv_opcode; +extern int ipath_verbs_registered; +extern int __ipath_layer_intr(struct ipath_devdata *, u32); +extern int ipath_layer_intr(struct ipath_devdata *, u32); +extern int __ipath_layer_rcv(struct ipath_devdata *, void *, + struct sk_buff *); +extern int __ipath_layer_rcv_lid(struct ipath_devdata *, void *); +extern int __ipath_verbs_piobufavail(struct ipath_devdata *); +extern int __ipath_verbs_rcv(struct ipath_devdata *, void *, void *, u32); + +void ipath_layer_add(struct ipath_devdata *); +void ipath_layer_del(struct ipath_devdata *); + +int ipath_init_chip(struct ipath_devdata *, int); +int ipath_enable_wc(struct ipath_devdata *dd); +void ipath_disable_wc(struct ipath_devdata *dd); +int ipath_count_units(int *npresentp, int *nupp, u32 *maxportsp); +void ipath_shutdown_device(struct ipath_devdata *); + +struct file_operations; +int ipath_cdev_init(int minor, char *name, struct file_operations *fops, + struct cdev **cdevp, struct class_device **class_devp); +void ipath_cdev_cleanup(struct cdev **cdevp, + struct class_device **class_devp); + +int ipath_diag_init(void); +void ipath_diag_cleanup(void); +void ipath_diag_bringup_link(struct ipath_devdata *); + +extern wait_queue_head_t ipath_sma_state_wait; + +int ipath_user_add(struct ipath_devdata *dd); +void ipath_user_del(struct ipath_devdata *dd); + +struct sk_buff *ipath_alloc_skb(struct ipath_devdata *dd, gfp_t); + +extern int ipath_diag_inuse; + +irqreturn_t ipath_intr(int irq, void *devid, struct pt_regs *regs); +void ipath_decode_err(char *buf, size_t blen, ipath_err_t err); +#if __IPATH_INFO || __IPATH_DBG +extern const char *ipath_ibcstatus_str[]; +#endif + +/* clean up any per-chip chip-specific stuff */ +void ipath_chip_cleanup(struct ipath_devdata *); +/* clean up any chip type-specific stuff */ +void ipath_chip_done(void); + +/* check to see if we have to force ordering for write combining */ +int ipath_unordered_wc(void); + +void ipath_disarm_piobufs(struct ipath_devdata *, unsigned first, + unsigned cnt); + +int ipath_create_rcvhdrq(struct ipath_devdata *, struct ipath_portdata *); +void ipath_free_pddata(struct ipath_devdata *, u32, int); + +int ipath_parse_ushort(const char *str, unsigned short *valp); + +int ipath_wait_linkstate(struct ipath_devdata *, u32, int); +void ipath_set_ib_lstate(struct ipath_devdata *, int); +void ipath_kreceive(struct ipath_devdata *); +int ipath_setrcvhdrsize(struct ipath_devdata *, unsigned); +int ipath_reset_device(int); +void ipath_get_faststats(unsigned long); + +/* for use in system calls, where we want to know device type, etc. */ +#define port_fp(fp) ((struct ipath_portdata *) (fp)->private_data) + +/* + * values for ipath_flags + */ +/* The chip is up and initted */ +#define IPATH_INITTED 0x2 + /* set if any user code has set kr_rcvhdrsize */ +#define IPATH_RCVHDRSZ_SET 0x4 + /* The chip is present and valid for accesses */ +#define IPATH_PRESENT 0x8 + /* HT link0 is only 8 bits wide, ignore upper byte crc + * errors, etc. */ +#define IPATH_8BIT_IN_HT0 0x10 + /* HT link1 is only 8 bits wide, ignore upper byte crc + * errors, etc. */ +#define IPATH_8BIT_IN_HT1 0x20 + /* The link is down */ +#define IPATH_LINKDOWN 0x40 + /* The link level is up (0x11) */ +#define IPATH_LINKINIT 0x80 + /* The link is in the armed (0x21) state */ +#define IPATH_LINKARMED 0x100 + /* The link is in the active (0x31) state */ +#define IPATH_LINKACTIVE 0x200 + /* link current state is unknown */ +#define IPATH_LINKUNK 0x400 + /* no IB cable, or no device on IB cable */ +#define IPATH_NOCABLE 0x4000 + /* Supports port zero per packet receive interrupts via + * GPIO */ +#define IPATH_GPIO_INTR 0x8000 + /* uses the coded 4byte TID, not 8 byte */ +#define IPATH_4BYTE_TID 0x10000 + /* packet/word counters are 32 bit, else those 4 counters + * are 64bit */ +#define IPATH_32BITCOUNTERS 0x20000 + /* can miss port0 rx interrupts */ +#define IPATH_POLL_RX_INTR 0x40000 +#define IPATH_DISABLED 0x80000 /* administratively disabled */ + +/* portdata flag bit offsets */ + /* waiting for a packet to arrive */ +#define IPATH_PORT_WAITING_RCV 2 + /* waiting for a PIO buffer to be available */ +#define IPATH_PORT_WAITING_PIO 3 + +/* free up any allocated data at closes */ +void ipath_free_data(struct ipath_portdata *dd); +int ipath_waitfor_mdio_cmdready(struct ipath_devdata *); +int ipath_waitfor_complete(struct ipath_devdata *, ipath_kreg, u64, u64 *); +u32 __iomem *ipath_getpiobuf(struct ipath_devdata *, u32 *); +/* init PE-800-specific func */ +void ipath_init_pe800_funcs(struct ipath_devdata *); +/* init HT-400-specific func */ +void ipath_init_ht400_funcs(struct ipath_devdata *); +void ipath_get_guid(struct ipath_devdata *); +u64 ipath_snap_cntr(struct ipath_devdata *, ipath_creg); + +/* + * number of words used for protocol header if not set by ipath_userinit(); + */ +#define IPATH_DFLT_RCVHDRSIZE 9 + +#define IPATH_MDIO_CMD_WRITE 1 +#define IPATH_MDIO_CMD_READ 2 +#define IPATH_MDIO_CLD_DIV 25 /* to get 2.5 Mhz mdio clock */ +#define IPATH_MDIO_CMDVALID 0x40000000 /* bit 30 */ +#define IPATH_MDIO_DATAVALID 0x80000000 /* bit 31 */ +#define IPATH_MDIO_CTRL_STD 0x0 + +static inline u64 ipath_mdio_req(int cmd, int dev, int reg, int data) +{ + return (((u64) IPATH_MDIO_CLD_DIV) << 32) | + (cmd << 26) | + (dev << 21) | + (reg << 16) | + (data & 0xFFFF); +} + + /* signal and fifo status, in bank 31 */ +#define IPATH_MDIO_CTRL_XGXS_REG_8 0x8 + /* controls loopback, redundancy */ +#define IPATH_MDIO_CTRL_8355_REG_1 0x10 + /* premph, encdec, etc. */ +#define IPATH_MDIO_CTRL_8355_REG_2 0x11 + /* Kchars, etc. */ +#define IPATH_MDIO_CTRL_8355_REG_6 0x15 +#define IPATH_MDIO_CTRL_8355_REG_9 0x18 +#define IPATH_MDIO_CTRL_8355_REG_10 0x1D + +int ipath_get_user_pages(unsigned long, size_t, struct page **); +int ipath_get_user_pages_nocopy(unsigned long, struct page **); +void ipath_release_user_pages(struct page **, size_t); +void ipath_release_user_pages_on_close(struct page **, size_t); +int ipath_eeprom_read(struct ipath_devdata *, u8, void *, int); +int ipath_eeprom_write(struct ipath_devdata *, u8, const void *, int); + +/* these are used for the registers that vary with port */ +void ipath_write_kreg_port(const struct ipath_devdata *, ipath_kreg, + unsigned, u64); +u64 ipath_read_kreg64_port(const struct ipath_devdata *, ipath_kreg, + unsigned); + +/* + * We could have a single register get/put routine, that takes a group type, + * but this is somewhat clearer and cleaner. It also gives us some error + * checking. 64 bit register reads should always work, but are inefficient + * on opteron (the northbridge always generates 2 separate HT 32 bit reads), + * so we use kreg32 wherever possible. User register and counter register + * reads are always 32 bit reads, so only one form of those routines. + */ + +/* + * At the moment, none of the s-registers are writable, so no + * ipath_write_sreg(), and none of the c-registers are writable, so no + * ipath_write_creg(). + */ + +/** + * ipath_read_ureg32 - read 32-bit virtualized per-port register + * @dd: device + * @regno: register number + * @port: port number + * + * Return the contents of a register that is virtualized to be per port. + * Prints a debug message and returns -1 on errors (not distinguishable from + * valid contents at runtime; we may add a separate error variable at some + * point). + * + * This is normally not used by the kernel, but may be for debugging, and + * has a different implementation than user mode, which is why it's not in + * _common.h. + */ +static inline u32 ipath_read_ureg32(const struct ipath_devdata *dd, + ipath_ureg regno, int port) +{ + if (!dd->ipath_kregbase) + return 0; + + return readl(regno + (u64 __iomem *) + (dd->ipath_uregbase + + (char __iomem *)dd->ipath_kregbase + + dd->ipath_palign * port)); +} + +/** + * ipath_write_ureg - write 32-bit virtualized per-port register + * @dd: device + * @regno: register number + * @value: value + * @port: port + * + * Write the contents of a register that is virtualized to be per port. + */ +static inline void ipath_write_ureg(const struct ipath_devdata *dd, + ipath_ureg regno, u64 value, int port) +{ + u64 __iomem *ubase = (u64 __iomem *) + (dd->ipath_uregbase + (char __iomem *) dd->ipath_kregbase + + dd->ipath_palign * port); + if (dd->ipath_kregbase) + writeq(value, &ubase[regno]); +} + +static inline u32 ipath_read_kreg32(const struct ipath_devdata *dd, + ipath_kreg regno) +{ + if (!dd->ipath_kregbase) + return -1; + return readl((u32 __iomem *) & dd->ipath_kregbase[regno]); +} + +static inline u64 ipath_read_kreg64(const struct ipath_devdata *dd, + ipath_kreg regno) +{ + if (!dd->ipath_kregbase) + return -1; + + return readq(&dd->ipath_kregbase[regno]); +} + +static inline void ipath_write_kreg(const struct ipath_devdata *dd, + ipath_kreg regno, u64 value) +{ + if (dd->ipath_kregbase) + writeq(value, &dd->ipath_kregbase[regno]); +} + +static inline u64 ipath_read_creg(const struct ipath_devdata *dd, + ipath_sreg regno) +{ + if (!dd->ipath_kregbase) + return 0; + + return readq(regno + (u64 __iomem *) + (dd->ipath_cregbase + + (char __iomem *)dd->ipath_kregbase)); +} + +static inline u32 ipath_read_creg32(const struct ipath_devdata *dd, + ipath_sreg regno) +{ + if (!dd->ipath_kregbase) + return 0; + return readl(regno + (u64 __iomem *) + (dd->ipath_cregbase + + (char __iomem *)dd->ipath_kregbase)); +} + +/* + * sysfs interface. + */ + +struct device_driver; + +extern const char ipath_core_version[]; + +int ipath_driver_create_group(struct device_driver *); +void ipath_driver_remove_group(struct device_driver *); + +int ipath_device_create_group(struct device *, struct ipath_devdata *); +void ipath_device_remove_group(struct device *, struct ipath_devdata *); +int ipath_expose_reset(struct device *); + +int ipath_init_ipathfs(void); +void ipath_exit_ipathfs(void); +int ipathfs_add_device(struct ipath_devdata *); +int ipathfs_remove_device(struct ipath_devdata *); + +/* + * Flush write combining store buffers (if present) and perform a write + * barrier. + */ +#if defined(CONFIG_X86_64) +#define ipath_flush_wc() asm volatile("sfence" ::: "memory") +#else +#define ipath_flush_wc() wmb() +#endif + +extern unsigned ipath_debug; /* debugging bit mask */ + +const char *ipath_get_unit_name(int unit); + +extern struct mutex ipath_mutex; + +#define IPATH_DRV_NAME "ipath_core" +#define IPATH_MAJOR 233 +#define IPATH_SMA_MINOR 128 +#define IPATH_DIAG_MINOR 129 +#define IPATH_NMINORS 130 + +#define ipath_dev_err(dd,fmt,...) \ + do { \ + const struct ipath_devdata *__dd = (dd); \ + if (__dd->pcidev) \ + dev_err(&__dd->pcidev->dev, "%s: " fmt, \ + ipath_get_unit_name(__dd->ipath_unit), \ + ##__VA_ARGS__); \ + else \ + printk(KERN_ERR IPATH_DRV_NAME ": %s: " fmt, \ + ipath_get_unit_name(__dd->ipath_unit), \ + ##__VA_ARGS__); \ + } while (0) + +#if _IPATH_DEBUGGING + +# define __IPATH_DBG_WHICH(which,fmt,...) \ + do { \ + if(unlikely(ipath_debug&(which))) \ + printk(KERN_DEBUG IPATH_DRV_NAME ": %s: " fmt, \ + __func__,##__VA_ARGS__); \ + } while(0) + +# define ipath_dbg(fmt,...) \ + __IPATH_DBG_WHICH(__IPATH_DBG,fmt,##__VA_ARGS__) +# define ipath_cdbg(which,fmt,...) \ + __IPATH_DBG_WHICH(__IPATH_##which##DBG,fmt,##__VA_ARGS__) + +#else /* ! _IPATH_DEBUGGING */ + +# define ipath_dbg(fmt,...) +# define ipath_cdbg(which,fmt,...) + +#endif /* _IPATH_DEBUGGING */ + +#endif /* _IPATH_KERNEL_H */ diff --git a/drivers/infiniband/hw/ipath/ipath_keys.c b/drivers/infiniband/hw/ipath/ipath_keys.c new file mode 100644 index 000000000000..aa33b0e9f2f6 --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_keys.c @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include "ipath_verbs.h" + +/** + * ipath_alloc_lkey - allocate an lkey + * @rkt: lkey table in which to allocate the lkey + * @mr: memory region that this lkey protects + * + * Returns 1 if successful, otherwise returns 0. + */ + +int ipath_alloc_lkey(struct ipath_lkey_table *rkt, struct ipath_mregion *mr) +{ + unsigned long flags; + u32 r; + u32 n; + int ret; + + spin_lock_irqsave(&rkt->lock, flags); + + /* Find the next available LKEY */ + r = n = rkt->next; + for (;;) { + if (rkt->table[r] == NULL) + break; + r = (r + 1) & (rkt->max - 1); + if (r == n) { + spin_unlock_irqrestore(&rkt->lock, flags); + _VERBS_INFO("LKEY table full\n"); + ret = 0; + goto bail; + } + } + rkt->next = (r + 1) & (rkt->max - 1); + /* + * Make sure lkey is never zero which is reserved to indicate an + * unrestricted LKEY. + */ + rkt->gen++; + mr->lkey = (r << (32 - ib_ipath_lkey_table_size)) | + ((((1 << (24 - ib_ipath_lkey_table_size)) - 1) & rkt->gen) + << 8); + if (mr->lkey == 0) { + mr->lkey |= 1 << 8; + rkt->gen++; + } + rkt->table[r] = mr; + spin_unlock_irqrestore(&rkt->lock, flags); + + ret = 1; + +bail: + return ret; +} + +/** + * ipath_free_lkey - free an lkey + * @rkt: table from which to free the lkey + * @lkey: lkey id to free + */ +void ipath_free_lkey(struct ipath_lkey_table *rkt, u32 lkey) +{ + unsigned long flags; + u32 r; + + if (lkey == 0) + return; + r = lkey >> (32 - ib_ipath_lkey_table_size); + spin_lock_irqsave(&rkt->lock, flags); + rkt->table[r] = NULL; + spin_unlock_irqrestore(&rkt->lock, flags); +} + +/** + * ipath_lkey_ok - check IB SGE for validity and initialize + * @rkt: table containing lkey to check SGE against + * @isge: outgoing internal SGE + * @sge: SGE to check + * @acc: access flags + * + * Return 1 if valid and successful, otherwise returns 0. + * + * Check the IB SGE for validity and initialize our internal version + * of it. + */ +int ipath_lkey_ok(struct ipath_lkey_table *rkt, struct ipath_sge *isge, + struct ib_sge *sge, int acc) +{ + struct ipath_mregion *mr; + size_t off; + int ret; + + /* + * We use LKEY == zero to mean a physical kmalloc() address. + * This is a bit of a hack since we rely on dma_map_single() + * being reversible by calling bus_to_virt(). + */ + if (sge->lkey == 0) { + isge->mr = NULL; + isge->vaddr = bus_to_virt(sge->addr); + isge->length = sge->length; + isge->sge_length = sge->length; + ret = 1; + goto bail; + } + spin_lock(&rkt->lock); + mr = rkt->table[(sge->lkey >> (32 - ib_ipath_lkey_table_size))]; + spin_unlock(&rkt->lock); + if (unlikely(mr == NULL || mr->lkey != sge->lkey)) { + ret = 0; + goto bail; + } + + off = sge->addr - mr->user_base; + if (unlikely(sge->addr < mr->user_base || + off + sge->length > mr->length || + (mr->access_flags & acc) != acc)) { + ret = 0; + goto bail; + } + + off += mr->offset; + isge->mr = mr; + isge->m = 0; + isge->n = 0; + while (off >= mr->map[isge->m]->segs[isge->n].length) { + off -= mr->map[isge->m]->segs[isge->n].length; + isge->n++; + if (isge->n >= IPATH_SEGSZ) { + isge->m++; + isge->n = 0; + } + } + isge->vaddr = mr->map[isge->m]->segs[isge->n].vaddr + off; + isge->length = mr->map[isge->m]->segs[isge->n].length - off; + isge->sge_length = sge->length; + + ret = 1; + +bail: + return ret; +} + +/** + * ipath_rkey_ok - check the IB virtual address, length, and RKEY + * @dev: infiniband device + * @ss: SGE state + * @len: length of data + * @vaddr: virtual address to place data + * @rkey: rkey to check + * @acc: access flags + * + * Return 1 if successful, otherwise 0. + * + * The QP r_rq.lock should be held. + */ +int ipath_rkey_ok(struct ipath_ibdev *dev, struct ipath_sge_state *ss, + u32 len, u64 vaddr, u32 rkey, int acc) +{ + struct ipath_lkey_table *rkt = &dev->lk_table; + struct ipath_sge *sge = &ss->sge; + struct ipath_mregion *mr; + size_t off; + int ret; + + spin_lock(&rkt->lock); + mr = rkt->table[(rkey >> (32 - ib_ipath_lkey_table_size))]; + spin_unlock(&rkt->lock); + if (unlikely(mr == NULL || mr->lkey != rkey)) { + ret = 0; + goto bail; + } + + off = vaddr - mr->iova; + if (unlikely(vaddr < mr->iova || off + len > mr->length || + (mr->access_flags & acc) == 0)) { + ret = 0; + goto bail; + } + + off += mr->offset; + sge->mr = mr; + sge->m = 0; + sge->n = 0; + while (off >= mr->map[sge->m]->segs[sge->n].length) { + off -= mr->map[sge->m]->segs[sge->n].length; + sge->n++; + if (sge->n >= IPATH_SEGSZ) { + sge->m++; + sge->n = 0; + } + } + sge->vaddr = mr->map[sge->m]->segs[sge->n].vaddr + off; + sge->length = mr->map[sge->m]->segs[sge->n].length - off; + sge->sge_length = len; + ss->sg_list = NULL; + ss->num_sge = 1; + + ret = 1; + +bail: + return ret; +} diff --git a/drivers/infiniband/hw/ipath/ipath_layer.c b/drivers/infiniband/hw/ipath/ipath_layer.c new file mode 100644 index 000000000000..2cabf6340572 --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_layer.c @@ -0,0 +1,1515 @@ +/* + * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * These are the routines used by layered drivers, currently just the + * layered ethernet driver and verbs layer. + */ + +#include +#include +#include + +#include "ipath_kernel.h" +#include "ips_common.h" +#include "ipath_layer.h" + +/* Acquire before ipath_devs_lock. */ +static DEFINE_MUTEX(ipath_layer_mutex); + +u16 ipath_layer_rcv_opcode; +static int (*layer_intr)(void *, u32); +static int (*layer_rcv)(void *, void *, struct sk_buff *); +static int (*layer_rcv_lid)(void *, void *); +static int (*verbs_piobufavail)(void *); +static void (*verbs_rcv)(void *, void *, void *, u32); +int ipath_verbs_registered; + +static void *(*layer_add_one)(int, struct ipath_devdata *); +static void (*layer_remove_one)(void *); +static void *(*verbs_add_one)(int, struct ipath_devdata *); +static void (*verbs_remove_one)(void *); +static void (*verbs_timer_cb)(void *); + +int __ipath_layer_intr(struct ipath_devdata *dd, u32 arg) +{ + int ret = -ENODEV; + + if (dd->ipath_layer.l_arg && layer_intr) + ret = layer_intr(dd->ipath_layer.l_arg, arg); + + return ret; +} + +int ipath_layer_intr(struct ipath_devdata *dd, u32 arg) +{ + int ret; + + mutex_lock(&ipath_layer_mutex); + + ret = __ipath_layer_intr(dd, arg); + + mutex_unlock(&ipath_layer_mutex); + + return ret; +} + +int __ipath_layer_rcv(struct ipath_devdata *dd, void *hdr, + struct sk_buff *skb) +{ + int ret = -ENODEV; + + if (dd->ipath_layer.l_arg && layer_rcv) + ret = layer_rcv(dd->ipath_layer.l_arg, hdr, skb); + + return ret; +} + +int __ipath_layer_rcv_lid(struct ipath_devdata *dd, void *hdr) +{ + int ret = -ENODEV; + + if (dd->ipath_layer.l_arg && layer_rcv_lid) + ret = layer_rcv_lid(dd->ipath_layer.l_arg, hdr); + + return ret; +} + +int __ipath_verbs_piobufavail(struct ipath_devdata *dd) +{ + int ret = -ENODEV; + + if (dd->verbs_layer.l_arg && verbs_piobufavail) + ret = verbs_piobufavail(dd->verbs_layer.l_arg); + + return ret; +} + +int __ipath_verbs_rcv(struct ipath_devdata *dd, void *rc, void *ebuf, + u32 tlen) +{ + int ret = -ENODEV; + + if (dd->verbs_layer.l_arg && verbs_rcv) { + verbs_rcv(dd->verbs_layer.l_arg, rc, ebuf, tlen); + ret = 0; + } + + return ret; +} + +int ipath_layer_set_linkstate(struct ipath_devdata *dd, u8 newstate) +{ + u32 lstate; + int ret; + + switch (newstate) { + case IPATH_IB_LINKDOWN: + ipath_set_ib_lstate(dd, INFINIPATH_IBCC_LINKINITCMD_POLL << + INFINIPATH_IBCC_LINKINITCMD_SHIFT); + /* don't wait */ + ret = 0; + goto bail; + + case IPATH_IB_LINKDOWN_SLEEP: + ipath_set_ib_lstate(dd, INFINIPATH_IBCC_LINKINITCMD_SLEEP << + INFINIPATH_IBCC_LINKINITCMD_SHIFT); + /* don't wait */ + ret = 0; + goto bail; + + case IPATH_IB_LINKDOWN_DISABLE: + ipath_set_ib_lstate(dd, + INFINIPATH_IBCC_LINKINITCMD_DISABLE << + INFINIPATH_IBCC_LINKINITCMD_SHIFT); + /* don't wait */ + ret = 0; + goto bail; + + case IPATH_IB_LINKINIT: + if (dd->ipath_flags & IPATH_LINKINIT) { + ret = 0; + goto bail; + } + ipath_set_ib_lstate(dd, INFINIPATH_IBCC_LINKCMD_INIT << + INFINIPATH_IBCC_LINKCMD_SHIFT); + lstate = IPATH_LINKINIT; + break; + + case IPATH_IB_LINKARM: + if (dd->ipath_flags & IPATH_LINKARMED) { + ret = 0; + goto bail; + } + if (!(dd->ipath_flags & + (IPATH_LINKINIT | IPATH_LINKACTIVE))) { + ret = -EINVAL; + goto bail; + } + ipath_set_ib_lstate(dd, INFINIPATH_IBCC_LINKCMD_ARMED << + INFINIPATH_IBCC_LINKCMD_SHIFT); + /* + * Since the port can transition to ACTIVE by receiving + * a non VL 15 packet, wait for either state. + */ + lstate = IPATH_LINKARMED | IPATH_LINKACTIVE; + break; + + case IPATH_IB_LINKACTIVE: + if (dd->ipath_flags & IPATH_LINKACTIVE) { + ret = 0; + goto bail; + } + if (!(dd->ipath_flags & IPATH_LINKARMED)) { + ret = -EINVAL; + goto bail; + } + ipath_set_ib_lstate(dd, INFINIPATH_IBCC_LINKCMD_ACTIVE << + INFINIPATH_IBCC_LINKCMD_SHIFT); + lstate = IPATH_LINKACTIVE; + break; + + default: + ipath_dbg("Invalid linkstate 0x%x requested\n", newstate); + ret = -EINVAL; + goto bail; + } + ret = ipath_wait_linkstate(dd, lstate, 2000); + +bail: + return ret; +} + +EXPORT_SYMBOL_GPL(ipath_layer_set_linkstate); + +/** + * ipath_layer_set_mtu - set the MTU + * @dd: the infinipath device + * @arg: the new MTU + * + * we can handle "any" incoming size, the issue here is whether we + * need to restrict our outgoing size. For now, we don't do any + * sanity checking on this, and we don't deal with what happens to + * programs that are already running when the size changes. + * NOTE: changing the MTU will usually cause the IBC to go back to + * link initialize (IPATH_IBSTATE_INIT) state... + */ +int ipath_layer_set_mtu(struct ipath_devdata *dd, u16 arg) +{ + u32 piosize; + int changed = 0; + int ret; + + /* + * mtu is IB data payload max. It's the largest power of 2 less + * than piosize (or even larger, since it only really controls the + * largest we can receive; we can send the max of the mtu and + * piosize). We check that it's one of the valid IB sizes. + */ + if (arg != 256 && arg != 512 && arg != 1024 && arg != 2048 && + arg != 4096) { + ipath_dbg("Trying to set invalid mtu %u, failing\n", arg); + ret = -EINVAL; + goto bail; + } + if (dd->ipath_ibmtu == arg) { + ret = 0; /* same as current */ + goto bail; + } + + piosize = dd->ipath_ibmaxlen; + dd->ipath_ibmtu = arg; + + if (arg >= (piosize - IPATH_PIO_MAXIBHDR)) { + /* Only if it's not the initial value (or reset to it) */ + if (piosize != dd->ipath_init_ibmaxlen) { + dd->ipath_ibmaxlen = piosize; + changed = 1; + } + } else if ((arg + IPATH_PIO_MAXIBHDR) != dd->ipath_ibmaxlen) { + piosize = arg + IPATH_PIO_MAXIBHDR; + ipath_cdbg(VERBOSE, "ibmaxlen was 0x%x, setting to 0x%x " + "(mtu 0x%x)\n", dd->ipath_ibmaxlen, piosize, + arg); + dd->ipath_ibmaxlen = piosize; + changed = 1; + } + + if (changed) { + /* + * set the IBC maxpktlength to the size of our pio + * buffers in words + */ + u64 ibc = dd->ipath_ibcctrl; + ibc &= ~(INFINIPATH_IBCC_MAXPKTLEN_MASK << + INFINIPATH_IBCC_MAXPKTLEN_SHIFT); + + piosize = piosize - 2 * sizeof(u32); /* ignore pbc */ + dd->ipath_ibmaxlen = piosize; + piosize /= sizeof(u32); /* in words */ + /* + * for ICRC, which we only send in diag test pkt mode, and + * we don't need to worry about that for mtu + */ + piosize += 1; + + ibc |= piosize << INFINIPATH_IBCC_MAXPKTLEN_SHIFT; + dd->ipath_ibcctrl = ibc; + ipath_write_kreg(dd, dd->ipath_kregs->kr_ibcctrl, + dd->ipath_ibcctrl); + dd->ipath_f_tidtemplate(dd); + } + + ret = 0; + +bail: + return ret; +} + +EXPORT_SYMBOL_GPL(ipath_layer_set_mtu); + +int ipath_set_sps_lid(struct ipath_devdata *dd, u32 arg, u8 lmc) +{ + ipath_stats.sps_lid[dd->ipath_unit] = arg; + dd->ipath_lid = arg; + dd->ipath_lmc = lmc; + + mutex_lock(&ipath_layer_mutex); + + if (dd->ipath_layer.l_arg && layer_intr) + layer_intr(dd->ipath_layer.l_arg, IPATH_LAYER_INT_LID); + + mutex_unlock(&ipath_layer_mutex); + + return 0; +} + +EXPORT_SYMBOL_GPL(ipath_set_sps_lid); + +int ipath_layer_set_guid(struct ipath_devdata *dd, __be64 guid) +{ + /* XXX - need to inform anyone who cares this just happened. */ + dd->ipath_guid = guid; + return 0; +} + +EXPORT_SYMBOL_GPL(ipath_layer_set_guid); + +__be64 ipath_layer_get_guid(struct ipath_devdata *dd) +{ + return dd->ipath_guid; +} + +EXPORT_SYMBOL_GPL(ipath_layer_get_guid); + +u32 ipath_layer_get_nguid(struct ipath_devdata *dd) +{ + return dd->ipath_nguid; +} + +EXPORT_SYMBOL_GPL(ipath_layer_get_nguid); + +int ipath_layer_query_device(struct ipath_devdata *dd, u32 * vendor, + u32 * boardrev, u32 * majrev, u32 * minrev) +{ + *vendor = dd->ipath_vendorid; + *boardrev = dd->ipath_boardrev; + *majrev = dd->ipath_majrev; + *minrev = dd->ipath_minrev; + + return 0; +} + +EXPORT_SYMBOL_GPL(ipath_layer_query_device); + +u32 ipath_layer_get_flags(struct ipath_devdata *dd) +{ + return dd->ipath_flags; +} + +EXPORT_SYMBOL_GPL(ipath_layer_get_flags); + +struct device *ipath_layer_get_device(struct ipath_devdata *dd) +{ + return &dd->pcidev->dev; +} + +EXPORT_SYMBOL_GPL(ipath_layer_get_device); + +u16 ipath_layer_get_deviceid(struct ipath_devdata *dd) +{ + return dd->ipath_deviceid; +} + +EXPORT_SYMBOL_GPL(ipath_layer_get_deviceid); + +u64 ipath_layer_get_lastibcstat(struct ipath_devdata *dd) +{ + return dd->ipath_lastibcstat; +} + +EXPORT_SYMBOL_GPL(ipath_layer_get_lastibcstat); + +u32 ipath_layer_get_ibmtu(struct ipath_devdata *dd) +{ + return dd->ipath_ibmtu; +} + +EXPORT_SYMBOL_GPL(ipath_layer_get_ibmtu); + +void ipath_layer_add(struct ipath_devdata *dd) +{ + mutex_lock(&ipath_layer_mutex); + + if (layer_add_one) + dd->ipath_layer.l_arg = + layer_add_one(dd->ipath_unit, dd); + + if (verbs_add_one) + dd->verbs_layer.l_arg = + verbs_add_one(dd->ipath_unit, dd); + + mutex_unlock(&ipath_layer_mutex); +} + +void ipath_layer_del(struct ipath_devdata *dd) +{ + mutex_lock(&ipath_layer_mutex); + + if (dd->ipath_layer.l_arg && layer_remove_one) { + layer_remove_one(dd->ipath_layer.l_arg); + dd->ipath_layer.l_arg = NULL; + } + + if (dd->verbs_layer.l_arg && verbs_remove_one) { + verbs_remove_one(dd->verbs_layer.l_arg); + dd->verbs_layer.l_arg = NULL; + } + + mutex_unlock(&ipath_layer_mutex); +} + +int ipath_layer_register(void *(*l_add)(int, struct ipath_devdata *), + void (*l_remove)(void *), + int (*l_intr)(void *, u32), + int (*l_rcv)(void *, void *, struct sk_buff *), + u16 l_rcv_opcode, + int (*l_rcv_lid)(void *, void *)) +{ + struct ipath_devdata *dd, *tmp; + unsigned long flags; + + mutex_lock(&ipath_layer_mutex); + + layer_add_one = l_add; + layer_remove_one = l_remove; + layer_intr = l_intr; + layer_rcv = l_rcv; + layer_rcv_lid = l_rcv_lid; + ipath_layer_rcv_opcode = l_rcv_opcode; + + spin_lock_irqsave(&ipath_devs_lock, flags); + + list_for_each_entry_safe(dd, tmp, &ipath_dev_list, ipath_list) { + if (!(dd->ipath_flags & IPATH_INITTED)) + continue; + + if (dd->ipath_layer.l_arg) + continue; + + if (!(*dd->ipath_statusp & IPATH_STATUS_SMA)) + *dd->ipath_statusp |= IPATH_STATUS_OIB_SMA; + + spin_unlock_irqrestore(&ipath_devs_lock, flags); + dd->ipath_layer.l_arg = l_add(dd->ipath_unit, dd); + spin_lock_irqsave(&ipath_devs_lock, flags); + } + + spin_unlock_irqrestore(&ipath_devs_lock, flags); + mutex_unlock(&ipath_layer_mutex); + + return 0; +} + +EXPORT_SYMBOL_GPL(ipath_layer_register); + +void ipath_layer_unregister(void) +{ + struct ipath_devdata *dd, *tmp; + unsigned long flags; + + mutex_lock(&ipath_layer_mutex); + spin_lock_irqsave(&ipath_devs_lock, flags); + + list_for_each_entry_safe(dd, tmp, &ipath_dev_list, ipath_list) { + if (dd->ipath_layer.l_arg && layer_remove_one) { + spin_unlock_irqrestore(&ipath_devs_lock, flags); + layer_remove_one(dd->ipath_layer.l_arg); + spin_lock_irqsave(&ipath_devs_lock, flags); + dd->ipath_layer.l_arg = NULL; + } + } + + spin_unlock_irqrestore(&ipath_devs_lock, flags); + + layer_add_one = NULL; + layer_remove_one = NULL; + layer_intr = NULL; + layer_rcv = NULL; + layer_rcv_lid = NULL; + + mutex_unlock(&ipath_layer_mutex); +} + +EXPORT_SYMBOL_GPL(ipath_layer_unregister); + +static void __ipath_verbs_timer(unsigned long arg) +{ + struct ipath_devdata *dd = (struct ipath_devdata *) arg; + + /* + * If port 0 receive packet interrupts are not available, or + * can be missed, poll the receive queue + */ + if (dd->ipath_flags & IPATH_POLL_RX_INTR) + ipath_kreceive(dd); + + /* Handle verbs layer timeouts. */ + if (dd->verbs_layer.l_arg && verbs_timer_cb) + verbs_timer_cb(dd->verbs_layer.l_arg); + + mod_timer(&dd->verbs_layer.l_timer, jiffies + 1); +} + +/** + * ipath_verbs_register - verbs layer registration + * @l_piobufavail: callback for when PIO buffers become available + * @l_rcv: callback for receiving a packet + * @l_timer_cb: timer callback + * @ipath_devdata: device data structure is put here + */ +int ipath_verbs_register(void *(*l_add)(int, struct ipath_devdata *), + void (*l_remove)(void *arg), + int (*l_piobufavail) (void *arg), + void (*l_rcv) (void *arg, void *rhdr, + void *data, u32 tlen), + void (*l_timer_cb) (void *arg)) +{ + struct ipath_devdata *dd, *tmp; + unsigned long flags; + + mutex_lock(&ipath_layer_mutex); + + verbs_add_one = l_add; + verbs_remove_one = l_remove; + verbs_piobufavail = l_piobufavail; + verbs_rcv = l_rcv; + verbs_timer_cb = l_timer_cb; + + spin_lock_irqsave(&ipath_devs_lock, flags); + + list_for_each_entry_safe(dd, tmp, &ipath_dev_list, ipath_list) { + if (!(dd->ipath_flags & IPATH_INITTED)) + continue; + + if (dd->verbs_layer.l_arg) + continue; + + spin_unlock_irqrestore(&ipath_devs_lock, flags); + dd->verbs_layer.l_arg = l_add(dd->ipath_unit, dd); + spin_lock_irqsave(&ipath_devs_lock, flags); + } + + spin_unlock_irqrestore(&ipath_devs_lock, flags); + mutex_unlock(&ipath_layer_mutex); + + ipath_verbs_registered = 1; + + return 0; +} + +EXPORT_SYMBOL_GPL(ipath_verbs_register); + +void ipath_verbs_unregister(void) +{ + struct ipath_devdata *dd, *tmp; + unsigned long flags; + + mutex_lock(&ipath_layer_mutex); + spin_lock_irqsave(&ipath_devs_lock, flags); + + list_for_each_entry_safe(dd, tmp, &ipath_dev_list, ipath_list) { + *dd->ipath_statusp &= ~IPATH_STATUS_OIB_SMA; + + if (dd->verbs_layer.l_arg && verbs_remove_one) { + spin_unlock_irqrestore(&ipath_devs_lock, flags); + verbs_remove_one(dd->verbs_layer.l_arg); + spin_lock_irqsave(&ipath_devs_lock, flags); + dd->verbs_layer.l_arg = NULL; + } + } + + spin_unlock_irqrestore(&ipath_devs_lock, flags); + + verbs_add_one = NULL; + verbs_remove_one = NULL; + verbs_piobufavail = NULL; + verbs_rcv = NULL; + verbs_timer_cb = NULL; + + mutex_unlock(&ipath_layer_mutex); +} + +EXPORT_SYMBOL_GPL(ipath_verbs_unregister); + +int ipath_layer_open(struct ipath_devdata *dd, u32 * pktmax) +{ + int ret; + u32 intval = 0; + + mutex_lock(&ipath_layer_mutex); + + if (!dd->ipath_layer.l_arg) { + ret = -EINVAL; + goto bail; + } + + ret = ipath_setrcvhdrsize(dd, NUM_OF_EXTRA_WORDS_IN_HEADER_QUEUE); + + if (ret < 0) + goto bail; + + *pktmax = dd->ipath_ibmaxlen; + + if (*dd->ipath_statusp & IPATH_STATUS_IB_READY) + intval |= IPATH_LAYER_INT_IF_UP; + if (ipath_stats.sps_lid[dd->ipath_unit]) + intval |= IPATH_LAYER_INT_LID; + if (ipath_stats.sps_mlid[dd->ipath_unit]) + intval |= IPATH_LAYER_INT_BCAST; + /* + * do this on open, in case low level is already up and + * just layered driver was reloaded, etc. + */ + if (intval) + layer_intr(dd->ipath_layer.l_arg, intval); + + ret = 0; +bail: + mutex_unlock(&ipath_layer_mutex); + + return ret; +} + +EXPORT_SYMBOL_GPL(ipath_layer_open); + +u16 ipath_layer_get_lid(struct ipath_devdata *dd) +{ + return dd->ipath_lid; +} + +EXPORT_SYMBOL_GPL(ipath_layer_get_lid); + +/** + * ipath_layer_get_mac - get the MAC address + * @dd: the infinipath device + * @mac: the MAC is put here + * + * This is the EUID-64 OUI octets (top 3), then + * skip the next 2 (which should both be zero or 0xff). + * The returned MAC is in network order + * mac points to at least 6 bytes of buffer + * We assume that by the time the LID is set, that the GUID is as valid + * as it's ever going to be, rather than adding yet another status bit. + */ + +int ipath_layer_get_mac(struct ipath_devdata *dd, u8 * mac) +{ + u8 *guid; + + guid = (u8 *) &dd->ipath_guid; + + mac[0] = guid[0]; + mac[1] = guid[1]; + mac[2] = guid[2]; + mac[3] = guid[5]; + mac[4] = guid[6]; + mac[5] = guid[7]; + if ((guid[3] || guid[4]) && !(guid[3] == 0xff && guid[4] == 0xff)) + ipath_dbg("Warning, guid bytes 3 and 4 not 0 or 0xffff: " + "%x %x\n", guid[3], guid[4]); + return 0; +} + +EXPORT_SYMBOL_GPL(ipath_layer_get_mac); + +u16 ipath_layer_get_bcast(struct ipath_devdata *dd) +{ + return dd->ipath_mlid; +} + +EXPORT_SYMBOL_GPL(ipath_layer_get_bcast); + +u32 ipath_layer_get_cr_errpkey(struct ipath_devdata *dd) +{ + return ipath_read_creg32(dd, dd->ipath_cregs->cr_errpkey); +} + +EXPORT_SYMBOL_GPL(ipath_layer_get_cr_errpkey); + +static void update_sge(struct ipath_sge_state *ss, u32 length) +{ + struct ipath_sge *sge = &ss->sge; + + sge->vaddr += length; + sge->length -= length; + sge->sge_length -= length; + if (sge->sge_length == 0) { + if (--ss->num_sge) + *sge = *ss->sg_list++; + } else if (sge->length == 0 && sge->mr != NULL) { + if (++sge->n >= IPATH_SEGSZ) { + if (++sge->m >= sge->mr->mapsz) + return; + sge->n = 0; + } + sge->vaddr = sge->mr->map[sge->m]->segs[sge->n].vaddr; + sge->length = sge->mr->map[sge->m]->segs[sge->n].length; + } +} + +#ifdef __LITTLE_ENDIAN +static inline u32 get_upper_bits(u32 data, u32 shift) +{ + return data >> shift; +} + +static inline u32 set_upper_bits(u32 data, u32 shift) +{ + return data << shift; +} + +static inline u32 clear_upper_bytes(u32 data, u32 n, u32 off) +{ + data <<= ((sizeof(u32) - n) * BITS_PER_BYTE); + data >>= ((sizeof(u32) - n - off) * BITS_PER_BYTE); + return data; +} +#else +static inline u32 get_upper_bits(u32 data, u32 shift) +{ + return data << shift; +} + +static inline u32 set_upper_bits(u32 data, u32 shift) +{ + return data >> shift; +} + +static inline u32 clear_upper_bytes(u32 data, u32 n, u32 off) +{ + data >>= ((sizeof(u32) - n) * BITS_PER_BYTE); + data <<= ((sizeof(u32) - n - off) * BITS_PER_BYTE); + return data; +} +#endif + +static void copy_io(u32 __iomem *piobuf, struct ipath_sge_state *ss, + u32 length) +{ + u32 extra = 0; + u32 data = 0; + u32 last; + + while (1) { + u32 len = ss->sge.length; + u32 off; + + BUG_ON(len == 0); + if (len > length) + len = length; + if (len > ss->sge.sge_length) + len = ss->sge.sge_length; + /* If the source address is not aligned, try to align it. */ + off = (unsigned long)ss->sge.vaddr & (sizeof(u32) - 1); + if (off) { + u32 *addr = (u32 *)((unsigned long)ss->sge.vaddr & + ~(sizeof(u32) - 1)); + u32 v = get_upper_bits(*addr, off * BITS_PER_BYTE); + u32 y; + + y = sizeof(u32) - off; + if (len > y) + len = y; + if (len + extra >= sizeof(u32)) { + data |= set_upper_bits(v, extra * + BITS_PER_BYTE); + len = sizeof(u32) - extra; + if (len == length) { + last = data; + break; + } + __raw_writel(data, piobuf); + piobuf++; + extra = 0; + data = 0; + } else { + /* Clear unused upper bytes */ + data |= clear_upper_bytes(v, len, extra); + if (len == length) { + last = data; + break; + } + extra += len; + } + } else if (extra) { + /* Source address is aligned. */ + u32 *addr = (u32 *) ss->sge.vaddr; + int shift = extra * BITS_PER_BYTE; + int ushift = 32 - shift; + u32 l = len; + + while (l >= sizeof(u32)) { + u32 v = *addr; + + data |= set_upper_bits(v, shift); + __raw_writel(data, piobuf); + data = get_upper_bits(v, ushift); + piobuf++; + addr++; + l -= sizeof(u32); + } + /* + * We still have 'extra' number of bytes leftover. + */ + if (l) { + u32 v = *addr; + + if (l + extra >= sizeof(u32)) { + data |= set_upper_bits(v, shift); + len -= l + extra - sizeof(u32); + if (len == length) { + last = data; + break; + } + __raw_writel(data, piobuf); + piobuf++; + extra = 0; + data = 0; + } else { + /* Clear unused upper bytes */ + data |= clear_upper_bytes(v, l, + extra); + if (len == length) { + last = data; + break; + } + extra += l; + } + } else if (len == length) { + last = data; + break; + } + } else if (len == length) { + u32 w; + + /* + * Need to round up for the last dword in the + * packet. + */ + w = (len + 3) >> 2; + __iowrite32_copy(piobuf, ss->sge.vaddr, w - 1); + piobuf += w - 1; + last = ((u32 *) ss->sge.vaddr)[w - 1]; + break; + } else { + u32 w = len >> 2; + + __iowrite32_copy(piobuf, ss->sge.vaddr, w); + piobuf += w; + + extra = len & (sizeof(u32) - 1); + if (extra) { + u32 v = ((u32 *) ss->sge.vaddr)[w]; + + /* Clear unused upper bytes */ + data = clear_upper_bytes(v, extra, 0); + } + } + update_sge(ss, len); + length -= len; + } + /* must flush early everything before trigger word */ + ipath_flush_wc(); + __raw_writel(last, piobuf); + /* be sure trigger word is written */ + ipath_flush_wc(); + update_sge(ss, length); +} + +/** + * ipath_verbs_send - send a packet from the verbs layer + * @dd: the infinipath device + * @hdrwords: the number of works in the header + * @hdr: the packet header + * @len: the length of the packet in bytes + * @ss: the SGE to send + * + * This is like ipath_sma_send_pkt() in that we need to be able to send + * packets after the chip is initialized (MADs) but also like + * ipath_layer_send_hdr() since its used by the verbs layer. + */ +int ipath_verbs_send(struct ipath_devdata *dd, u32 hdrwords, + u32 *hdr, u32 len, struct ipath_sge_state *ss) +{ + u32 __iomem *piobuf; + u32 plen; + int ret; + + /* +1 is for the qword padding of pbc */ + plen = hdrwords + ((len + 3) >> 2) + 1; + if (unlikely((plen << 2) > dd->ipath_ibmaxlen)) { + ipath_dbg("packet len 0x%x too long, failing\n", plen); + ret = -EINVAL; + goto bail; + } + + /* Get a PIO buffer to use. */ + piobuf = ipath_getpiobuf(dd, NULL); + if (unlikely(piobuf == NULL)) { + ret = -EBUSY; + goto bail; + } + + /* + * Write len to control qword, no flags. + * We have to flush after the PBC for correctness on some cpus + * or WC buffer can be written out of order. + */ + writeq(plen, piobuf); + ipath_flush_wc(); + piobuf += 2; + if (len == 0) { + /* + * If there is just the header portion, must flush before + * writing last word of header for correctness, and after + * the last header word (trigger word). + */ + __iowrite32_copy(piobuf, hdr, hdrwords - 1); + ipath_flush_wc(); + __raw_writel(hdr[hdrwords - 1], piobuf + hdrwords - 1); + ipath_flush_wc(); + ret = 0; + goto bail; + } + + __iowrite32_copy(piobuf, hdr, hdrwords); + piobuf += hdrwords; + + /* The common case is aligned and contained in one segment. */ + if (likely(ss->num_sge == 1 && len <= ss->sge.length && + !((unsigned long)ss->sge.vaddr & (sizeof(u32) - 1)))) { + u32 w; + + /* Need to round up for the last dword in the packet. */ + w = (len + 3) >> 2; + __iowrite32_copy(piobuf, ss->sge.vaddr, w - 1); + /* must flush early everything before trigger word */ + ipath_flush_wc(); + __raw_writel(((u32 *) ss->sge.vaddr)[w - 1], + piobuf + w - 1); + /* be sure trigger word is written */ + ipath_flush_wc(); + update_sge(ss, len); + ret = 0; + goto bail; + } + copy_io(piobuf, ss, len); + ret = 0; + +bail: + return ret; +} + +EXPORT_SYMBOL_GPL(ipath_verbs_send); + +int ipath_layer_snapshot_counters(struct ipath_devdata *dd, u64 *swords, + u64 *rwords, u64 *spkts, u64 *rpkts, + u64 *xmit_wait) +{ + int ret; + + if (!(dd->ipath_flags & IPATH_INITTED)) { + /* no hardware, freeze, etc. */ + ipath_dbg("unit %u not usable\n", dd->ipath_unit); + ret = -EINVAL; + goto bail; + } + *swords = ipath_snap_cntr(dd, dd->ipath_cregs->cr_wordsendcnt); + *rwords = ipath_snap_cntr(dd, dd->ipath_cregs->cr_wordrcvcnt); + *spkts = ipath_snap_cntr(dd, dd->ipath_cregs->cr_pktsendcnt); + *rpkts = ipath_snap_cntr(dd, dd->ipath_cregs->cr_pktrcvcnt); + *xmit_wait = ipath_snap_cntr(dd, dd->ipath_cregs->cr_sendstallcnt); + + ret = 0; + +bail: + return ret; +} + +EXPORT_SYMBOL_GPL(ipath_layer_snapshot_counters); + +/** + * ipath_layer_get_counters - get various chip counters + * @dd: the infinipath device + * @cntrs: counters are placed here + * + * Return the counters needed by recv_pma_get_portcounters(). + */ +int ipath_layer_get_counters(struct ipath_devdata *dd, + struct ipath_layer_counters *cntrs) +{ + int ret; + + if (!(dd->ipath_flags & IPATH_INITTED)) { + /* no hardware, freeze, etc. */ + ipath_dbg("unit %u not usable\n", dd->ipath_unit); + ret = -EINVAL; + goto bail; + } + cntrs->symbol_error_counter = + ipath_snap_cntr(dd, dd->ipath_cregs->cr_ibsymbolerrcnt); + cntrs->link_error_recovery_counter = + ipath_snap_cntr(dd, dd->ipath_cregs->cr_iblinkerrrecovcnt); + cntrs->link_downed_counter = + ipath_snap_cntr(dd, dd->ipath_cregs->cr_iblinkdowncnt); + cntrs->port_rcv_errors = + ipath_snap_cntr(dd, dd->ipath_cregs->cr_rxdroppktcnt) + + ipath_snap_cntr(dd, dd->ipath_cregs->cr_rcvovflcnt) + + ipath_snap_cntr(dd, dd->ipath_cregs->cr_portovflcnt) + + ipath_snap_cntr(dd, dd->ipath_cregs->cr_errrcvflowctrlcnt) + + ipath_snap_cntr(dd, dd->ipath_cregs->cr_err_rlencnt) + + ipath_snap_cntr(dd, dd->ipath_cregs->cr_invalidrlencnt) + + ipath_snap_cntr(dd, dd->ipath_cregs->cr_erricrccnt) + + ipath_snap_cntr(dd, dd->ipath_cregs->cr_errvcrccnt) + + ipath_snap_cntr(dd, dd->ipath_cregs->cr_errlpcrccnt) + + ipath_snap_cntr(dd, dd->ipath_cregs->cr_errlinkcnt) + + ipath_snap_cntr(dd, dd->ipath_cregs->cr_badformatcnt); + cntrs->port_rcv_remphys_errors = + ipath_snap_cntr(dd, dd->ipath_cregs->cr_rcvebpcnt); + cntrs->port_xmit_discards = + ipath_snap_cntr(dd, dd->ipath_cregs->cr_unsupvlcnt); + cntrs->port_xmit_data = + ipath_snap_cntr(dd, dd->ipath_cregs->cr_wordsendcnt); + cntrs->port_rcv_data = + ipath_snap_cntr(dd, dd->ipath_cregs->cr_wordrcvcnt); + cntrs->port_xmit_packets = + ipath_snap_cntr(dd, dd->ipath_cregs->cr_pktsendcnt); + cntrs->port_rcv_packets = + ipath_snap_cntr(dd, dd->ipath_cregs->cr_pktrcvcnt); + + ret = 0; + +bail: + return ret; +} + +EXPORT_SYMBOL_GPL(ipath_layer_get_counters); + +int ipath_layer_want_buffer(struct ipath_devdata *dd) +{ + set_bit(IPATH_S_PIOINTBUFAVAIL, &dd->ipath_sendctrl); + ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, + dd->ipath_sendctrl); + + return 0; +} + +EXPORT_SYMBOL_GPL(ipath_layer_want_buffer); + +int ipath_layer_send_hdr(struct ipath_devdata *dd, struct ether_header *hdr) +{ + int ret = 0; + u32 __iomem *piobuf; + u32 plen, *uhdr; + size_t count; + __be16 vlsllnh; + + if (!(dd->ipath_flags & IPATH_RCVHDRSZ_SET)) { + ipath_dbg("send while not open\n"); + ret = -EINVAL; + } else + if ((dd->ipath_flags & (IPATH_LINKUNK | IPATH_LINKDOWN)) || + dd->ipath_lid == 0) { + /* + * lid check is for when sma hasn't yet configured + */ + ret = -ENETDOWN; + ipath_cdbg(VERBOSE, "send while not ready, " + "mylid=%u, flags=0x%x\n", + dd->ipath_lid, dd->ipath_flags); + } + + vlsllnh = *((__be16 *) hdr); + if (vlsllnh != htons(IPS_LRH_BTH)) { + ipath_dbg("Warning: lrh[0] wrong (%x, not %x); " + "not sending\n", be16_to_cpu(vlsllnh), + IPS_LRH_BTH); + ret = -EINVAL; + } + if (ret) + goto done; + + /* Get a PIO buffer to use. */ + piobuf = ipath_getpiobuf(dd, NULL); + if (piobuf == NULL) { + ret = -EBUSY; + goto done; + } + + plen = (sizeof(*hdr) >> 2); /* actual length */ + ipath_cdbg(EPKT, "0x%x+1w pio %p\n", plen, piobuf); + + writeq(plen+1, piobuf); /* len (+1 for pad) to pbc, no flags */ + ipath_flush_wc(); + piobuf += 2; + uhdr = (u32 *)hdr; + count = plen-1; /* amount we can copy before trigger word */ + __iowrite32_copy(piobuf, uhdr, count); + ipath_flush_wc(); + __raw_writel(uhdr[count], piobuf + count); + ipath_flush_wc(); /* ensure it's sent, now */ + + ipath_stats.sps_ether_spkts++; /* ether packet sent */ + +done: + return ret; +} + +EXPORT_SYMBOL_GPL(ipath_layer_send_hdr); + +int ipath_layer_set_piointbufavail_int(struct ipath_devdata *dd) +{ + set_bit(IPATH_S_PIOINTBUFAVAIL, &dd->ipath_sendctrl); + + ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, + dd->ipath_sendctrl); + return 0; +} + +EXPORT_SYMBOL_GPL(ipath_layer_set_piointbufavail_int); + +int ipath_layer_enable_timer(struct ipath_devdata *dd) +{ + /* + * HT-400 has a design flaw where the chip and kernel idea + * of the tail register don't always agree, and therefore we won't + * get an interrupt on the next packet received. + * If the board supports per packet receive interrupts, use it. + * Otherwise, the timer function periodically checks for packets + * to cover this case. + * Either way, the timer is needed for verbs layer related + * processing. + */ + if (dd->ipath_flags & IPATH_GPIO_INTR) { + ipath_write_kreg(dd, dd->ipath_kregs->kr_debugportselect, + 0x2074076542310ULL); + /* Enable GPIO bit 2 interrupt */ + ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_mask, + (u64) (1 << 2)); + } + + init_timer(&dd->verbs_layer.l_timer); + dd->verbs_layer.l_timer.function = __ipath_verbs_timer; + dd->verbs_layer.l_timer.data = (unsigned long)dd; + dd->verbs_layer.l_timer.expires = jiffies + 1; + add_timer(&dd->verbs_layer.l_timer); + + return 0; +} + +EXPORT_SYMBOL_GPL(ipath_layer_enable_timer); + +int ipath_layer_disable_timer(struct ipath_devdata *dd) +{ + /* Disable GPIO bit 2 interrupt */ + if (dd->ipath_flags & IPATH_GPIO_INTR) + ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_mask, 0); + + del_timer_sync(&dd->verbs_layer.l_timer); + + return 0; +} + +EXPORT_SYMBOL_GPL(ipath_layer_disable_timer); + +/** + * ipath_layer_set_verbs_flags - set the verbs layer flags + * @dd: the infinipath device + * @flags: the flags to set + */ +int ipath_layer_set_verbs_flags(struct ipath_devdata *dd, unsigned flags) +{ + struct ipath_devdata *ss; + unsigned long lflags; + + spin_lock_irqsave(&ipath_devs_lock, lflags); + + list_for_each_entry(ss, &ipath_dev_list, ipath_list) { + if (!(ss->ipath_flags & IPATH_INITTED)) + continue; + if ((flags & IPATH_VERBS_KERNEL_SMA) && + !(*ss->ipath_statusp & IPATH_STATUS_SMA)) + *ss->ipath_statusp |= IPATH_STATUS_OIB_SMA; + else + *ss->ipath_statusp &= ~IPATH_STATUS_OIB_SMA; + } + + spin_unlock_irqrestore(&ipath_devs_lock, lflags); + + return 0; +} + +EXPORT_SYMBOL_GPL(ipath_layer_set_verbs_flags); + +/** + * ipath_layer_get_npkeys - return the size of the PKEY table for port 0 + * @dd: the infinipath device + */ +unsigned ipath_layer_get_npkeys(struct ipath_devdata *dd) +{ + return ARRAY_SIZE(dd->ipath_pd[0]->port_pkeys); +} + +EXPORT_SYMBOL_GPL(ipath_layer_get_npkeys); + +/** + * ipath_layer_get_pkey - return the indexed PKEY from the port 0 PKEY table + * @dd: the infinipath device + * @index: the PKEY index + */ +unsigned ipath_layer_get_pkey(struct ipath_devdata *dd, unsigned index) +{ + unsigned ret; + + if (index >= ARRAY_SIZE(dd->ipath_pd[0]->port_pkeys)) + ret = 0; + else + ret = dd->ipath_pd[0]->port_pkeys[index]; + + return ret; +} + +EXPORT_SYMBOL_GPL(ipath_layer_get_pkey); + +/** + * ipath_layer_get_pkeys - return the PKEY table for port 0 + * @dd: the infinipath device + * @pkeys: the pkey table is placed here + */ +int ipath_layer_get_pkeys(struct ipath_devdata *dd, u16 * pkeys) +{ + struct ipath_portdata *pd = dd->ipath_pd[0]; + + memcpy(pkeys, pd->port_pkeys, sizeof(pd->port_pkeys)); + + return 0; +} + +EXPORT_SYMBOL_GPL(ipath_layer_get_pkeys); + +/** + * rm_pkey - decrecment the reference count for the given PKEY + * @dd: the infinipath device + * @key: the PKEY index + * + * Return true if this was the last reference and the hardware table entry + * needs to be changed. + */ +static int rm_pkey(struct ipath_devdata *dd, u16 key) +{ + int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(dd->ipath_pkeys); i++) { + if (dd->ipath_pkeys[i] != key) + continue; + if (atomic_dec_and_test(&dd->ipath_pkeyrefs[i])) { + dd->ipath_pkeys[i] = 0; + ret = 1; + goto bail; + } + break; + } + + ret = 0; + +bail: + return ret; +} + +/** + * add_pkey - add the given PKEY to the hardware table + * @dd: the infinipath device + * @key: the PKEY + * + * Return an error code if unable to add the entry, zero if no change, + * or 1 if the hardware PKEY register needs to be updated. + */ +static int add_pkey(struct ipath_devdata *dd, u16 key) +{ + int i; + u16 lkey = key & 0x7FFF; + int any = 0; + int ret; + + if (lkey == 0x7FFF) { + ret = 0; + goto bail; + } + + /* Look for an empty slot or a matching PKEY. */ + for (i = 0; i < ARRAY_SIZE(dd->ipath_pkeys); i++) { + if (!dd->ipath_pkeys[i]) { + any++; + continue; + } + /* If it matches exactly, try to increment the ref count */ + if (dd->ipath_pkeys[i] == key) { + if (atomic_inc_return(&dd->ipath_pkeyrefs[i]) > 1) { + ret = 0; + goto bail; + } + /* Lost the race. Look for an empty slot below. */ + atomic_dec(&dd->ipath_pkeyrefs[i]); + any++; + } + /* + * It makes no sense to have both the limited and unlimited + * PKEY set at the same time since the unlimited one will + * disable the limited one. + */ + if ((dd->ipath_pkeys[i] & 0x7FFF) == lkey) { + ret = -EEXIST; + goto bail; + } + } + if (!any) { + ret = -EBUSY; + goto bail; + } + for (i = 0; i < ARRAY_SIZE(dd->ipath_pkeys); i++) { + if (!dd->ipath_pkeys[i] && + atomic_inc_return(&dd->ipath_pkeyrefs[i]) == 1) { + /* for ipathstats, etc. */ + ipath_stats.sps_pkeys[i] = lkey; + dd->ipath_pkeys[i] = key; + ret = 1; + goto bail; + } + } + ret = -EBUSY; + +bail: + return ret; +} + +/** + * ipath_layer_set_pkeys - set the PKEY table for port 0 + * @dd: the infinipath device + * @pkeys: the PKEY table + */ +int ipath_layer_set_pkeys(struct ipath_devdata *dd, u16 * pkeys) +{ + struct ipath_portdata *pd; + int i; + int changed = 0; + + pd = dd->ipath_pd[0]; + + for (i = 0; i < ARRAY_SIZE(pd->port_pkeys); i++) { + u16 key = pkeys[i]; + u16 okey = pd->port_pkeys[i]; + + if (key == okey) + continue; + /* + * The value of this PKEY table entry is changing. + * Remove the old entry in the hardware's array of PKEYs. + */ + if (okey & 0x7FFF) + changed |= rm_pkey(dd, okey); + if (key & 0x7FFF) { + int ret = add_pkey(dd, key); + + if (ret < 0) + key = 0; + else + changed |= ret; + } + pd->port_pkeys[i] = key; + } + if (changed) { + u64 pkey; + + pkey = (u64) dd->ipath_pkeys[0] | + ((u64) dd->ipath_pkeys[1] << 16) | + ((u64) dd->ipath_pkeys[2] << 32) | + ((u64) dd->ipath_pkeys[3] << 48); + ipath_cdbg(VERBOSE, "p0 new pkey reg %llx\n", + (unsigned long long) pkey); + ipath_write_kreg(dd, dd->ipath_kregs->kr_partitionkey, + pkey); + } + return 0; +} + +EXPORT_SYMBOL_GPL(ipath_layer_set_pkeys); + +/** + * ipath_layer_get_linkdowndefaultstate - get the default linkdown state + * @dd: the infinipath device + * + * Returns zero if the default is POLL, 1 if the default is SLEEP. + */ +int ipath_layer_get_linkdowndefaultstate(struct ipath_devdata *dd) +{ + return !!(dd->ipath_ibcctrl & INFINIPATH_IBCC_LINKDOWNDEFAULTSTATE); +} + +EXPORT_SYMBOL_GPL(ipath_layer_get_linkdowndefaultstate); + +/** + * ipath_layer_set_linkdowndefaultstate - set the default linkdown state + * @dd: the infinipath device + * @sleep: the new state + * + * Note that this will only take effect when the link state changes. + */ +int ipath_layer_set_linkdowndefaultstate(struct ipath_devdata *dd, + int sleep) +{ + if (sleep) + dd->ipath_ibcctrl |= INFINIPATH_IBCC_LINKDOWNDEFAULTSTATE; + else + dd->ipath_ibcctrl &= ~INFINIPATH_IBCC_LINKDOWNDEFAULTSTATE; + ipath_write_kreg(dd, dd->ipath_kregs->kr_ibcctrl, + dd->ipath_ibcctrl); + return 0; +} + +EXPORT_SYMBOL_GPL(ipath_layer_set_linkdowndefaultstate); + +int ipath_layer_get_phyerrthreshold(struct ipath_devdata *dd) +{ + return (dd->ipath_ibcctrl >> + INFINIPATH_IBCC_PHYERRTHRESHOLD_SHIFT) & + INFINIPATH_IBCC_PHYERRTHRESHOLD_MASK; +} + +EXPORT_SYMBOL_GPL(ipath_layer_get_phyerrthreshold); + +/** + * ipath_layer_set_phyerrthreshold - set the physical error threshold + * @dd: the infinipath device + * @n: the new threshold + * + * Note that this will only take effect when the link state changes. + */ +int ipath_layer_set_phyerrthreshold(struct ipath_devdata *dd, unsigned n) +{ + unsigned v; + + v = (dd->ipath_ibcctrl >> INFINIPATH_IBCC_PHYERRTHRESHOLD_SHIFT) & + INFINIPATH_IBCC_PHYERRTHRESHOLD_MASK; + if (v != n) { + dd->ipath_ibcctrl &= + ~(INFINIPATH_IBCC_PHYERRTHRESHOLD_MASK << + INFINIPATH_IBCC_PHYERRTHRESHOLD_SHIFT); + dd->ipath_ibcctrl |= + (u64) n << INFINIPATH_IBCC_PHYERRTHRESHOLD_SHIFT; + ipath_write_kreg(dd, dd->ipath_kregs->kr_ibcctrl, + dd->ipath_ibcctrl); + } + return 0; +} + +EXPORT_SYMBOL_GPL(ipath_layer_set_phyerrthreshold); + +int ipath_layer_get_overrunthreshold(struct ipath_devdata *dd) +{ + return (dd->ipath_ibcctrl >> + INFINIPATH_IBCC_OVERRUNTHRESHOLD_SHIFT) & + INFINIPATH_IBCC_OVERRUNTHRESHOLD_MASK; +} + +EXPORT_SYMBOL_GPL(ipath_layer_get_overrunthreshold); + +/** + * ipath_layer_set_overrunthreshold - set the overrun threshold + * @dd: the infinipath device + * @n: the new threshold + * + * Note that this will only take effect when the link state changes. + */ +int ipath_layer_set_overrunthreshold(struct ipath_devdata *dd, unsigned n) +{ + unsigned v; + + v = (dd->ipath_ibcctrl >> INFINIPATH_IBCC_OVERRUNTHRESHOLD_SHIFT) & + INFINIPATH_IBCC_OVERRUNTHRESHOLD_MASK; + if (v != n) { + dd->ipath_ibcctrl &= + ~(INFINIPATH_IBCC_OVERRUNTHRESHOLD_MASK << + INFINIPATH_IBCC_OVERRUNTHRESHOLD_SHIFT); + dd->ipath_ibcctrl |= + (u64) n << INFINIPATH_IBCC_OVERRUNTHRESHOLD_SHIFT; + ipath_write_kreg(dd, dd->ipath_kregs->kr_ibcctrl, + dd->ipath_ibcctrl); + } + return 0; +} + +EXPORT_SYMBOL_GPL(ipath_layer_set_overrunthreshold); + +int ipath_layer_get_boardname(struct ipath_devdata *dd, char *name, + size_t namelen) +{ + return dd->ipath_f_get_boardname(dd, name, namelen); +} +EXPORT_SYMBOL_GPL(ipath_layer_get_boardname); + +u32 ipath_layer_get_rcvhdrentsize(struct ipath_devdata *dd) +{ + return dd->ipath_rcvhdrentsize; +} +EXPORT_SYMBOL_GPL(ipath_layer_get_rcvhdrentsize); diff --git a/drivers/infiniband/hw/ipath/ipath_layer.h b/drivers/infiniband/hw/ipath/ipath_layer.h new file mode 100644 index 000000000000..6fefd15bd2da --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_layer.h @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _IPATH_LAYER_H +#define _IPATH_LAYER_H + +/* + * This header file is for symbols shared between the infinipath driver + * and drivers layered upon it (such as ipath). + */ + +struct sk_buff; +struct ipath_sge_state; +struct ipath_devdata; +struct ether_header; + +struct ipath_layer_counters { + u64 symbol_error_counter; + u64 link_error_recovery_counter; + u64 link_downed_counter; + u64 port_rcv_errors; + u64 port_rcv_remphys_errors; + u64 port_xmit_discards; + u64 port_xmit_data; + u64 port_rcv_data; + u64 port_xmit_packets; + u64 port_rcv_packets; +}; + +/* + * A segment is a linear region of low physical memory. + * XXX Maybe we should use phys addr here and kmap()/kunmap(). + * Used by the verbs layer. + */ +struct ipath_seg { + void *vaddr; + size_t length; +}; + +/* The number of ipath_segs that fit in a page. */ +#define IPATH_SEGSZ (PAGE_SIZE / sizeof (struct ipath_seg)) + +struct ipath_segarray { + struct ipath_seg segs[IPATH_SEGSZ]; +}; + +struct ipath_mregion { + u64 user_base; /* User's address for this region */ + u64 iova; /* IB start address of this region */ + size_t length; + u32 lkey; + u32 offset; /* offset (bytes) to start of region */ + int access_flags; + u32 max_segs; /* number of ipath_segs in all the arrays */ + u32 mapsz; /* size of the map array */ + struct ipath_segarray *map[0]; /* the segments */ +}; + +/* + * These keep track of the copy progress within a memory region. + * Used by the verbs layer. + */ +struct ipath_sge { + struct ipath_mregion *mr; + void *vaddr; /* current pointer into the segment */ + u32 sge_length; /* length of the SGE */ + u32 length; /* remaining length of the segment */ + u16 m; /* current index: mr->map[m] */ + u16 n; /* current index: mr->map[m]->segs[n] */ +}; + +struct ipath_sge_state { + struct ipath_sge *sg_list; /* next SGE to be used if any */ + struct ipath_sge sge; /* progress state for the current SGE */ + u8 num_sge; +}; + +int ipath_layer_register(void *(*l_add)(int, struct ipath_devdata *), + void (*l_remove)(void *), + int (*l_intr)(void *, u32), + int (*l_rcv)(void *, void *, + struct sk_buff *), + u16 rcv_opcode, + int (*l_rcv_lid)(void *, void *)); +int ipath_verbs_register(void *(*l_add)(int, struct ipath_devdata *), + void (*l_remove)(void *arg), + int (*l_piobufavail)(void *arg), + void (*l_rcv)(void *arg, void *rhdr, + void *data, u32 tlen), + void (*l_timer_cb)(void *arg)); +void ipath_layer_unregister(void); +void ipath_verbs_unregister(void); +int ipath_layer_open(struct ipath_devdata *, u32 * pktmax); +u16 ipath_layer_get_lid(struct ipath_devdata *dd); +int ipath_layer_get_mac(struct ipath_devdata *dd, u8 *); +u16 ipath_layer_get_bcast(struct ipath_devdata *dd); +u32 ipath_layer_get_cr_errpkey(struct ipath_devdata *dd); +int ipath_layer_set_linkstate(struct ipath_devdata *dd, u8 state); +int ipath_layer_set_mtu(struct ipath_devdata *, u16); +int ipath_set_sps_lid(struct ipath_devdata *, u32, u8); +int ipath_layer_send_hdr(struct ipath_devdata *dd, + struct ether_header *hdr); +int ipath_verbs_send(struct ipath_devdata *dd, u32 hdrwords, + u32 * hdr, u32 len, struct ipath_sge_state *ss); +int ipath_layer_set_piointbufavail_int(struct ipath_devdata *dd); +int ipath_layer_get_boardname(struct ipath_devdata *dd, char *name, + size_t namelen); +int ipath_layer_snapshot_counters(struct ipath_devdata *dd, u64 *swords, + u64 *rwords, u64 *spkts, u64 *rpkts, + u64 *xmit_wait); +int ipath_layer_get_counters(struct ipath_devdata *dd, + struct ipath_layer_counters *cntrs); +int ipath_layer_want_buffer(struct ipath_devdata *dd); +int ipath_layer_set_guid(struct ipath_devdata *, __be64 guid); +__be64 ipath_layer_get_guid(struct ipath_devdata *); +u32 ipath_layer_get_nguid(struct ipath_devdata *); +int ipath_layer_query_device(struct ipath_devdata *, u32 * vendor, + u32 * boardrev, u32 * majrev, u32 * minrev); +u32 ipath_layer_get_flags(struct ipath_devdata *dd); +struct device *ipath_layer_get_device(struct ipath_devdata *dd); +u16 ipath_layer_get_deviceid(struct ipath_devdata *dd); +u64 ipath_layer_get_lastibcstat(struct ipath_devdata *dd); +u32 ipath_layer_get_ibmtu(struct ipath_devdata *dd); +int ipath_layer_enable_timer(struct ipath_devdata *dd); +int ipath_layer_disable_timer(struct ipath_devdata *dd); +int ipath_layer_set_verbs_flags(struct ipath_devdata *dd, unsigned flags); +unsigned ipath_layer_get_npkeys(struct ipath_devdata *dd); +unsigned ipath_layer_get_pkey(struct ipath_devdata *dd, unsigned index); +int ipath_layer_get_pkeys(struct ipath_devdata *dd, u16 *pkeys); +int ipath_layer_set_pkeys(struct ipath_devdata *dd, u16 *pkeys); +int ipath_layer_get_linkdowndefaultstate(struct ipath_devdata *dd); +int ipath_layer_set_linkdowndefaultstate(struct ipath_devdata *dd, + int sleep); +int ipath_layer_get_phyerrthreshold(struct ipath_devdata *dd); +int ipath_layer_set_phyerrthreshold(struct ipath_devdata *dd, unsigned n); +int ipath_layer_get_overrunthreshold(struct ipath_devdata *dd); +int ipath_layer_set_overrunthreshold(struct ipath_devdata *dd, unsigned n); +u32 ipath_layer_get_rcvhdrentsize(struct ipath_devdata *dd); + +/* ipath_ether interrupt values */ +#define IPATH_LAYER_INT_IF_UP 0x2 +#define IPATH_LAYER_INT_IF_DOWN 0x4 +#define IPATH_LAYER_INT_LID 0x8 +#define IPATH_LAYER_INT_SEND_CONTINUE 0x10 +#define IPATH_LAYER_INT_BCAST 0x40 + +/* _verbs_layer.l_flags */ +#define IPATH_VERBS_KERNEL_SMA 0x1 + +extern unsigned ipath_debug; /* debugging bit mask */ + +#endif /* _IPATH_LAYER_H */ diff --git a/drivers/infiniband/hw/ipath/ipath_mad.c b/drivers/infiniband/hw/ipath/ipath_mad.c new file mode 100644 index 000000000000..f7f8391fe43f --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_mad.c @@ -0,0 +1,1352 @@ +/* + * Copyright (c) 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include "ipath_kernel.h" +#include "ipath_verbs.h" +#include "ips_common.h" + +#define IB_SMP_UNSUP_VERSION __constant_htons(0x0004) +#define IB_SMP_UNSUP_METHOD __constant_htons(0x0008) +#define IB_SMP_UNSUP_METH_ATTR __constant_htons(0x000C) +#define IB_SMP_INVALID_FIELD __constant_htons(0x001C) + +static int reply(struct ib_smp *smp) +{ + /* + * The verbs framework will handle the directed/LID route + * packet changes. + */ + smp->method = IB_MGMT_METHOD_GET_RESP; + if (smp->mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) + smp->status |= IB_SMP_DIRECTION; + return IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_REPLY; +} + +static int recv_subn_get_nodedescription(struct ib_smp *smp, + struct ib_device *ibdev) +{ + if (smp->attr_mod) + smp->status |= IB_SMP_INVALID_FIELD; + + strncpy(smp->data, ibdev->node_desc, sizeof(smp->data)); + + return reply(smp); +} + +struct nodeinfo { + u8 base_version; + u8 class_version; + u8 node_type; + u8 num_ports; + __be64 sys_guid; + __be64 node_guid; + __be64 port_guid; + __be16 partition_cap; + __be16 device_id; + __be32 revision; + u8 local_port_num; + u8 vendor_id[3]; +} __attribute__ ((packed)); + +static int recv_subn_get_nodeinfo(struct ib_smp *smp, + struct ib_device *ibdev, u8 port) +{ + struct nodeinfo *nip = (struct nodeinfo *)&smp->data; + struct ipath_devdata *dd = to_idev(ibdev)->dd; + u32 vendor, boardid, majrev, minrev; + + if (smp->attr_mod) + smp->status |= IB_SMP_INVALID_FIELD; + + nip->base_version = 1; + nip->class_version = 1; + nip->node_type = 1; /* channel adapter */ + /* + * XXX The num_ports value will need a layer function to get + * the value if we ever have more than one IB port on a chip. + * We will also need to get the GUID for the port. + */ + nip->num_ports = ibdev->phys_port_cnt; + /* This is already in network order */ + nip->sys_guid = to_idev(ibdev)->sys_image_guid; + nip->node_guid = ipath_layer_get_guid(dd); + nip->port_guid = nip->sys_guid; + nip->partition_cap = cpu_to_be16(ipath_layer_get_npkeys(dd)); + nip->device_id = cpu_to_be16(ipath_layer_get_deviceid(dd)); + ipath_layer_query_device(dd, &vendor, &boardid, &majrev, &minrev); + nip->revision = cpu_to_be32((majrev << 16) | minrev); + nip->local_port_num = port; + nip->vendor_id[0] = 0; + nip->vendor_id[1] = vendor >> 8; + nip->vendor_id[2] = vendor; + + return reply(smp); +} + +static int recv_subn_get_guidinfo(struct ib_smp *smp, + struct ib_device *ibdev) +{ + u32 startgx = 8 * be32_to_cpu(smp->attr_mod); + __be64 *p = (__be64 *) smp->data; + + /* 32 blocks of 8 64-bit GUIDs per block */ + + memset(smp->data, 0, sizeof(smp->data)); + + /* + * We only support one GUID for now. If this changes, the + * portinfo.guid_cap field needs to be updated too. + */ + if (startgx == 0) + /* The first is a copy of the read-only HW GUID. */ + *p = ipath_layer_get_guid(to_idev(ibdev)->dd); + else + smp->status |= IB_SMP_INVALID_FIELD; + + return reply(smp); +} + +struct port_info { + __be64 mkey; + __be64 gid_prefix; + __be16 lid; + __be16 sm_lid; + __be32 cap_mask; + __be16 diag_code; + __be16 mkey_lease_period; + u8 local_port_num; + u8 link_width_enabled; + u8 link_width_supported; + u8 link_width_active; + u8 linkspeed_portstate; /* 4 bits, 4 bits */ + u8 portphysstate_linkdown; /* 4 bits, 4 bits */ + u8 mkeyprot_resv_lmc; /* 2 bits, 3, 3 */ + u8 linkspeedactive_enabled; /* 4 bits, 4 bits */ + u8 neighbormtu_mastersmsl; /* 4 bits, 4 bits */ + u8 vlcap_inittype; /* 4 bits, 4 bits */ + u8 vl_high_limit; + u8 vl_arb_high_cap; + u8 vl_arb_low_cap; + u8 inittypereply_mtucap; /* 4 bits, 4 bits */ + u8 vlstallcnt_hoqlife; /* 3 bits, 5 bits */ + u8 operationalvl_pei_peo_fpi_fpo; /* 4 bits, 1, 1, 1, 1 */ + __be16 mkey_violations; + __be16 pkey_violations; + __be16 qkey_violations; + u8 guid_cap; + u8 clientrereg_resv_subnetto; /* 1 bit, 2 bits, 5 */ + u8 resv_resptimevalue; /* 3 bits, 5 bits */ + u8 localphyerrors_overrunerrors; /* 4 bits, 4 bits */ + __be16 max_credit_hint; + u8 resv; + u8 link_roundtrip_latency[3]; +} __attribute__ ((packed)); + +static int recv_subn_get_portinfo(struct ib_smp *smp, + struct ib_device *ibdev, u8 port) +{ + struct ipath_ibdev *dev; + struct port_info *pip = (struct port_info *)smp->data; + u16 lid; + u8 ibcstat; + u8 mtu; + int ret; + + if (be32_to_cpu(smp->attr_mod) > ibdev->phys_port_cnt) { + smp->status |= IB_SMP_INVALID_FIELD; + ret = reply(smp); + goto bail; + } + + dev = to_idev(ibdev); + + /* Clear all fields. Only set the non-zero fields. */ + memset(smp->data, 0, sizeof(smp->data)); + + /* Only return the mkey if the protection field allows it. */ + if (smp->method == IB_MGMT_METHOD_SET || dev->mkey == smp->mkey || + (dev->mkeyprot_resv_lmc >> 6) == 0) + pip->mkey = dev->mkey; + pip->gid_prefix = dev->gid_prefix; + lid = ipath_layer_get_lid(dev->dd); + pip->lid = lid ? cpu_to_be16(lid) : IB_LID_PERMISSIVE; + pip->sm_lid = cpu_to_be16(dev->sm_lid); + pip->cap_mask = cpu_to_be32(dev->port_cap_flags); + /* pip->diag_code; */ + pip->mkey_lease_period = cpu_to_be16(dev->mkey_lease_period); + pip->local_port_num = port; + pip->link_width_enabled = dev->link_width_enabled; + pip->link_width_supported = 3; /* 1x or 4x */ + pip->link_width_active = 2; /* 4x */ + pip->linkspeed_portstate = 0x10; /* 2.5Gbps */ + ibcstat = ipath_layer_get_lastibcstat(dev->dd); + pip->linkspeed_portstate |= ((ibcstat >> 4) & 0x3) + 1; + pip->portphysstate_linkdown = + (ipath_cvt_physportstate[ibcstat & 0xf] << 4) | + (ipath_layer_get_linkdowndefaultstate(dev->dd) ? 1 : 2); + pip->mkeyprot_resv_lmc = dev->mkeyprot_resv_lmc; + pip->linkspeedactive_enabled = 0x11; /* 2.5Gbps, 2.5Gbps */ + switch (ipath_layer_get_ibmtu(dev->dd)) { + case 4096: + mtu = IB_MTU_4096; + break; + case 2048: + mtu = IB_MTU_2048; + break; + case 1024: + mtu = IB_MTU_1024; + break; + case 512: + mtu = IB_MTU_512; + break; + case 256: + mtu = IB_MTU_256; + break; + default: /* oops, something is wrong */ + mtu = IB_MTU_2048; + break; + } + pip->neighbormtu_mastersmsl = (mtu << 4) | dev->sm_sl; + pip->vlcap_inittype = 0x10; /* VLCap = VL0, InitType = 0 */ + pip->vl_high_limit = dev->vl_high_limit; + /* pip->vl_arb_high_cap; // only one VL */ + /* pip->vl_arb_low_cap; // only one VL */ + /* InitTypeReply = 0 */ + pip->inittypereply_mtucap = IB_MTU_4096; + // HCAs ignore VLStallCount and HOQLife + /* pip->vlstallcnt_hoqlife; */ + pip->operationalvl_pei_peo_fpi_fpo = 0x10; /* OVLs = 1 */ + pip->mkey_violations = cpu_to_be16(dev->mkey_violations); + /* P_KeyViolations are counted by hardware. */ + pip->pkey_violations = + cpu_to_be16((ipath_layer_get_cr_errpkey(dev->dd) - + dev->n_pkey_violations) & 0xFFFF); + pip->qkey_violations = cpu_to_be16(dev->qkey_violations); + /* Only the hardware GUID is supported for now */ + pip->guid_cap = 1; + pip->clientrereg_resv_subnetto = dev->subnet_timeout; + /* 32.768 usec. response time (guessing) */ + pip->resv_resptimevalue = 3; + pip->localphyerrors_overrunerrors = + (ipath_layer_get_phyerrthreshold(dev->dd) << 4) | + ipath_layer_get_overrunthreshold(dev->dd); + /* pip->max_credit_hint; */ + /* pip->link_roundtrip_latency[3]; */ + + ret = reply(smp); + +bail: + return ret; +} + +static int recv_subn_get_pkeytable(struct ib_smp *smp, + struct ib_device *ibdev) +{ + u32 startpx = 32 * (be32_to_cpu(smp->attr_mod) & 0xffff); + u16 *p = (u16 *) smp->data; + __be16 *q = (__be16 *) smp->data; + + /* 64 blocks of 32 16-bit P_Key entries */ + + memset(smp->data, 0, sizeof(smp->data)); + if (startpx == 0) { + struct ipath_ibdev *dev = to_idev(ibdev); + unsigned i, n = ipath_layer_get_npkeys(dev->dd); + + ipath_layer_get_pkeys(dev->dd, p); + + for (i = 0; i < n; i++) + q[i] = cpu_to_be16(p[i]); + } else + smp->status |= IB_SMP_INVALID_FIELD; + + return reply(smp); +} + +static int recv_subn_set_guidinfo(struct ib_smp *smp, + struct ib_device *ibdev) +{ + /* The only GUID we support is the first read-only entry. */ + return recv_subn_get_guidinfo(smp, ibdev); +} + +/** + * recv_subn_set_portinfo - set port information + * @smp: the incoming SM packet + * @ibdev: the infiniband device + * @port: the port on the device + * + * Set Portinfo (see ch. 14.2.5.6). + */ +static int recv_subn_set_portinfo(struct ib_smp *smp, + struct ib_device *ibdev, u8 port) +{ + struct port_info *pip = (struct port_info *)smp->data; + struct ib_event event; + struct ipath_ibdev *dev; + u32 flags; + char clientrereg = 0; + u16 lid, smlid; + u8 lwe; + u8 lse; + u8 state; + u16 lstate; + u32 mtu; + int ret; + + if (be32_to_cpu(smp->attr_mod) > ibdev->phys_port_cnt) + goto err; + + dev = to_idev(ibdev); + event.device = ibdev; + event.element.port_num = port; + + dev->mkey = pip->mkey; + dev->gid_prefix = pip->gid_prefix; + dev->mkey_lease_period = be16_to_cpu(pip->mkey_lease_period); + + lid = be16_to_cpu(pip->lid); + if (lid != ipath_layer_get_lid(dev->dd)) { + /* Must be a valid unicast LID address. */ + if (lid == 0 || lid >= IPS_MULTICAST_LID_BASE) + goto err; + ipath_set_sps_lid(dev->dd, lid, pip->mkeyprot_resv_lmc & 7); + event.event = IB_EVENT_LID_CHANGE; + ib_dispatch_event(&event); + } + + smlid = be16_to_cpu(pip->sm_lid); + if (smlid != dev->sm_lid) { + /* Must be a valid unicast LID address. */ + if (smlid == 0 || smlid >= IPS_MULTICAST_LID_BASE) + goto err; + dev->sm_lid = smlid; + event.event = IB_EVENT_SM_CHANGE; + ib_dispatch_event(&event); + } + + /* Only 4x supported but allow 1x or 4x to be set (see 14.2.6.6). */ + lwe = pip->link_width_enabled; + if ((lwe >= 4 && lwe <= 8) || (lwe >= 0xC && lwe <= 0xFE)) + goto err; + if (lwe == 0xFF) + dev->link_width_enabled = 3; /* 1x or 4x */ + else if (lwe) + dev->link_width_enabled = lwe; + + /* Only 2.5 Gbs supported. */ + lse = pip->linkspeedactive_enabled & 0xF; + if (lse >= 2 && lse <= 0xE) + goto err; + + /* Set link down default state. */ + switch (pip->portphysstate_linkdown & 0xF) { + case 0: /* NOP */ + break; + case 1: /* SLEEP */ + if (ipath_layer_set_linkdowndefaultstate(dev->dd, 1)) + goto err; + break; + case 2: /* POLL */ + if (ipath_layer_set_linkdowndefaultstate(dev->dd, 0)) + goto err; + break; + default: + goto err; + } + + dev->mkeyprot_resv_lmc = pip->mkeyprot_resv_lmc; + dev->vl_high_limit = pip->vl_high_limit; + + switch ((pip->neighbormtu_mastersmsl >> 4) & 0xF) { + case IB_MTU_256: + mtu = 256; + break; + case IB_MTU_512: + mtu = 512; + break; + case IB_MTU_1024: + mtu = 1024; + break; + case IB_MTU_2048: + mtu = 2048; + break; + case IB_MTU_4096: + mtu = 4096; + break; + default: + /* XXX We have already partially updated our state! */ + goto err; + } + ipath_layer_set_mtu(dev->dd, mtu); + + dev->sm_sl = pip->neighbormtu_mastersmsl & 0xF; + + /* We only support VL0 */ + if (((pip->operationalvl_pei_peo_fpi_fpo >> 4) & 0xF) > 1) + goto err; + + if (pip->mkey_violations == 0) + dev->mkey_violations = 0; + + /* + * Hardware counter can't be reset so snapshot and subtract + * later. + */ + if (pip->pkey_violations == 0) + dev->n_pkey_violations = + ipath_layer_get_cr_errpkey(dev->dd); + + if (pip->qkey_violations == 0) + dev->qkey_violations = 0; + + if (ipath_layer_set_phyerrthreshold( + dev->dd, + (pip->localphyerrors_overrunerrors >> 4) & 0xF)) + goto err; + + if (ipath_layer_set_overrunthreshold( + dev->dd, + (pip->localphyerrors_overrunerrors & 0xF))) + goto err; + + dev->subnet_timeout = pip->clientrereg_resv_subnetto & 0x1F; + + if (pip->clientrereg_resv_subnetto & 0x80) { + clientrereg = 1; + event.event = IB_EVENT_LID_CHANGE; + ib_dispatch_event(&event); + } + + /* + * Do the port state change now that the other link parameters + * have been set. + * Changing the port physical state only makes sense if the link + * is down or is being set to down. + */ + state = pip->linkspeed_portstate & 0xF; + flags = ipath_layer_get_flags(dev->dd); + lstate = (pip->portphysstate_linkdown >> 4) & 0xF; + if (lstate && !(state == IB_PORT_DOWN || state == IB_PORT_NOP)) + goto err; + + /* + * Only state changes of DOWN, ARM, and ACTIVE are valid + * and must be in the correct state to take effect (see 7.2.6). + */ + switch (state) { + case IB_PORT_NOP: + if (lstate == 0) + break; + /* FALLTHROUGH */ + case IB_PORT_DOWN: + if (lstate == 0) + if (ipath_layer_get_linkdowndefaultstate(dev->dd)) + lstate = IPATH_IB_LINKDOWN_SLEEP; + else + lstate = IPATH_IB_LINKDOWN; + else if (lstate == 1) + lstate = IPATH_IB_LINKDOWN_SLEEP; + else if (lstate == 2) + lstate = IPATH_IB_LINKDOWN; + else if (lstate == 3) + lstate = IPATH_IB_LINKDOWN_DISABLE; + else + goto err; + ipath_layer_set_linkstate(dev->dd, lstate); + if (flags & IPATH_LINKACTIVE) { + event.event = IB_EVENT_PORT_ERR; + ib_dispatch_event(&event); + } + break; + case IB_PORT_ARMED: + if (!(flags & (IPATH_LINKINIT | IPATH_LINKACTIVE))) + break; + ipath_layer_set_linkstate(dev->dd, IPATH_IB_LINKARM); + if (flags & IPATH_LINKACTIVE) { + event.event = IB_EVENT_PORT_ERR; + ib_dispatch_event(&event); + } + break; + case IB_PORT_ACTIVE: + if (!(flags & IPATH_LINKARMED)) + break; + ipath_layer_set_linkstate(dev->dd, IPATH_IB_LINKACTIVE); + event.event = IB_EVENT_PORT_ACTIVE; + ib_dispatch_event(&event); + break; + default: + /* XXX We have already partially updated our state! */ + goto err; + } + + ret = recv_subn_get_portinfo(smp, ibdev, port); + + if (clientrereg) + pip->clientrereg_resv_subnetto |= 0x80; + + goto done; + +err: + smp->status |= IB_SMP_INVALID_FIELD; + ret = recv_subn_get_portinfo(smp, ibdev, port); + +done: + return ret; +} + +static int recv_subn_set_pkeytable(struct ib_smp *smp, + struct ib_device *ibdev) +{ + u32 startpx = 32 * (be32_to_cpu(smp->attr_mod) & 0xffff); + __be16 *p = (__be16 *) smp->data; + u16 *q = (u16 *) smp->data; + struct ipath_ibdev *dev = to_idev(ibdev); + unsigned i, n = ipath_layer_get_npkeys(dev->dd); + + for (i = 0; i < n; i++) + q[i] = be16_to_cpu(p[i]); + + if (startpx != 0 || + ipath_layer_set_pkeys(dev->dd, q) != 0) + smp->status |= IB_SMP_INVALID_FIELD; + + return recv_subn_get_pkeytable(smp, ibdev); +} + +#define IB_PMA_CLASS_PORT_INFO __constant_htons(0x0001) +#define IB_PMA_PORT_SAMPLES_CONTROL __constant_htons(0x0010) +#define IB_PMA_PORT_SAMPLES_RESULT __constant_htons(0x0011) +#define IB_PMA_PORT_COUNTERS __constant_htons(0x0012) +#define IB_PMA_PORT_COUNTERS_EXT __constant_htons(0x001D) +#define IB_PMA_PORT_SAMPLES_RESULT_EXT __constant_htons(0x001E) + +struct ib_perf { + u8 base_version; + u8 mgmt_class; + u8 class_version; + u8 method; + __be16 status; + __be16 unused; + __be64 tid; + __be16 attr_id; + __be16 resv; + __be32 attr_mod; + u8 reserved[40]; + u8 data[192]; +} __attribute__ ((packed)); + +struct ib_pma_classportinfo { + u8 base_version; + u8 class_version; + __be16 cap_mask; + u8 reserved[3]; + u8 resp_time_value; /* only lower 5 bits */ + union ib_gid redirect_gid; + __be32 redirect_tc_sl_fl; /* 8, 4, 20 bits respectively */ + __be16 redirect_lid; + __be16 redirect_pkey; + __be32 redirect_qp; /* only lower 24 bits */ + __be32 redirect_qkey; + union ib_gid trap_gid; + __be32 trap_tc_sl_fl; /* 8, 4, 20 bits respectively */ + __be16 trap_lid; + __be16 trap_pkey; + __be32 trap_hl_qp; /* 8, 24 bits respectively */ + __be32 trap_qkey; +} __attribute__ ((packed)); + +struct ib_pma_portsamplescontrol { + u8 opcode; + u8 port_select; + u8 tick; + u8 counter_width; /* only lower 3 bits */ + __be32 counter_mask0_9; /* 2, 10 * 3, bits */ + __be16 counter_mask10_14; /* 1, 5 * 3, bits */ + u8 sample_mechanisms; + u8 sample_status; /* only lower 2 bits */ + __be64 option_mask; + __be64 vendor_mask; + __be32 sample_start; + __be32 sample_interval; + __be16 tag; + __be16 counter_select[15]; +} __attribute__ ((packed)); + +struct ib_pma_portsamplesresult { + __be16 tag; + __be16 sample_status; /* only lower 2 bits */ + __be32 counter[15]; +} __attribute__ ((packed)); + +struct ib_pma_portsamplesresult_ext { + __be16 tag; + __be16 sample_status; /* only lower 2 bits */ + __be32 extended_width; /* only upper 2 bits */ + __be64 counter[15]; +} __attribute__ ((packed)); + +struct ib_pma_portcounters { + u8 reserved; + u8 port_select; + __be16 counter_select; + __be16 symbol_error_counter; + u8 link_error_recovery_counter; + u8 link_downed_counter; + __be16 port_rcv_errors; + __be16 port_rcv_remphys_errors; + __be16 port_rcv_switch_relay_errors; + __be16 port_xmit_discards; + u8 port_xmit_constraint_errors; + u8 port_rcv_constraint_errors; + u8 reserved1; + u8 lli_ebor_errors; /* 4, 4, bits */ + __be16 reserved2; + __be16 vl15_dropped; + __be32 port_xmit_data; + __be32 port_rcv_data; + __be32 port_xmit_packets; + __be32 port_rcv_packets; +} __attribute__ ((packed)); + +#define IB_PMA_SEL_SYMBOL_ERROR __constant_htons(0x0001) +#define IB_PMA_SEL_LINK_ERROR_RECOVERY __constant_htons(0x0002) +#define IB_PMA_SEL_LINK_DOWNED __constant_htons(0x0004) +#define IB_PMA_SEL_PORT_RCV_ERRORS __constant_htons(0x0008) +#define IB_PMA_SEL_PORT_RCV_REMPHYS_ERRORS __constant_htons(0x0010) +#define IB_PMA_SEL_PORT_XMIT_DISCARDS __constant_htons(0x0040) +#define IB_PMA_SEL_PORT_XMIT_DATA __constant_htons(0x1000) +#define IB_PMA_SEL_PORT_RCV_DATA __constant_htons(0x2000) +#define IB_PMA_SEL_PORT_XMIT_PACKETS __constant_htons(0x4000) +#define IB_PMA_SEL_PORT_RCV_PACKETS __constant_htons(0x8000) + +struct ib_pma_portcounters_ext { + u8 reserved; + u8 port_select; + __be16 counter_select; + __be32 reserved1; + __be64 port_xmit_data; + __be64 port_rcv_data; + __be64 port_xmit_packets; + __be64 port_rcv_packets; + __be64 port_unicast_xmit_packets; + __be64 port_unicast_rcv_packets; + __be64 port_multicast_xmit_packets; + __be64 port_multicast_rcv_packets; +} __attribute__ ((packed)); + +#define IB_PMA_SELX_PORT_XMIT_DATA __constant_htons(0x0001) +#define IB_PMA_SELX_PORT_RCV_DATA __constant_htons(0x0002) +#define IB_PMA_SELX_PORT_XMIT_PACKETS __constant_htons(0x0004) +#define IB_PMA_SELX_PORT_RCV_PACKETS __constant_htons(0x0008) +#define IB_PMA_SELX_PORT_UNI_XMIT_PACKETS __constant_htons(0x0010) +#define IB_PMA_SELX_PORT_UNI_RCV_PACKETS __constant_htons(0x0020) +#define IB_PMA_SELX_PORT_MULTI_XMIT_PACKETS __constant_htons(0x0040) +#define IB_PMA_SELX_PORT_MULTI_RCV_PACKETS __constant_htons(0x0080) + +static int recv_pma_get_classportinfo(struct ib_perf *pmp) +{ + struct ib_pma_classportinfo *p = + (struct ib_pma_classportinfo *)pmp->data; + + memset(pmp->data, 0, sizeof(pmp->data)); + + if (pmp->attr_mod != 0) + pmp->status |= IB_SMP_INVALID_FIELD; + + /* Indicate AllPortSelect is valid (only one port anyway) */ + p->cap_mask = __constant_cpu_to_be16(1 << 8); + p->base_version = 1; + p->class_version = 1; + /* + * Expected response time is 4.096 usec. * 2^18 == 1.073741824 + * sec. + */ + p->resp_time_value = 18; + + return reply((struct ib_smp *) pmp); +} + +/* + * The PortSamplesControl.CounterMasks field is an array of 3 bit fields + * which specify the N'th counter's capabilities. See ch. 16.1.3.2. + * We support 5 counters which only count the mandatory quantities. + */ +#define COUNTER_MASK(q, n) (q << ((9 - n) * 3)) +#define COUNTER_MASK0_9 \ + __constant_cpu_to_be32(COUNTER_MASK(1, 0) | \ + COUNTER_MASK(1, 1) | \ + COUNTER_MASK(1, 2) | \ + COUNTER_MASK(1, 3) | \ + COUNTER_MASK(1, 4)) + +static int recv_pma_get_portsamplescontrol(struct ib_perf *pmp, + struct ib_device *ibdev, u8 port) +{ + struct ib_pma_portsamplescontrol *p = + (struct ib_pma_portsamplescontrol *)pmp->data; + struct ipath_ibdev *dev = to_idev(ibdev); + unsigned long flags; + u8 port_select = p->port_select; + + memset(pmp->data, 0, sizeof(pmp->data)); + + p->port_select = port_select; + if (pmp->attr_mod != 0 || + (port_select != port && port_select != 0xFF)) + pmp->status |= IB_SMP_INVALID_FIELD; + /* + * Ticks are 10x the link transfer period which for 2.5Gbs is 4 + * nsec. 0 == 4 nsec., 1 == 8 nsec., ..., 255 == 1020 nsec. Sample + * intervals are counted in ticks. Since we use Linux timers, that + * count in jiffies, we can't sample for less than 1000 ticks if HZ + * == 1000 (4000 ticks if HZ is 250). + */ + /* XXX This is WRONG. */ + p->tick = 250; /* 1 usec. */ + p->counter_width = 4; /* 32 bit counters */ + p->counter_mask0_9 = COUNTER_MASK0_9; + spin_lock_irqsave(&dev->pending_lock, flags); + p->sample_status = dev->pma_sample_status; + p->sample_start = cpu_to_be32(dev->pma_sample_start); + p->sample_interval = cpu_to_be32(dev->pma_sample_interval); + p->tag = cpu_to_be16(dev->pma_tag); + p->counter_select[0] = dev->pma_counter_select[0]; + p->counter_select[1] = dev->pma_counter_select[1]; + p->counter_select[2] = dev->pma_counter_select[2]; + p->counter_select[3] = dev->pma_counter_select[3]; + p->counter_select[4] = dev->pma_counter_select[4]; + spin_unlock_irqrestore(&dev->pending_lock, flags); + + return reply((struct ib_smp *) pmp); +} + +static int recv_pma_set_portsamplescontrol(struct ib_perf *pmp, + struct ib_device *ibdev, u8 port) +{ + struct ib_pma_portsamplescontrol *p = + (struct ib_pma_portsamplescontrol *)pmp->data; + struct ipath_ibdev *dev = to_idev(ibdev); + unsigned long flags; + u32 start; + int ret; + + if (pmp->attr_mod != 0 || + (p->port_select != port && p->port_select != 0xFF)) { + pmp->status |= IB_SMP_INVALID_FIELD; + ret = reply((struct ib_smp *) pmp); + goto bail; + } + + start = be32_to_cpu(p->sample_start); + if (start != 0) { + spin_lock_irqsave(&dev->pending_lock, flags); + if (dev->pma_sample_status == IB_PMA_SAMPLE_STATUS_DONE) { + dev->pma_sample_status = + IB_PMA_SAMPLE_STATUS_STARTED; + dev->pma_sample_start = start; + dev->pma_sample_interval = + be32_to_cpu(p->sample_interval); + dev->pma_tag = be16_to_cpu(p->tag); + if (p->counter_select[0]) + dev->pma_counter_select[0] = + p->counter_select[0]; + if (p->counter_select[1]) + dev->pma_counter_select[1] = + p->counter_select[1]; + if (p->counter_select[2]) + dev->pma_counter_select[2] = + p->counter_select[2]; + if (p->counter_select[3]) + dev->pma_counter_select[3] = + p->counter_select[3]; + if (p->counter_select[4]) + dev->pma_counter_select[4] = + p->counter_select[4]; + } + spin_unlock_irqrestore(&dev->pending_lock, flags); + } + ret = recv_pma_get_portsamplescontrol(pmp, ibdev, port); + +bail: + return ret; +} + +static u64 get_counter(struct ipath_ibdev *dev, __be16 sel) +{ + u64 ret; + + switch (sel) { + case IB_PMA_PORT_XMIT_DATA: + ret = dev->ipath_sword; + break; + case IB_PMA_PORT_RCV_DATA: + ret = dev->ipath_rword; + break; + case IB_PMA_PORT_XMIT_PKTS: + ret = dev->ipath_spkts; + break; + case IB_PMA_PORT_RCV_PKTS: + ret = dev->ipath_rpkts; + break; + case IB_PMA_PORT_XMIT_WAIT: + ret = dev->ipath_xmit_wait; + break; + default: + ret = 0; + } + + return ret; +} + +static int recv_pma_get_portsamplesresult(struct ib_perf *pmp, + struct ib_device *ibdev) +{ + struct ib_pma_portsamplesresult *p = + (struct ib_pma_portsamplesresult *)pmp->data; + struct ipath_ibdev *dev = to_idev(ibdev); + int i; + + memset(pmp->data, 0, sizeof(pmp->data)); + p->tag = cpu_to_be16(dev->pma_tag); + p->sample_status = cpu_to_be16(dev->pma_sample_status); + for (i = 0; i < ARRAY_SIZE(dev->pma_counter_select); i++) + p->counter[i] = cpu_to_be32( + get_counter(dev, dev->pma_counter_select[i])); + + return reply((struct ib_smp *) pmp); +} + +static int recv_pma_get_portsamplesresult_ext(struct ib_perf *pmp, + struct ib_device *ibdev) +{ + struct ib_pma_portsamplesresult_ext *p = + (struct ib_pma_portsamplesresult_ext *)pmp->data; + struct ipath_ibdev *dev = to_idev(ibdev); + int i; + + memset(pmp->data, 0, sizeof(pmp->data)); + p->tag = cpu_to_be16(dev->pma_tag); + p->sample_status = cpu_to_be16(dev->pma_sample_status); + /* 64 bits */ + p->extended_width = __constant_cpu_to_be32(0x80000000); + for (i = 0; i < ARRAY_SIZE(dev->pma_counter_select); i++) + p->counter[i] = cpu_to_be64( + get_counter(dev, dev->pma_counter_select[i])); + + return reply((struct ib_smp *) pmp); +} + +static int recv_pma_get_portcounters(struct ib_perf *pmp, + struct ib_device *ibdev, u8 port) +{ + struct ib_pma_portcounters *p = (struct ib_pma_portcounters *) + pmp->data; + struct ipath_ibdev *dev = to_idev(ibdev); + struct ipath_layer_counters cntrs; + u8 port_select = p->port_select; + + ipath_layer_get_counters(dev->dd, &cntrs); + + /* Adjust counters for any resets done. */ + cntrs.symbol_error_counter -= dev->n_symbol_error_counter; + cntrs.link_error_recovery_counter -= + dev->n_link_error_recovery_counter; + cntrs.link_downed_counter -= dev->n_link_downed_counter; + cntrs.port_rcv_errors += dev->rcv_errors; + cntrs.port_rcv_errors -= dev->n_port_rcv_errors; + cntrs.port_rcv_remphys_errors -= dev->n_port_rcv_remphys_errors; + cntrs.port_xmit_discards -= dev->n_port_xmit_discards; + cntrs.port_xmit_data -= dev->n_port_xmit_data; + cntrs.port_rcv_data -= dev->n_port_rcv_data; + cntrs.port_xmit_packets -= dev->n_port_xmit_packets; + cntrs.port_rcv_packets -= dev->n_port_rcv_packets; + + memset(pmp->data, 0, sizeof(pmp->data)); + + p->port_select = port_select; + if (pmp->attr_mod != 0 || + (port_select != port && port_select != 0xFF)) + pmp->status |= IB_SMP_INVALID_FIELD; + + if (cntrs.symbol_error_counter > 0xFFFFUL) + p->symbol_error_counter = __constant_cpu_to_be16(0xFFFF); + else + p->symbol_error_counter = + cpu_to_be16((u16)cntrs.symbol_error_counter); + if (cntrs.link_error_recovery_counter > 0xFFUL) + p->link_error_recovery_counter = 0xFF; + else + p->link_error_recovery_counter = + (u8)cntrs.link_error_recovery_counter; + if (cntrs.link_downed_counter > 0xFFUL) + p->link_downed_counter = 0xFF; + else + p->link_downed_counter = (u8)cntrs.link_downed_counter; + if (cntrs.port_rcv_errors > 0xFFFFUL) + p->port_rcv_errors = __constant_cpu_to_be16(0xFFFF); + else + p->port_rcv_errors = + cpu_to_be16((u16) cntrs.port_rcv_errors); + if (cntrs.port_rcv_remphys_errors > 0xFFFFUL) + p->port_rcv_remphys_errors = __constant_cpu_to_be16(0xFFFF); + else + p->port_rcv_remphys_errors = + cpu_to_be16((u16)cntrs.port_rcv_remphys_errors); + if (cntrs.port_xmit_discards > 0xFFFFUL) + p->port_xmit_discards = __constant_cpu_to_be16(0xFFFF); + else + p->port_xmit_discards = + cpu_to_be16((u16)cntrs.port_xmit_discards); + if (cntrs.port_xmit_data > 0xFFFFFFFFUL) + p->port_xmit_data = __constant_cpu_to_be32(0xFFFFFFFF); + else + p->port_xmit_data = cpu_to_be32((u32)cntrs.port_xmit_data); + if (cntrs.port_rcv_data > 0xFFFFFFFFUL) + p->port_rcv_data = __constant_cpu_to_be32(0xFFFFFFFF); + else + p->port_rcv_data = cpu_to_be32((u32)cntrs.port_rcv_data); + if (cntrs.port_xmit_packets > 0xFFFFFFFFUL) + p->port_xmit_packets = __constant_cpu_to_be32(0xFFFFFFFF); + else + p->port_xmit_packets = + cpu_to_be32((u32)cntrs.port_xmit_packets); + if (cntrs.port_rcv_packets > 0xFFFFFFFFUL) + p->port_rcv_packets = __constant_cpu_to_be32(0xFFFFFFFF); + else + p->port_rcv_packets = + cpu_to_be32((u32) cntrs.port_rcv_packets); + + return reply((struct ib_smp *) pmp); +} + +static int recv_pma_get_portcounters_ext(struct ib_perf *pmp, + struct ib_device *ibdev, u8 port) +{ + struct ib_pma_portcounters_ext *p = + (struct ib_pma_portcounters_ext *)pmp->data; + struct ipath_ibdev *dev = to_idev(ibdev); + u64 swords, rwords, spkts, rpkts, xwait; + u8 port_select = p->port_select; + + ipath_layer_snapshot_counters(dev->dd, &swords, &rwords, &spkts, + &rpkts, &xwait); + + /* Adjust counters for any resets done. */ + swords -= dev->n_port_xmit_data; + rwords -= dev->n_port_rcv_data; + spkts -= dev->n_port_xmit_packets; + rpkts -= dev->n_port_rcv_packets; + + memset(pmp->data, 0, sizeof(pmp->data)); + + p->port_select = port_select; + if (pmp->attr_mod != 0 || + (port_select != port && port_select != 0xFF)) + pmp->status |= IB_SMP_INVALID_FIELD; + + p->port_xmit_data = cpu_to_be64(swords); + p->port_rcv_data = cpu_to_be64(rwords); + p->port_xmit_packets = cpu_to_be64(spkts); + p->port_rcv_packets = cpu_to_be64(rpkts); + p->port_unicast_xmit_packets = cpu_to_be64(dev->n_unicast_xmit); + p->port_unicast_rcv_packets = cpu_to_be64(dev->n_unicast_rcv); + p->port_multicast_xmit_packets = cpu_to_be64(dev->n_multicast_xmit); + p->port_multicast_rcv_packets = cpu_to_be64(dev->n_multicast_rcv); + + return reply((struct ib_smp *) pmp); +} + +static int recv_pma_set_portcounters(struct ib_perf *pmp, + struct ib_device *ibdev, u8 port) +{ + struct ib_pma_portcounters *p = (struct ib_pma_portcounters *) + pmp->data; + struct ipath_ibdev *dev = to_idev(ibdev); + struct ipath_layer_counters cntrs; + + /* + * Since the HW doesn't support clearing counters, we save the + * current count and subtract it from future responses. + */ + ipath_layer_get_counters(dev->dd, &cntrs); + + if (p->counter_select & IB_PMA_SEL_SYMBOL_ERROR) + dev->n_symbol_error_counter = cntrs.symbol_error_counter; + + if (p->counter_select & IB_PMA_SEL_LINK_ERROR_RECOVERY) + dev->n_link_error_recovery_counter = + cntrs.link_error_recovery_counter; + + if (p->counter_select & IB_PMA_SEL_LINK_DOWNED) + dev->n_link_downed_counter = cntrs.link_downed_counter; + + if (p->counter_select & IB_PMA_SEL_PORT_RCV_ERRORS) + dev->n_port_rcv_errors = + cntrs.port_rcv_errors + dev->rcv_errors; + + if (p->counter_select & IB_PMA_SEL_PORT_RCV_REMPHYS_ERRORS) + dev->n_port_rcv_remphys_errors = + cntrs.port_rcv_remphys_errors; + + if (p->counter_select & IB_PMA_SEL_PORT_XMIT_DISCARDS) + dev->n_port_xmit_discards = cntrs.port_xmit_discards; + + if (p->counter_select & IB_PMA_SEL_PORT_XMIT_DATA) + dev->n_port_xmit_data = cntrs.port_xmit_data; + + if (p->counter_select & IB_PMA_SEL_PORT_RCV_DATA) + dev->n_port_rcv_data = cntrs.port_rcv_data; + + if (p->counter_select & IB_PMA_SEL_PORT_XMIT_PACKETS) + dev->n_port_xmit_packets = cntrs.port_xmit_packets; + + if (p->counter_select & IB_PMA_SEL_PORT_RCV_PACKETS) + dev->n_port_rcv_packets = cntrs.port_rcv_packets; + + return recv_pma_get_portcounters(pmp, ibdev, port); +} + +static int recv_pma_set_portcounters_ext(struct ib_perf *pmp, + struct ib_device *ibdev, u8 port) +{ + struct ib_pma_portcounters *p = (struct ib_pma_portcounters *) + pmp->data; + struct ipath_ibdev *dev = to_idev(ibdev); + u64 swords, rwords, spkts, rpkts, xwait; + + ipath_layer_snapshot_counters(dev->dd, &swords, &rwords, &spkts, + &rpkts, &xwait); + + if (p->counter_select & IB_PMA_SELX_PORT_XMIT_DATA) + dev->n_port_xmit_data = swords; + + if (p->counter_select & IB_PMA_SELX_PORT_RCV_DATA) + dev->n_port_rcv_data = rwords; + + if (p->counter_select & IB_PMA_SELX_PORT_XMIT_PACKETS) + dev->n_port_xmit_packets = spkts; + + if (p->counter_select & IB_PMA_SELX_PORT_RCV_PACKETS) + dev->n_port_rcv_packets = rpkts; + + if (p->counter_select & IB_PMA_SELX_PORT_UNI_XMIT_PACKETS) + dev->n_unicast_xmit = 0; + + if (p->counter_select & IB_PMA_SELX_PORT_UNI_RCV_PACKETS) + dev->n_unicast_rcv = 0; + + if (p->counter_select & IB_PMA_SELX_PORT_MULTI_XMIT_PACKETS) + dev->n_multicast_xmit = 0; + + if (p->counter_select & IB_PMA_SELX_PORT_MULTI_RCV_PACKETS) + dev->n_multicast_rcv = 0; + + return recv_pma_get_portcounters_ext(pmp, ibdev, port); +} + +static int process_subn(struct ib_device *ibdev, int mad_flags, + u8 port_num, struct ib_mad *in_mad, + struct ib_mad *out_mad) +{ + struct ib_smp *smp = (struct ib_smp *)out_mad; + struct ipath_ibdev *dev = to_idev(ibdev); + int ret; + + *out_mad = *in_mad; + if (smp->class_version != 1) { + smp->status |= IB_SMP_UNSUP_VERSION; + ret = reply(smp); + goto bail; + } + + /* Is the mkey in the process of expiring? */ + if (dev->mkey_lease_timeout && jiffies >= dev->mkey_lease_timeout) { + /* Clear timeout and mkey protection field. */ + dev->mkey_lease_timeout = 0; + dev->mkeyprot_resv_lmc &= 0x3F; + } + + /* + * M_Key checking depends on + * Portinfo:M_Key_protect_bits + */ + if ((mad_flags & IB_MAD_IGNORE_MKEY) == 0 && dev->mkey != 0 && + dev->mkey != smp->mkey && + (smp->method == IB_MGMT_METHOD_SET || + (smp->method == IB_MGMT_METHOD_GET && + (dev->mkeyprot_resv_lmc >> 7) != 0))) { + if (dev->mkey_violations != 0xFFFF) + ++dev->mkey_violations; + if (dev->mkey_lease_timeout || + dev->mkey_lease_period == 0) { + ret = IB_MAD_RESULT_SUCCESS | + IB_MAD_RESULT_CONSUMED; + goto bail; + } + dev->mkey_lease_timeout = jiffies + + dev->mkey_lease_period * HZ; + /* Future: Generate a trap notice. */ + ret = IB_MAD_RESULT_SUCCESS | IB_MAD_RESULT_CONSUMED; + goto bail; + } else if (dev->mkey_lease_timeout) + dev->mkey_lease_timeout = 0; + + switch (smp->method) { + case IB_MGMT_METHOD_GET: + switch (smp->attr_id) { + case IB_SMP_ATTR_NODE_DESC: + ret = recv_subn_get_nodedescription(smp, ibdev); + goto bail; + case IB_SMP_ATTR_NODE_INFO: + ret = recv_subn_get_nodeinfo(smp, ibdev, port_num); + goto bail; + case IB_SMP_ATTR_GUID_INFO: + ret = recv_subn_get_guidinfo(smp, ibdev); + goto bail; + case IB_SMP_ATTR_PORT_INFO: + ret = recv_subn_get_portinfo(smp, ibdev, port_num); + goto bail; + case IB_SMP_ATTR_PKEY_TABLE: + ret = recv_subn_get_pkeytable(smp, ibdev); + goto bail; + case IB_SMP_ATTR_SM_INFO: + if (dev->port_cap_flags & IB_PORT_SM_DISABLED) { + ret = IB_MAD_RESULT_SUCCESS | + IB_MAD_RESULT_CONSUMED; + goto bail; + } + if (dev->port_cap_flags & IB_PORT_SM) { + ret = IB_MAD_RESULT_SUCCESS; + goto bail; + } + /* FALLTHROUGH */ + default: + smp->status |= IB_SMP_UNSUP_METH_ATTR; + ret = reply(smp); + goto bail; + } + + case IB_MGMT_METHOD_SET: + switch (smp->attr_id) { + case IB_SMP_ATTR_GUID_INFO: + ret = recv_subn_set_guidinfo(smp, ibdev); + goto bail; + case IB_SMP_ATTR_PORT_INFO: + ret = recv_subn_set_portinfo(smp, ibdev, port_num); + goto bail; + case IB_SMP_ATTR_PKEY_TABLE: + ret = recv_subn_set_pkeytable(smp, ibdev); + goto bail; + case IB_SMP_ATTR_SM_INFO: + if (dev->port_cap_flags & IB_PORT_SM_DISABLED) { + ret = IB_MAD_RESULT_SUCCESS | + IB_MAD_RESULT_CONSUMED; + goto bail; + } + if (dev->port_cap_flags & IB_PORT_SM) { + ret = IB_MAD_RESULT_SUCCESS; + goto bail; + } + /* FALLTHROUGH */ + default: + smp->status |= IB_SMP_UNSUP_METH_ATTR; + ret = reply(smp); + goto bail; + } + + case IB_MGMT_METHOD_GET_RESP: + /* + * The ib_mad module will call us to process responses + * before checking for other consumers. + * Just tell the caller to process it normally. + */ + ret = IB_MAD_RESULT_FAILURE; + goto bail; + default: + smp->status |= IB_SMP_UNSUP_METHOD; + ret = reply(smp); + } + +bail: + return ret; +} + +static int process_perf(struct ib_device *ibdev, u8 port_num, + struct ib_mad *in_mad, + struct ib_mad *out_mad) +{ + struct ib_perf *pmp = (struct ib_perf *)out_mad; + int ret; + + *out_mad = *in_mad; + if (pmp->class_version != 1) { + pmp->status |= IB_SMP_UNSUP_VERSION; + ret = reply((struct ib_smp *) pmp); + goto bail; + } + + switch (pmp->method) { + case IB_MGMT_METHOD_GET: + switch (pmp->attr_id) { + case IB_PMA_CLASS_PORT_INFO: + ret = recv_pma_get_classportinfo(pmp); + goto bail; + case IB_PMA_PORT_SAMPLES_CONTROL: + ret = recv_pma_get_portsamplescontrol(pmp, ibdev, + port_num); + goto bail; + case IB_PMA_PORT_SAMPLES_RESULT: + ret = recv_pma_get_portsamplesresult(pmp, ibdev); + goto bail; + case IB_PMA_PORT_SAMPLES_RESULT_EXT: + ret = recv_pma_get_portsamplesresult_ext(pmp, + ibdev); + goto bail; + case IB_PMA_PORT_COUNTERS: + ret = recv_pma_get_portcounters(pmp, ibdev, + port_num); + goto bail; + case IB_PMA_PORT_COUNTERS_EXT: + ret = recv_pma_get_portcounters_ext(pmp, ibdev, + port_num); + goto bail; + default: + pmp->status |= IB_SMP_UNSUP_METH_ATTR; + ret = reply((struct ib_smp *) pmp); + goto bail; + } + + case IB_MGMT_METHOD_SET: + switch (pmp->attr_id) { + case IB_PMA_PORT_SAMPLES_CONTROL: + ret = recv_pma_set_portsamplescontrol(pmp, ibdev, + port_num); + goto bail; + case IB_PMA_PORT_COUNTERS: + ret = recv_pma_set_portcounters(pmp, ibdev, + port_num); + goto bail; + case IB_PMA_PORT_COUNTERS_EXT: + ret = recv_pma_set_portcounters_ext(pmp, ibdev, + port_num); + goto bail; + default: + pmp->status |= IB_SMP_UNSUP_METH_ATTR; + ret = reply((struct ib_smp *) pmp); + goto bail; + } + + case IB_MGMT_METHOD_GET_RESP: + /* + * The ib_mad module will call us to process responses + * before checking for other consumers. + * Just tell the caller to process it normally. + */ + ret = IB_MAD_RESULT_FAILURE; + goto bail; + default: + pmp->status |= IB_SMP_UNSUP_METHOD; + ret = reply((struct ib_smp *) pmp); + } + +bail: + return ret; +} + +/** + * ipath_process_mad - process an incoming MAD packet + * @ibdev: the infiniband device this packet came in on + * @mad_flags: MAD flags + * @port_num: the port number this packet came in on + * @in_wc: the work completion entry for this packet + * @in_grh: the global route header for this packet + * @in_mad: the incoming MAD + * @out_mad: any outgoing MAD reply + * + * Returns IB_MAD_RESULT_SUCCESS if this is a MAD that we are not + * interested in processing. + * + * Note that the verbs framework has already done the MAD sanity checks, + * and hop count/pointer updating for IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE + * MADs. + * + * This is called by the ib_mad module. + */ +int ipath_process_mad(struct ib_device *ibdev, int mad_flags, u8 port_num, + struct ib_wc *in_wc, struct ib_grh *in_grh, + struct ib_mad *in_mad, struct ib_mad *out_mad) +{ + struct ipath_ibdev *dev = to_idev(ibdev); + int ret; + + /* + * Snapshot current HW counters to "clear" them. + * This should be done when the driver is loaded except that for + * some reason we get a zillion errors when brining up the link. + */ + if (dev->rcv_errors == 0) { + struct ipath_layer_counters cntrs; + + ipath_layer_get_counters(to_idev(ibdev)->dd, &cntrs); + dev->rcv_errors++; + dev->n_symbol_error_counter = cntrs.symbol_error_counter; + dev->n_link_error_recovery_counter = + cntrs.link_error_recovery_counter; + dev->n_link_downed_counter = cntrs.link_downed_counter; + dev->n_port_rcv_errors = cntrs.port_rcv_errors + 1; + dev->n_port_rcv_remphys_errors = + cntrs.port_rcv_remphys_errors; + dev->n_port_xmit_discards = cntrs.port_xmit_discards; + dev->n_port_xmit_data = cntrs.port_xmit_data; + dev->n_port_rcv_data = cntrs.port_rcv_data; + dev->n_port_xmit_packets = cntrs.port_xmit_packets; + dev->n_port_rcv_packets = cntrs.port_rcv_packets; + } + switch (in_mad->mad_hdr.mgmt_class) { + case IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE: + case IB_MGMT_CLASS_SUBN_LID_ROUTED: + ret = process_subn(ibdev, mad_flags, port_num, + in_mad, out_mad); + goto bail; + case IB_MGMT_CLASS_PERF_MGMT: + ret = process_perf(ibdev, port_num, in_mad, out_mad); + goto bail; + default: + ret = IB_MAD_RESULT_SUCCESS; + } + +bail: + return ret; +} diff --git a/drivers/infiniband/hw/ipath/ipath_mr.c b/drivers/infiniband/hw/ipath/ipath_mr.c new file mode 100644 index 000000000000..69ffec66d45d --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_mr.c @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include "ipath_verbs.h" + +/** + * ipath_get_dma_mr - get a DMA memory region + * @pd: protection domain for this memory region + * @acc: access flags + * + * Returns the memory region on success, otherwise returns an errno. + */ +struct ib_mr *ipath_get_dma_mr(struct ib_pd *pd, int acc) +{ + struct ipath_mr *mr; + struct ib_mr *ret; + + mr = kzalloc(sizeof *mr, GFP_KERNEL); + if (!mr) { + ret = ERR_PTR(-ENOMEM); + goto bail; + } + + mr->mr.access_flags = acc; + ret = &mr->ibmr; + +bail: + return ret; +} + +static struct ipath_mr *alloc_mr(int count, + struct ipath_lkey_table *lk_table) +{ + struct ipath_mr *mr; + int m, i = 0; + + /* Allocate struct plus pointers to first level page tables. */ + m = (count + IPATH_SEGSZ - 1) / IPATH_SEGSZ; + mr = kmalloc(sizeof *mr + m * sizeof mr->mr.map[0], GFP_KERNEL); + if (!mr) + goto done; + + /* Allocate first level page tables. */ + for (; i < m; i++) { + mr->mr.map[i] = kmalloc(sizeof *mr->mr.map[0], GFP_KERNEL); + if (!mr->mr.map[i]) + goto bail; + } + mr->mr.mapsz = m; + + /* + * ib_reg_phys_mr() will initialize mr->ibmr except for + * lkey and rkey. + */ + if (!ipath_alloc_lkey(lk_table, &mr->mr)) + goto bail; + mr->ibmr.rkey = mr->ibmr.lkey = mr->mr.lkey; + + goto done; + +bail: + while (i) { + i--; + kfree(mr->mr.map[i]); + } + kfree(mr); + mr = NULL; + +done: + return mr; +} + +/** + * ipath_reg_phys_mr - register a physical memory region + * @pd: protection domain for this memory region + * @buffer_list: pointer to the list of physical buffers to register + * @num_phys_buf: the number of physical buffers to register + * @iova_start: the starting address passed over IB which maps to this MR + * + * Returns the memory region on success, otherwise returns an errno. + */ +struct ib_mr *ipath_reg_phys_mr(struct ib_pd *pd, + struct ib_phys_buf *buffer_list, + int num_phys_buf, int acc, u64 *iova_start) +{ + struct ipath_mr *mr; + int n, m, i; + struct ib_mr *ret; + + mr = alloc_mr(num_phys_buf, &to_idev(pd->device)->lk_table); + if (mr == NULL) { + ret = ERR_PTR(-ENOMEM); + goto bail; + } + + mr->mr.user_base = *iova_start; + mr->mr.iova = *iova_start; + mr->mr.length = 0; + mr->mr.offset = 0; + mr->mr.access_flags = acc; + mr->mr.max_segs = num_phys_buf; + + m = 0; + n = 0; + for (i = 0; i < num_phys_buf; i++) { + mr->mr.map[m]->segs[n].vaddr = + phys_to_virt(buffer_list[i].addr); + mr->mr.map[m]->segs[n].length = buffer_list[i].size; + mr->mr.length += buffer_list[i].size; + n++; + if (n == IPATH_SEGSZ) { + m++; + n = 0; + } + } + + ret = &mr->ibmr; + +bail: + return ret; +} + +/** + * ipath_reg_user_mr - register a userspace memory region + * @pd: protection domain for this memory region + * @region: the user memory region + * @mr_access_flags: access flags for this memory region + * @udata: unused by the InfiniPath driver + * + * Returns the memory region on success, otherwise returns an errno. + */ +struct ib_mr *ipath_reg_user_mr(struct ib_pd *pd, struct ib_umem *region, + int mr_access_flags, struct ib_udata *udata) +{ + struct ipath_mr *mr; + struct ib_umem_chunk *chunk; + int n, m, i; + struct ib_mr *ret; + + n = 0; + list_for_each_entry(chunk, ®ion->chunk_list, list) + n += chunk->nents; + + mr = alloc_mr(n, &to_idev(pd->device)->lk_table); + if (!mr) { + ret = ERR_PTR(-ENOMEM); + goto bail; + } + + mr->mr.user_base = region->user_base; + mr->mr.iova = region->virt_base; + mr->mr.length = region->length; + mr->mr.offset = region->offset; + mr->mr.access_flags = mr_access_flags; + mr->mr.max_segs = n; + + m = 0; + n = 0; + list_for_each_entry(chunk, ®ion->chunk_list, list) { + for (i = 0; i < chunk->nmap; i++) { + mr->mr.map[m]->segs[n].vaddr = + page_address(chunk->page_list[i].page); + mr->mr.map[m]->segs[n].length = region->page_size; + n++; + if (n == IPATH_SEGSZ) { + m++; + n = 0; + } + } + } + ret = &mr->ibmr; + +bail: + return ret; +} + +/** + * ipath_dereg_mr - unregister and free a memory region + * @ibmr: the memory region to free + * + * Returns 0 on success. + * + * Note that this is called to free MRs created by ipath_get_dma_mr() + * or ipath_reg_user_mr(). + */ +int ipath_dereg_mr(struct ib_mr *ibmr) +{ + struct ipath_mr *mr = to_imr(ibmr); + int i; + + ipath_free_lkey(&to_idev(ibmr->device)->lk_table, ibmr->lkey); + i = mr->mr.mapsz; + while (i) { + i--; + kfree(mr->mr.map[i]); + } + kfree(mr); + return 0; +} + +/** + * ipath_alloc_fmr - allocate a fast memory region + * @pd: the protection domain for this memory region + * @mr_access_flags: access flags for this memory region + * @fmr_attr: fast memory region attributes + * + * Returns the memory region on success, otherwise returns an errno. + */ +struct ib_fmr *ipath_alloc_fmr(struct ib_pd *pd, int mr_access_flags, + struct ib_fmr_attr *fmr_attr) +{ + struct ipath_fmr *fmr; + int m, i = 0; + struct ib_fmr *ret; + + /* Allocate struct plus pointers to first level page tables. */ + m = (fmr_attr->max_pages + IPATH_SEGSZ - 1) / IPATH_SEGSZ; + fmr = kmalloc(sizeof *fmr + m * sizeof fmr->mr.map[0], GFP_KERNEL); + if (!fmr) + goto bail; + + /* Allocate first level page tables. */ + for (; i < m; i++) { + fmr->mr.map[i] = kmalloc(sizeof *fmr->mr.map[0], + GFP_KERNEL); + if (!fmr->mr.map[i]) + goto bail; + } + fmr->mr.mapsz = m; + + /* + * ib_alloc_fmr() will initialize fmr->ibfmr except for lkey & + * rkey. + */ + if (!ipath_alloc_lkey(&to_idev(pd->device)->lk_table, &fmr->mr)) + goto bail; + fmr->ibfmr.rkey = fmr->ibfmr.lkey = fmr->mr.lkey; + /* + * Resources are allocated but no valid mapping (RKEY can't be + * used). + */ + fmr->mr.user_base = 0; + fmr->mr.iova = 0; + fmr->mr.length = 0; + fmr->mr.offset = 0; + fmr->mr.access_flags = mr_access_flags; + fmr->mr.max_segs = fmr_attr->max_pages; + fmr->page_shift = fmr_attr->page_shift; + + ret = &fmr->ibfmr; + goto done; + +bail: + while (i) + kfree(fmr->mr.map[--i]); + kfree(fmr); + ret = ERR_PTR(-ENOMEM); + +done: + return ret; +} + +/** + * ipath_map_phys_fmr - set up a fast memory region + * @ibmfr: the fast memory region to set up + * @page_list: the list of pages to associate with the fast memory region + * @list_len: the number of pages to associate with the fast memory region + * @iova: the virtual address of the start of the fast memory region + * + * This may be called from interrupt context. + */ + +int ipath_map_phys_fmr(struct ib_fmr *ibfmr, u64 * page_list, + int list_len, u64 iova) +{ + struct ipath_fmr *fmr = to_ifmr(ibfmr); + struct ipath_lkey_table *rkt; + unsigned long flags; + int m, n, i; + u32 ps; + int ret; + + if (list_len > fmr->mr.max_segs) { + ret = -EINVAL; + goto bail; + } + rkt = &to_idev(ibfmr->device)->lk_table; + spin_lock_irqsave(&rkt->lock, flags); + fmr->mr.user_base = iova; + fmr->mr.iova = iova; + ps = 1 << fmr->page_shift; + fmr->mr.length = list_len * ps; + m = 0; + n = 0; + ps = 1 << fmr->page_shift; + for (i = 0; i < list_len; i++) { + fmr->mr.map[m]->segs[n].vaddr = phys_to_virt(page_list[i]); + fmr->mr.map[m]->segs[n].length = ps; + if (++n == IPATH_SEGSZ) { + m++; + n = 0; + } + } + spin_unlock_irqrestore(&rkt->lock, flags); + ret = 0; + +bail: + return ret; +} + +/** + * ipath_unmap_fmr - unmap fast memory regions + * @fmr_list: the list of fast memory regions to unmap + * + * Returns 0 on success. + */ +int ipath_unmap_fmr(struct list_head *fmr_list) +{ + struct ipath_fmr *fmr; + struct ipath_lkey_table *rkt; + unsigned long flags; + + list_for_each_entry(fmr, fmr_list, ibfmr.list) { + rkt = &to_idev(fmr->ibfmr.device)->lk_table; + spin_lock_irqsave(&rkt->lock, flags); + fmr->mr.user_base = 0; + fmr->mr.iova = 0; + fmr->mr.length = 0; + spin_unlock_irqrestore(&rkt->lock, flags); + } + return 0; +} + +/** + * ipath_dealloc_fmr - deallocate a fast memory region + * @ibfmr: the fast memory region to deallocate + * + * Returns 0 on success. + */ +int ipath_dealloc_fmr(struct ib_fmr *ibfmr) +{ + struct ipath_fmr *fmr = to_ifmr(ibfmr); + int i; + + ipath_free_lkey(&to_idev(ibfmr->device)->lk_table, ibfmr->lkey); + i = fmr->mr.mapsz; + while (i) + kfree(fmr->mr.map[--i]); + kfree(fmr); + return 0; +} diff --git a/drivers/infiniband/hw/ipath/ipath_pe800.c b/drivers/infiniband/hw/ipath/ipath_pe800.c new file mode 100644 index 000000000000..e693a7a82667 --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_pe800.c @@ -0,0 +1,1247 @@ +/* + * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +/* + * This file contains all of the code that is specific to the + * InfiniPath PE-800 chip. + */ + +#include +#include +#include + + +#include "ipath_kernel.h" +#include "ipath_registers.h" + +/* + * This file contains all the chip-specific register information and + * access functions for the PathScale PE800, the PCI-Express chip. + * + * This lists the InfiniPath PE800 registers, in the actual chip layout. + * This structure should never be directly accessed. + */ +struct _infinipath_do_not_use_kernel_regs { + unsigned long long Revision; + unsigned long long Control; + unsigned long long PageAlign; + unsigned long long PortCnt; + unsigned long long DebugPortSelect; + unsigned long long Reserved0; + unsigned long long SendRegBase; + unsigned long long UserRegBase; + unsigned long long CounterRegBase; + unsigned long long Scratch; + unsigned long long Reserved1; + unsigned long long Reserved2; + unsigned long long IntBlocked; + unsigned long long IntMask; + unsigned long long IntStatus; + unsigned long long IntClear; + unsigned long long ErrorMask; + unsigned long long ErrorStatus; + unsigned long long ErrorClear; + unsigned long long HwErrMask; + unsigned long long HwErrStatus; + unsigned long long HwErrClear; + unsigned long long HwDiagCtrl; + unsigned long long MDIO; + unsigned long long IBCStatus; + unsigned long long IBCCtrl; + unsigned long long ExtStatus; + unsigned long long ExtCtrl; + unsigned long long GPIOOut; + unsigned long long GPIOMask; + unsigned long long GPIOStatus; + unsigned long long GPIOClear; + unsigned long long RcvCtrl; + unsigned long long RcvBTHQP; + unsigned long long RcvHdrSize; + unsigned long long RcvHdrCnt; + unsigned long long RcvHdrEntSize; + unsigned long long RcvTIDBase; + unsigned long long RcvTIDCnt; + unsigned long long RcvEgrBase; + unsigned long long RcvEgrCnt; + unsigned long long RcvBufBase; + unsigned long long RcvBufSize; + unsigned long long RxIntMemBase; + unsigned long long RxIntMemSize; + unsigned long long RcvPartitionKey; + unsigned long long Reserved3; + unsigned long long RcvPktLEDCnt; + unsigned long long Reserved4[8]; + unsigned long long SendCtrl; + unsigned long long SendPIOBufBase; + unsigned long long SendPIOSize; + unsigned long long SendPIOBufCnt; + unsigned long long SendPIOAvailAddr; + unsigned long long TxIntMemBase; + unsigned long long TxIntMemSize; + unsigned long long Reserved5; + unsigned long long PCIeRBufTestReg0; + unsigned long long PCIeRBufTestReg1; + unsigned long long Reserved51[6]; + unsigned long long SendBufferError; + unsigned long long SendBufferErrorCONT1; + unsigned long long Reserved6SBE[6]; + unsigned long long RcvHdrAddr0; + unsigned long long RcvHdrAddr1; + unsigned long long RcvHdrAddr2; + unsigned long long RcvHdrAddr3; + unsigned long long RcvHdrAddr4; + unsigned long long Reserved7RHA[11]; + unsigned long long RcvHdrTailAddr0; + unsigned long long RcvHdrTailAddr1; + unsigned long long RcvHdrTailAddr2; + unsigned long long RcvHdrTailAddr3; + unsigned long long RcvHdrTailAddr4; + unsigned long long Reserved8RHTA[11]; + unsigned long long Reserved9SW[8]; + unsigned long long SerdesConfig0; + unsigned long long SerdesConfig1; + unsigned long long SerdesStatus; + unsigned long long XGXSConfig; + unsigned long long IBPLLCfg; + unsigned long long Reserved10SW2[3]; + unsigned long long PCIEQ0SerdesConfig0; + unsigned long long PCIEQ0SerdesConfig1; + unsigned long long PCIEQ0SerdesStatus; + unsigned long long Reserved11; + unsigned long long PCIEQ1SerdesConfig0; + unsigned long long PCIEQ1SerdesConfig1; + unsigned long long PCIEQ1SerdesStatus; + unsigned long long Reserved12; +}; + +#define IPATH_KREG_OFFSET(field) (offsetof(struct \ + _infinipath_do_not_use_kernel_regs, field) / sizeof(u64)) +#define IPATH_CREG_OFFSET(field) (offsetof( \ + struct infinipath_counters, field) / sizeof(u64)) + +static const struct ipath_kregs ipath_pe_kregs = { + .kr_control = IPATH_KREG_OFFSET(Control), + .kr_counterregbase = IPATH_KREG_OFFSET(CounterRegBase), + .kr_debugportselect = IPATH_KREG_OFFSET(DebugPortSelect), + .kr_errorclear = IPATH_KREG_OFFSET(ErrorClear), + .kr_errormask = IPATH_KREG_OFFSET(ErrorMask), + .kr_errorstatus = IPATH_KREG_OFFSET(ErrorStatus), + .kr_extctrl = IPATH_KREG_OFFSET(ExtCtrl), + .kr_extstatus = IPATH_KREG_OFFSET(ExtStatus), + .kr_gpio_clear = IPATH_KREG_OFFSET(GPIOClear), + .kr_gpio_mask = IPATH_KREG_OFFSET(GPIOMask), + .kr_gpio_out = IPATH_KREG_OFFSET(GPIOOut), + .kr_gpio_status = IPATH_KREG_OFFSET(GPIOStatus), + .kr_hwdiagctrl = IPATH_KREG_OFFSET(HwDiagCtrl), + .kr_hwerrclear = IPATH_KREG_OFFSET(HwErrClear), + .kr_hwerrmask = IPATH_KREG_OFFSET(HwErrMask), + .kr_hwerrstatus = IPATH_KREG_OFFSET(HwErrStatus), + .kr_ibcctrl = IPATH_KREG_OFFSET(IBCCtrl), + .kr_ibcstatus = IPATH_KREG_OFFSET(IBCStatus), + .kr_intblocked = IPATH_KREG_OFFSET(IntBlocked), + .kr_intclear = IPATH_KREG_OFFSET(IntClear), + .kr_intmask = IPATH_KREG_OFFSET(IntMask), + .kr_intstatus = IPATH_KREG_OFFSET(IntStatus), + .kr_mdio = IPATH_KREG_OFFSET(MDIO), + .kr_pagealign = IPATH_KREG_OFFSET(PageAlign), + .kr_partitionkey = IPATH_KREG_OFFSET(RcvPartitionKey), + .kr_portcnt = IPATH_KREG_OFFSET(PortCnt), + .kr_rcvbthqp = IPATH_KREG_OFFSET(RcvBTHQP), + .kr_rcvbufbase = IPATH_KREG_OFFSET(RcvBufBase), + .kr_rcvbufsize = IPATH_KREG_OFFSET(RcvBufSize), + .kr_rcvctrl = IPATH_KREG_OFFSET(RcvCtrl), + .kr_rcvegrbase = IPATH_KREG_OFFSET(RcvEgrBase), + .kr_rcvegrcnt = IPATH_KREG_OFFSET(RcvEgrCnt), + .kr_rcvhdrcnt = IPATH_KREG_OFFSET(RcvHdrCnt), + .kr_rcvhdrentsize = IPATH_KREG_OFFSET(RcvHdrEntSize), + .kr_rcvhdrsize = IPATH_KREG_OFFSET(RcvHdrSize), + .kr_rcvintmembase = IPATH_KREG_OFFSET(RxIntMemBase), + .kr_rcvintmemsize = IPATH_KREG_OFFSET(RxIntMemSize), + .kr_rcvtidbase = IPATH_KREG_OFFSET(RcvTIDBase), + .kr_rcvtidcnt = IPATH_KREG_OFFSET(RcvTIDCnt), + .kr_revision = IPATH_KREG_OFFSET(Revision), + .kr_scratch = IPATH_KREG_OFFSET(Scratch), + .kr_sendbuffererror = IPATH_KREG_OFFSET(SendBufferError), + .kr_sendctrl = IPATH_KREG_OFFSET(SendCtrl), + .kr_sendpioavailaddr = IPATH_KREG_OFFSET(SendPIOAvailAddr), + .kr_sendpiobufbase = IPATH_KREG_OFFSET(SendPIOBufBase), + .kr_sendpiobufcnt = IPATH_KREG_OFFSET(SendPIOBufCnt), + .kr_sendpiosize = IPATH_KREG_OFFSET(SendPIOSize), + .kr_sendregbase = IPATH_KREG_OFFSET(SendRegBase), + .kr_txintmembase = IPATH_KREG_OFFSET(TxIntMemBase), + .kr_txintmemsize = IPATH_KREG_OFFSET(TxIntMemSize), + .kr_userregbase = IPATH_KREG_OFFSET(UserRegBase), + .kr_serdesconfig0 = IPATH_KREG_OFFSET(SerdesConfig0), + .kr_serdesconfig1 = IPATH_KREG_OFFSET(SerdesConfig1), + .kr_serdesstatus = IPATH_KREG_OFFSET(SerdesStatus), + .kr_xgxsconfig = IPATH_KREG_OFFSET(XGXSConfig), + .kr_ibpllcfg = IPATH_KREG_OFFSET(IBPLLCfg), + + /* + * These should not be used directly via ipath_read_kreg64(), + * use them with ipath_read_kreg64_port() + */ + .kr_rcvhdraddr = IPATH_KREG_OFFSET(RcvHdrAddr0), + .kr_rcvhdrtailaddr = IPATH_KREG_OFFSET(RcvHdrTailAddr0), + + /* This group is pe-800-specific; and used only in this file */ + /* The rcvpktled register controls one of the debug port signals, so + * a packet activity LED can be connected to it. */ + .kr_rcvpktledcnt = IPATH_KREG_OFFSET(RcvPktLEDCnt), + .kr_pcierbuftestreg0 = IPATH_KREG_OFFSET(PCIeRBufTestReg0), + .kr_pcierbuftestreg1 = IPATH_KREG_OFFSET(PCIeRBufTestReg1), + .kr_pcieq0serdesconfig0 = IPATH_KREG_OFFSET(PCIEQ0SerdesConfig0), + .kr_pcieq0serdesconfig1 = IPATH_KREG_OFFSET(PCIEQ0SerdesConfig1), + .kr_pcieq0serdesstatus = IPATH_KREG_OFFSET(PCIEQ0SerdesStatus), + .kr_pcieq1serdesconfig0 = IPATH_KREG_OFFSET(PCIEQ1SerdesConfig0), + .kr_pcieq1serdesconfig1 = IPATH_KREG_OFFSET(PCIEQ1SerdesConfig1), + .kr_pcieq1serdesstatus = IPATH_KREG_OFFSET(PCIEQ1SerdesStatus) +}; + +static const struct ipath_cregs ipath_pe_cregs = { + .cr_badformatcnt = IPATH_CREG_OFFSET(RxBadFormatCnt), + .cr_erricrccnt = IPATH_CREG_OFFSET(RxICRCErrCnt), + .cr_errlinkcnt = IPATH_CREG_OFFSET(RxLinkProblemCnt), + .cr_errlpcrccnt = IPATH_CREG_OFFSET(RxLPCRCErrCnt), + .cr_errpkey = IPATH_CREG_OFFSET(RxPKeyMismatchCnt), + .cr_errrcvflowctrlcnt = IPATH_CREG_OFFSET(RxFlowCtrlErrCnt), + .cr_err_rlencnt = IPATH_CREG_OFFSET(RxLenErrCnt), + .cr_errslencnt = IPATH_CREG_OFFSET(TxLenErrCnt), + .cr_errtidfull = IPATH_CREG_OFFSET(RxTIDFullErrCnt), + .cr_errtidvalid = IPATH_CREG_OFFSET(RxTIDValidErrCnt), + .cr_errvcrccnt = IPATH_CREG_OFFSET(RxVCRCErrCnt), + .cr_ibstatuschange = IPATH_CREG_OFFSET(IBStatusChangeCnt), + .cr_intcnt = IPATH_CREG_OFFSET(LBIntCnt), + .cr_invalidrlencnt = IPATH_CREG_OFFSET(RxMaxMinLenErrCnt), + .cr_invalidslencnt = IPATH_CREG_OFFSET(TxMaxMinLenErrCnt), + .cr_lbflowstallcnt = IPATH_CREG_OFFSET(LBFlowStallCnt), + .cr_pktrcvcnt = IPATH_CREG_OFFSET(RxDataPktCnt), + .cr_pktrcvflowctrlcnt = IPATH_CREG_OFFSET(RxFlowPktCnt), + .cr_pktsendcnt = IPATH_CREG_OFFSET(TxDataPktCnt), + .cr_pktsendflowcnt = IPATH_CREG_OFFSET(TxFlowPktCnt), + .cr_portovflcnt = IPATH_CREG_OFFSET(RxP0HdrEgrOvflCnt), + .cr_rcvebpcnt = IPATH_CREG_OFFSET(RxEBPCnt), + .cr_rcvovflcnt = IPATH_CREG_OFFSET(RxBufOvflCnt), + .cr_senddropped = IPATH_CREG_OFFSET(TxDroppedPktCnt), + .cr_sendstallcnt = IPATH_CREG_OFFSET(TxFlowStallCnt), + .cr_sendunderruncnt = IPATH_CREG_OFFSET(TxUnderrunCnt), + .cr_wordrcvcnt = IPATH_CREG_OFFSET(RxDwordCnt), + .cr_wordsendcnt = IPATH_CREG_OFFSET(TxDwordCnt), + .cr_unsupvlcnt = IPATH_CREG_OFFSET(TxUnsupVLErrCnt), + .cr_rxdroppktcnt = IPATH_CREG_OFFSET(RxDroppedPktCnt), + .cr_iblinkerrrecovcnt = IPATH_CREG_OFFSET(IBLinkErrRecoveryCnt), + .cr_iblinkdowncnt = IPATH_CREG_OFFSET(IBLinkDownedCnt), + .cr_ibsymbolerrcnt = IPATH_CREG_OFFSET(IBSymbolErrCnt) +}; + +/* kr_intstatus, kr_intclear, kr_intmask bits */ +#define INFINIPATH_I_RCVURG_MASK 0x1F +#define INFINIPATH_I_RCVAVAIL_MASK 0x1F + +/* kr_hwerrclear, kr_hwerrmask, kr_hwerrstatus, bits */ +#define INFINIPATH_HWE_PCIEMEMPARITYERR_MASK 0x000000000000003fULL +#define INFINIPATH_HWE_PCIEMEMPARITYERR_SHIFT 0 +#define INFINIPATH_HWE_PCIEPOISONEDTLP 0x0000000010000000ULL +#define INFINIPATH_HWE_PCIECPLTIMEOUT 0x0000000020000000ULL +#define INFINIPATH_HWE_PCIEBUSPARITYXTLH 0x0000000040000000ULL +#define INFINIPATH_HWE_PCIEBUSPARITYXADM 0x0000000080000000ULL +#define INFINIPATH_HWE_PCIEBUSPARITYRADM 0x0000000100000000ULL +#define INFINIPATH_HWE_COREPLL_FBSLIP 0x0080000000000000ULL +#define INFINIPATH_HWE_COREPLL_RFSLIP 0x0100000000000000ULL +#define INFINIPATH_HWE_PCIE1PLLFAILED 0x0400000000000000ULL +#define INFINIPATH_HWE_PCIE0PLLFAILED 0x0800000000000000ULL +#define INFINIPATH_HWE_SERDESPLLFAILED 0x1000000000000000ULL + +/* kr_extstatus bits */ +#define INFINIPATH_EXTS_FREQSEL 0x2 +#define INFINIPATH_EXTS_SERDESSEL 0x4 +#define INFINIPATH_EXTS_MEMBIST_ENDTEST 0x0000000000004000 +#define INFINIPATH_EXTS_MEMBIST_FOUND 0x0000000000008000 + +#define _IPATH_GPIO_SDA_NUM 1 +#define _IPATH_GPIO_SCL_NUM 0 + +#define IPATH_GPIO_SDA (1ULL << \ + (_IPATH_GPIO_SDA_NUM+INFINIPATH_EXTC_GPIOOE_SHIFT)) +#define IPATH_GPIO_SCL (1ULL << \ + (_IPATH_GPIO_SCL_NUM+INFINIPATH_EXTC_GPIOOE_SHIFT)) + +/** + * ipath_pe_handle_hwerrors - display hardware errors. + * @dd: the infinipath device + * @msg: the output buffer + * @msgl: the size of the output buffer + * + * Use same msg buffer as regular errors to avoid excessive stack + * use. Most hardware errors are catastrophic, but for right now, + * we'll print them and continue. We reuse the same message buffer as + * ipath_handle_errors() to avoid excessive stack usage. + */ +void ipath_pe_handle_hwerrors(struct ipath_devdata *dd, char *msg, + size_t msgl) +{ + ipath_err_t hwerrs; + u32 bits, ctrl; + int isfatal = 0; + char bitsmsg[64]; + + hwerrs = ipath_read_kreg64(dd, dd->ipath_kregs->kr_hwerrstatus); + if (!hwerrs) { + /* + * better than printing cofusing messages + * This seems to be related to clearing the crc error, or + * the pll error during init. + */ + ipath_cdbg(VERBOSE, "Called but no hardware errors set\n"); + return; + } else if (hwerrs == ~0ULL) { + ipath_dev_err(dd, "Read of hardware error status failed " + "(all bits set); ignoring\n"); + return; + } + ipath_stats.sps_hwerrs++; + + /* Always clear the error status register, except MEMBISTFAIL, + * regardless of whether we continue or stop using the chip. + * We want that set so we know it failed, even across driver reload. + * We'll still ignore it in the hwerrmask. We do this partly for + * diagnostics, but also for support */ + ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrclear, + hwerrs&~INFINIPATH_HWE_MEMBISTFAILED); + + hwerrs &= dd->ipath_hwerrmask; + + /* + * make sure we get this much out, unless told to be quiet, + * or it's occurred within the last 5 seconds + */ + if ((hwerrs & ~dd->ipath_lasthwerror) || + (ipath_debug & __IPATH_VERBDBG)) + dev_info(&dd->pcidev->dev, "Hardware error: hwerr=0x%llx " + "(cleared)\n", (unsigned long long) hwerrs); + dd->ipath_lasthwerror |= hwerrs; + + if (hwerrs & ~infinipath_hwe_bitsextant) + ipath_dev_err(dd, "hwerror interrupt with unknown errors " + "%llx set\n", (unsigned long long) + (hwerrs & ~infinipath_hwe_bitsextant)); + + ctrl = ipath_read_kreg32(dd, dd->ipath_kregs->kr_control); + if (ctrl & INFINIPATH_C_FREEZEMODE) { + if (hwerrs) { + /* + * if any set that we aren't ignoring only make the + * complaint once, in case it's stuck or recurring, + * and we get here multiple times + */ + if (dd->ipath_flags & IPATH_INITTED) { + ipath_dev_err(dd, "Fatal Error (freeze " + "mode), no longer usable\n"); + isfatal = 1; + } + /* + * Mark as having had an error for driver, and also + * for /sys and status word mapped to user programs. + * This marks unit as not usable, until reset + */ + *dd->ipath_statusp &= ~IPATH_STATUS_IB_READY; + *dd->ipath_statusp |= IPATH_STATUS_HWERROR; + dd->ipath_flags &= ~IPATH_INITTED; + } else { + ipath_dbg("Clearing freezemode on ignored hardware " + "error\n"); + ctrl &= ~INFINIPATH_C_FREEZEMODE; + ipath_write_kreg(dd, dd->ipath_kregs->kr_control, + ctrl); + } + } + + *msg = '\0'; + + if (hwerrs & INFINIPATH_HWE_MEMBISTFAILED) { + strlcat(msg, "[Memory BIST test failed, PE-800 unusable]", + msgl); + /* ignore from now on, so disable until driver reloaded */ + *dd->ipath_statusp |= IPATH_STATUS_HWERROR; + dd->ipath_hwerrmask &= ~INFINIPATH_HWE_MEMBISTFAILED; + ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrmask, + dd->ipath_hwerrmask); + } + if (hwerrs & (INFINIPATH_HWE_RXEMEMPARITYERR_MASK + << INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT)) { + bits = (u32) ((hwerrs >> + INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) & + INFINIPATH_HWE_RXEMEMPARITYERR_MASK); + snprintf(bitsmsg, sizeof bitsmsg, "[RXE Parity Errs %x] ", + bits); + strlcat(msg, bitsmsg, msgl); + } + if (hwerrs & (INFINIPATH_HWE_TXEMEMPARITYERR_MASK + << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)) { + bits = (u32) ((hwerrs >> + INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT) & + INFINIPATH_HWE_TXEMEMPARITYERR_MASK); + snprintf(bitsmsg, sizeof bitsmsg, "[TXE Parity Errs %x] ", + bits); + strlcat(msg, bitsmsg, msgl); + } + if (hwerrs & (INFINIPATH_HWE_PCIEMEMPARITYERR_MASK + << INFINIPATH_HWE_PCIEMEMPARITYERR_SHIFT)) { + bits = (u32) ((hwerrs >> + INFINIPATH_HWE_PCIEMEMPARITYERR_SHIFT) & + INFINIPATH_HWE_PCIEMEMPARITYERR_MASK); + snprintf(bitsmsg, sizeof bitsmsg, + "[PCIe Mem Parity Errs %x] ", bits); + strlcat(msg, bitsmsg, msgl); + } + if (hwerrs & INFINIPATH_HWE_IBCBUSTOSPCPARITYERR) + strlcat(msg, "[IB2IPATH Parity]", msgl); + if (hwerrs & INFINIPATH_HWE_IBCBUSFRSPCPARITYERR) + strlcat(msg, "[IPATH2IB Parity]", msgl); + +#define _IPATH_PLL_FAIL (INFINIPATH_HWE_COREPLL_FBSLIP | \ + INFINIPATH_HWE_COREPLL_RFSLIP ) + + if (hwerrs & _IPATH_PLL_FAIL) { + snprintf(bitsmsg, sizeof bitsmsg, + "[PLL failed (%llx), PE-800 unusable]", + (unsigned long long) hwerrs & _IPATH_PLL_FAIL); + strlcat(msg, bitsmsg, msgl); + /* ignore from now on, so disable until driver reloaded */ + dd->ipath_hwerrmask &= ~(hwerrs & _IPATH_PLL_FAIL); + ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrmask, + dd->ipath_hwerrmask); + } + + if (hwerrs & INFINIPATH_HWE_SERDESPLLFAILED) { + /* + * If it occurs, it is left masked since the eternal + * interface is unused + */ + dd->ipath_hwerrmask &= ~INFINIPATH_HWE_SERDESPLLFAILED; + ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrmask, + dd->ipath_hwerrmask); + } + + if (hwerrs & INFINIPATH_HWE_PCIEPOISONEDTLP) + strlcat(msg, "[PCIe Poisoned TLP]", msgl); + if (hwerrs & INFINIPATH_HWE_PCIECPLTIMEOUT) + strlcat(msg, "[PCIe completion timeout]", msgl); + + /* + * In practice, it's unlikely wthat we'll see PCIe PLL, or bus + * parity or memory parity error failures, because most likely we + * won't be able to talk to the core of the chip. Nonetheless, we + * might see them, if they are in parts of the PCIe core that aren't + * essential. + */ + if (hwerrs & INFINIPATH_HWE_PCIE1PLLFAILED) + strlcat(msg, "[PCIePLL1]", msgl); + if (hwerrs & INFINIPATH_HWE_PCIE0PLLFAILED) + strlcat(msg, "[PCIePLL0]", msgl); + if (hwerrs & INFINIPATH_HWE_PCIEBUSPARITYXTLH) + strlcat(msg, "[PCIe XTLH core parity]", msgl); + if (hwerrs & INFINIPATH_HWE_PCIEBUSPARITYXADM) + strlcat(msg, "[PCIe ADM TX core parity]", msgl); + if (hwerrs & INFINIPATH_HWE_PCIEBUSPARITYRADM) + strlcat(msg, "[PCIe ADM RX core parity]", msgl); + + if (hwerrs & INFINIPATH_HWE_RXDSYNCMEMPARITYERR) + strlcat(msg, "[Rx Dsync]", msgl); + if (hwerrs & INFINIPATH_HWE_SERDESPLLFAILED) + strlcat(msg, "[SerDes PLL]", msgl); + + ipath_dev_err(dd, "%s hardware error\n", msg); + if (isfatal && !ipath_diag_inuse && dd->ipath_freezemsg) { + /* + * for /sys status file ; if no trailing } is copied, we'll + * know it was truncated. + */ + snprintf(dd->ipath_freezemsg, dd->ipath_freezelen, + "{%s}", msg); + } +} + +/** + * ipath_pe_boardname - fill in the board name + * @dd: the infinipath device + * @name: the output buffer + * @namelen: the size of the output buffer + * + * info is based on the board revision register + */ +static int ipath_pe_boardname(struct ipath_devdata *dd, char *name, + size_t namelen) +{ + char *n = NULL; + u8 boardrev = dd->ipath_boardrev; + int ret; + + switch (boardrev) { + case 0: + n = "InfiniPath_Emulation"; + break; + case 1: + n = "InfiniPath_PE-800-Bringup"; + break; + case 2: + n = "InfiniPath_PE-880"; + break; + case 3: + n = "InfiniPath_PE-850"; + break; + case 4: + n = "InfiniPath_PE-860"; + break; + default: + ipath_dev_err(dd, + "Don't yet know about board with ID %u\n", + boardrev); + snprintf(name, namelen, "Unknown_InfiniPath_PE-8xx_%u", + boardrev); + break; + } + if (n) + snprintf(name, namelen, "%s", n); + + if (dd->ipath_majrev != 4 || dd->ipath_minrev != 1) { + ipath_dev_err(dd, "Unsupported PE-800 revision %u.%u!\n", + dd->ipath_majrev, dd->ipath_minrev); + ret = 1; + } else + ret = 0; + + return ret; +} + +/** + * ipath_pe_init_hwerrors - enable hardware errors + * @dd: the infinipath device + * + * now that we have finished initializing everything that might reasonably + * cause a hardware error, and cleared those errors bits as they occur, + * we can enable hardware errors in the mask (potentially enabling + * freeze mode), and enable hardware errors as errors (along with + * everything else) in errormask + */ +void ipath_pe_init_hwerrors(struct ipath_devdata *dd) +{ + ipath_err_t val; + u64 extsval; + + extsval = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extstatus); + + if (!(extsval & INFINIPATH_EXTS_MEMBIST_ENDTEST)) + ipath_dev_err(dd, "MemBIST did not complete!\n"); + + val = ~0ULL; /* barring bugs, all hwerrors become interrupts, */ + + if (!dd->ipath_boardrev) // no PLL for Emulator + val &= ~INFINIPATH_HWE_SERDESPLLFAILED; + + /* workaround bug 9460 in internal interface bus parity checking */ + val &= ~INFINIPATH_HWE_PCIEBUSPARITYRADM; + + dd->ipath_hwerrmask = val; +} + +/** + * ipath_pe_bringup_serdes - bring up the serdes + * @dd: the infinipath device + */ +int ipath_pe_bringup_serdes(struct ipath_devdata *dd) +{ + u64 val, tmp, config1; + int ret = 0, change = 0; + + ipath_dbg("Trying to bringup serdes\n"); + + if (ipath_read_kreg64(dd, dd->ipath_kregs->kr_hwerrstatus) & + INFINIPATH_HWE_SERDESPLLFAILED) { + ipath_dbg("At start, serdes PLL failed bit set " + "in hwerrstatus, clearing and continuing\n"); + ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrclear, + INFINIPATH_HWE_SERDESPLLFAILED); + } + + val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesconfig0); + config1 = ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesconfig1); + + ipath_cdbg(VERBOSE, "SerDes status config0=%llx config1=%llx, " + "xgxsconfig %llx\n", (unsigned long long) val, + (unsigned long long) config1, (unsigned long long) + ipath_read_kreg64(dd, dd->ipath_kregs->kr_xgxsconfig)); + + /* + * Force reset on, also set rxdetect enable. Must do before reading + * serdesstatus at least for simulation, or some of the bits in + * serdes status will come back as undefined and cause simulation + * failures + */ + val |= INFINIPATH_SERDC0_RESET_PLL | INFINIPATH_SERDC0_RXDETECT_EN + | INFINIPATH_SERDC0_L1PWR_DN; + ipath_write_kreg(dd, dd->ipath_kregs->kr_serdesconfig0, val); + /* be sure chip saw it */ + tmp = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch); + udelay(5); /* need pll reset set at least for a bit */ + /* + * after PLL is reset, set the per-lane Resets and TxIdle and + * clear the PLL reset and rxdetect (to get falling edge). + * Leave L1PWR bits set (permanently) + */ + val &= ~(INFINIPATH_SERDC0_RXDETECT_EN | INFINIPATH_SERDC0_RESET_PLL + | INFINIPATH_SERDC0_L1PWR_DN); + val |= INFINIPATH_SERDC0_RESET_MASK | INFINIPATH_SERDC0_TXIDLE; + ipath_cdbg(VERBOSE, "Clearing pll reset and setting lane resets " + "and txidle (%llx)\n", (unsigned long long) val); + ipath_write_kreg(dd, dd->ipath_kregs->kr_serdesconfig0, val); + /* be sure chip saw it */ + tmp = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch); + /* need PLL reset clear for at least 11 usec before lane + * resets cleared; give it a few more to be sure */ + udelay(15); + val &= ~(INFINIPATH_SERDC0_RESET_MASK | INFINIPATH_SERDC0_TXIDLE); + + ipath_cdbg(VERBOSE, "Clearing lane resets and txidle " + "(writing %llx)\n", (unsigned long long) val); + ipath_write_kreg(dd, dd->ipath_kregs->kr_serdesconfig0, val); + /* be sure chip saw it */ + val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch); + + val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_xgxsconfig); + if (((val >> INFINIPATH_XGXS_MDIOADDR_SHIFT) & + INFINIPATH_XGXS_MDIOADDR_MASK) != 3) { + val &= + ~(INFINIPATH_XGXS_MDIOADDR_MASK << + INFINIPATH_XGXS_MDIOADDR_SHIFT); + /* MDIO address 3 */ + val |= 3ULL << INFINIPATH_XGXS_MDIOADDR_SHIFT; + change = 1; + } + if (val & INFINIPATH_XGXS_RESET) { + val &= ~INFINIPATH_XGXS_RESET; + change = 1; + } + if (change) + ipath_write_kreg(dd, dd->ipath_kregs->kr_xgxsconfig, val); + + val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesconfig0); + + /* clear current and de-emphasis bits */ + config1 &= ~0x0ffffffff00ULL; + /* set current to 20ma */ + config1 |= 0x00000000000ULL; + /* set de-emphasis to -5.68dB */ + config1 |= 0x0cccc000000ULL; + ipath_write_kreg(dd, dd->ipath_kregs->kr_serdesconfig1, config1); + + ipath_cdbg(VERBOSE, "done: SerDes status config0=%llx " + "config1=%llx, sstatus=%llx xgxs=%llx\n", + (unsigned long long) val, (unsigned long long) config1, + (unsigned long long) + ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesstatus), + (unsigned long long) + ipath_read_kreg64(dd, dd->ipath_kregs->kr_xgxsconfig)); + + if (!ipath_waitfor_mdio_cmdready(dd)) { + ipath_write_kreg( + dd, dd->ipath_kregs->kr_mdio, + ipath_mdio_req(IPATH_MDIO_CMD_READ, 31, + IPATH_MDIO_CTRL_XGXS_REG_8, 0)); + if (ipath_waitfor_complete(dd, dd->ipath_kregs->kr_mdio, + IPATH_MDIO_DATAVALID, &val)) + ipath_dbg("Never got MDIO data for XGXS " + "status read\n"); + else + ipath_cdbg(VERBOSE, "MDIO Read reg8, " + "'bank' 31 %x\n", (u32) val); + } else + ipath_dbg("Never got MDIO cmdready for XGXS status read\n"); + + return ret; +} + +/** + * ipath_pe_quiet_serdes - set serdes to txidle + * @dd: the infinipath device + * Called when driver is being unloaded + */ +void ipath_pe_quiet_serdes(struct ipath_devdata *dd) +{ + u64 val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesconfig0); + + val |= INFINIPATH_SERDC0_TXIDLE; + ipath_dbg("Setting TxIdleEn on serdes (config0 = %llx)\n", + (unsigned long long) val); + ipath_write_kreg(dd, dd->ipath_kregs->kr_serdesconfig0, val); +} + +/* this is not yet needed on the PE800, so just return 0. */ +static int ipath_pe_intconfig(struct ipath_devdata *dd) +{ + return 0; +} + +/** + * ipath_setup_pe_setextled - set the state of the two external LEDs + * @dd: the infinipath device + * @lst: the L state + * @ltst: the LT state + + * These LEDs indicate the physical and logical state of IB link. + * For this chip (at least with recommended board pinouts), LED1 + * is Yellow (logical state) and LED2 is Green (physical state), + * + * Note: We try to match the Mellanox HCA LED behavior as best + * we can. Green indicates physical link state is OK (something is + * plugged in, and we can train). + * Amber indicates the link is logically up (ACTIVE). + * Mellanox further blinks the amber LED to indicate data packet + * activity, but we have no hardware support for that, so it would + * require waking up every 10-20 msecs and checking the counters + * on the chip, and then turning the LED off if appropriate. That's + * visible overhead, so not something we will do. + * + */ +static void ipath_setup_pe_setextled(struct ipath_devdata *dd, u64 lst, + u64 ltst) +{ + u64 extctl; + + /* the diags use the LED to indicate diag info, so we leave + * the external LED alone when the diags are running */ + if (ipath_diag_inuse) + return; + + extctl = dd->ipath_extctrl & ~(INFINIPATH_EXTC_LED1PRIPORT_ON | + INFINIPATH_EXTC_LED2PRIPORT_ON); + + if (ltst & INFINIPATH_IBCS_LT_STATE_LINKUP) + extctl |= INFINIPATH_EXTC_LED2PRIPORT_ON; + if (lst == INFINIPATH_IBCS_L_STATE_ACTIVE) + extctl |= INFINIPATH_EXTC_LED1PRIPORT_ON; + dd->ipath_extctrl = extctl; + ipath_write_kreg(dd, dd->ipath_kregs->kr_extctrl, extctl); +} + +/** + * ipath_setup_pe_cleanup - clean up any per-chip chip-specific stuff + * @dd: the infinipath device + * + * This is called during driver unload. + * We do the pci_disable_msi here, not in generic code, because it + * isn't used for the HT-400. If we do end up needing pci_enable_msi + * at some point in the future for HT-400, we'll move the call back + * into the main init_one code. + */ +static void ipath_setup_pe_cleanup(struct ipath_devdata *dd) +{ + dd->ipath_msi_lo = 0; /* just in case unload fails */ + pci_disable_msi(dd->pcidev); +} + +/** + * ipath_setup_pe_config - setup PCIe config related stuff + * @dd: the infinipath device + * @pdev: the PCI device + * + * The pci_enable_msi() call will fail on systems with MSI quirks + * such as those with AMD8131, even if the device of interest is not + * attached to that device, (in the 2.6.13 - 2.6.15 kernels, at least, fixed + * late in 2.6.16). + * All that can be done is to edit the kernel source to remove the quirk + * check until that is fixed. + * We do not need to call enable_msi() for our HyperTransport chip (HT-400), + * even those it uses MSI, and we want to avoid the quirk warning, so + * So we call enable_msi only for the PE-800. If we do end up needing + * pci_enable_msi at some point in the future for HT-400, we'll move the + * call back into the main init_one code. + * We save the msi lo and hi values, so we can restore them after + * chip reset (the kernel PCI infrastructure doesn't yet handle that + * correctly). + */ +static int ipath_setup_pe_config(struct ipath_devdata *dd, + struct pci_dev *pdev) +{ + int pos, ret; + + dd->ipath_msi_lo = 0; /* used as a flag during reset processing */ + ret = pci_enable_msi(dd->pcidev); + if (ret) + ipath_dev_err(dd, "pci_enable_msi failed: %d, " + "interrupts may not work\n", ret); + /* continue even if it fails, we may still be OK... */ + + if ((pos = pci_find_capability(dd->pcidev, PCI_CAP_ID_MSI))) { + u16 control; + pci_read_config_dword(dd->pcidev, pos + PCI_MSI_ADDRESS_LO, + &dd->ipath_msi_lo); + pci_read_config_dword(dd->pcidev, pos + PCI_MSI_ADDRESS_HI, + &dd->ipath_msi_hi); + pci_read_config_word(dd->pcidev, pos + PCI_MSI_FLAGS, + &control); + /* now save the data (vector) info */ + pci_read_config_word(dd->pcidev, + pos + ((control & PCI_MSI_FLAGS_64BIT) + ? 12 : 8), + &dd->ipath_msi_data); + ipath_cdbg(VERBOSE, "Read msi data 0x%x from config offset " + "0x%x, control=0x%x\n", dd->ipath_msi_data, + pos + ((control & PCI_MSI_FLAGS_64BIT) ? 12 : 8), + control); + /* we save the cachelinesize also, although it doesn't + * really matter */ + pci_read_config_byte(dd->pcidev, PCI_CACHE_LINE_SIZE, + &dd->ipath_pci_cacheline); + } else + ipath_dev_err(dd, "Can't find MSI capability, " + "can't save MSI settings for reset\n"); + if ((pos = pci_find_capability(dd->pcidev, PCI_CAP_ID_EXP))) { + u16 linkstat; + pci_read_config_word(dd->pcidev, pos + PCI_EXP_LNKSTA, + &linkstat); + linkstat >>= 4; + linkstat &= 0x1f; + if (linkstat != 8) + ipath_dev_err(dd, "PCIe width %u, " + "performance reduced\n", linkstat); + } + else + ipath_dev_err(dd, "Can't find PCI Express " + "capability!\n"); + return 0; +} + +static void ipath_init_pe_variables(void) +{ + /* + * bits for selecting i2c direction and values, + * used for I2C serial flash + */ + ipath_gpio_sda_num = _IPATH_GPIO_SDA_NUM; + ipath_gpio_scl_num = _IPATH_GPIO_SCL_NUM; + ipath_gpio_sda = IPATH_GPIO_SDA; + ipath_gpio_scl = IPATH_GPIO_SCL; + + /* variables for sanity checking interrupt and errors */ + infinipath_hwe_bitsextant = + (INFINIPATH_HWE_RXEMEMPARITYERR_MASK << + INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) | + (INFINIPATH_HWE_PCIEMEMPARITYERR_MASK << + INFINIPATH_HWE_PCIEMEMPARITYERR_SHIFT) | + INFINIPATH_HWE_PCIE1PLLFAILED | + INFINIPATH_HWE_PCIE0PLLFAILED | + INFINIPATH_HWE_PCIEPOISONEDTLP | + INFINIPATH_HWE_PCIECPLTIMEOUT | + INFINIPATH_HWE_PCIEBUSPARITYXTLH | + INFINIPATH_HWE_PCIEBUSPARITYXADM | + INFINIPATH_HWE_PCIEBUSPARITYRADM | + INFINIPATH_HWE_MEMBISTFAILED | + INFINIPATH_HWE_COREPLL_FBSLIP | + INFINIPATH_HWE_COREPLL_RFSLIP | + INFINIPATH_HWE_SERDESPLLFAILED | + INFINIPATH_HWE_IBCBUSTOSPCPARITYERR | + INFINIPATH_HWE_IBCBUSFRSPCPARITYERR; + infinipath_i_bitsextant = + (INFINIPATH_I_RCVURG_MASK << INFINIPATH_I_RCVURG_SHIFT) | + (INFINIPATH_I_RCVAVAIL_MASK << + INFINIPATH_I_RCVAVAIL_SHIFT) | + INFINIPATH_I_ERROR | INFINIPATH_I_SPIOSENT | + INFINIPATH_I_SPIOBUFAVAIL | INFINIPATH_I_GPIO; + infinipath_e_bitsextant = + INFINIPATH_E_RFORMATERR | INFINIPATH_E_RVCRC | + INFINIPATH_E_RICRC | INFINIPATH_E_RMINPKTLEN | + INFINIPATH_E_RMAXPKTLEN | INFINIPATH_E_RLONGPKTLEN | + INFINIPATH_E_RSHORTPKTLEN | INFINIPATH_E_RUNEXPCHAR | + INFINIPATH_E_RUNSUPVL | INFINIPATH_E_REBP | + INFINIPATH_E_RIBFLOW | INFINIPATH_E_RBADVERSION | + INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL | + INFINIPATH_E_RBADTID | INFINIPATH_E_RHDRLEN | + INFINIPATH_E_RHDR | INFINIPATH_E_RIBLOSTLINK | + INFINIPATH_E_SMINPKTLEN | INFINIPATH_E_SMAXPKTLEN | + INFINIPATH_E_SUNDERRUN | INFINIPATH_E_SPKTLEN | + INFINIPATH_E_SDROPPEDSMPPKT | INFINIPATH_E_SDROPPEDDATAPKT | + INFINIPATH_E_SPIOARMLAUNCH | INFINIPATH_E_SUNEXPERRPKTNUM | + INFINIPATH_E_SUNSUPVL | INFINIPATH_E_IBSTATUSCHANGED | + INFINIPATH_E_INVALIDADDR | INFINIPATH_E_RESET | + INFINIPATH_E_HARDWARE; + + infinipath_i_rcvavail_mask = INFINIPATH_I_RCVAVAIL_MASK; + infinipath_i_rcvurg_mask = INFINIPATH_I_RCVURG_MASK; +} + +/* setup the MSI stuff again after a reset. I'd like to just call + * pci_enable_msi() and request_irq() again, but when I do that, + * the MSI enable bit doesn't get set in the command word, and + * we switch to to a different interrupt vector, which is confusing, + * so I instead just do it all inline. Perhaps somehow can tie this + * into the PCIe hotplug support at some point + * Note, because I'm doing it all here, I don't call pci_disable_msi() + * or free_irq() at the start of ipath_setup_pe_reset(). + */ +static int ipath_reinit_msi(struct ipath_devdata *dd) +{ + int pos; + u16 control; + int ret; + + if (!dd->ipath_msi_lo) { + dev_info(&dd->pcidev->dev, "Can't restore MSI config, " + "initial setup failed?\n"); + ret = 0; + goto bail; + } + + if (!(pos = pci_find_capability(dd->pcidev, PCI_CAP_ID_MSI))) { + ipath_dev_err(dd, "Can't find MSI capability, " + "can't restore MSI settings\n"); + ret = 0; + goto bail; + } + ipath_cdbg(VERBOSE, "Writing msi_lo 0x%x to config offset 0x%x\n", + dd->ipath_msi_lo, pos + PCI_MSI_ADDRESS_LO); + pci_write_config_dword(dd->pcidev, pos + PCI_MSI_ADDRESS_LO, + dd->ipath_msi_lo); + ipath_cdbg(VERBOSE, "Writing msi_lo 0x%x to config offset 0x%x\n", + dd->ipath_msi_hi, pos + PCI_MSI_ADDRESS_HI); + pci_write_config_dword(dd->pcidev, pos + PCI_MSI_ADDRESS_HI, + dd->ipath_msi_hi); + pci_read_config_word(dd->pcidev, pos + PCI_MSI_FLAGS, &control); + if (!(control & PCI_MSI_FLAGS_ENABLE)) { + ipath_cdbg(VERBOSE, "MSI control at off %x was %x, " + "setting MSI enable (%x)\n", pos + PCI_MSI_FLAGS, + control, control | PCI_MSI_FLAGS_ENABLE); + control |= PCI_MSI_FLAGS_ENABLE; + pci_write_config_word(dd->pcidev, pos + PCI_MSI_FLAGS, + control); + } + /* now rewrite the data (vector) info */ + pci_write_config_word(dd->pcidev, pos + + ((control & PCI_MSI_FLAGS_64BIT) ? 12 : 8), + dd->ipath_msi_data); + /* we restore the cachelinesize also, although it doesn't really + * matter */ + pci_write_config_byte(dd->pcidev, PCI_CACHE_LINE_SIZE, + dd->ipath_pci_cacheline); + /* and now set the pci master bit again */ + pci_set_master(dd->pcidev); + ret = 1; + +bail: + return ret; +} + +/* This routine sleeps, so it can only be called from user context, not + * from interrupt context. If we need interrupt context, we can split + * it into two routines. +*/ +static int ipath_setup_pe_reset(struct ipath_devdata *dd) +{ + u64 val; + int i; + int ret; + + /* Use ERROR so it shows up in logs, etc. */ + ipath_dev_err(dd, "Resetting PE-800 unit %u\n", + dd->ipath_unit); + val = dd->ipath_control | INFINIPATH_C_RESET; + ipath_write_kreg(dd, dd->ipath_kregs->kr_control, val); + mb(); + + for (i = 1; i <= 5; i++) { + int r; + /* allow MBIST, etc. to complete; longer on each retry. + * We sometimes get machine checks from bus timeout if no + * response, so for now, make it *really* long. + */ + msleep(1000 + (1 + i) * 2000); + if ((r = + pci_write_config_dword(dd->pcidev, PCI_BASE_ADDRESS_0, + dd->ipath_pcibar0))) + ipath_dev_err(dd, "rewrite of BAR0 failed: %d\n", + r); + if ((r = + pci_write_config_dword(dd->pcidev, PCI_BASE_ADDRESS_1, + dd->ipath_pcibar1))) + ipath_dev_err(dd, "rewrite of BAR1 failed: %d\n", + r); + /* now re-enable memory access */ + if ((r = pci_enable_device(dd->pcidev))) + ipath_dev_err(dd, "pci_enable_device failed after " + "reset: %d\n", r); + val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_revision); + if (val == dd->ipath_revision) { + ipath_cdbg(VERBOSE, "Got matching revision " + "register %llx on try %d\n", + (unsigned long long) val, i); + ret = ipath_reinit_msi(dd); + goto bail; + } + /* Probably getting -1 back */ + ipath_dbg("Didn't get expected revision register, " + "got %llx, try %d\n", (unsigned long long) val, + i + 1); + } + ret = 0; /* failed */ + +bail: + return ret; +} + +/** + * ipath_pe_put_tid - write a TID in chip + * @dd: the infinipath device + * @tidptr: pointer to the expected TID (in chip) to udpate + * @tidtype: 0 for eager, 1 for expected + * @pa: physical address of in memory buffer; ipath_tidinvalid if freeing + * + * This exists as a separate routine to allow for special locking etc. + * It's used for both the full cleanup on exit, as well as the normal + * setup and teardown. + */ +static void ipath_pe_put_tid(struct ipath_devdata *dd, u64 __iomem *tidptr, + u32 type, unsigned long pa) +{ + u32 __iomem *tidp32 = (u32 __iomem *)tidptr; + unsigned long flags = 0; /* keep gcc quiet */ + + if (pa != dd->ipath_tidinvalid) { + if (pa & ((1U << 11) - 1)) { + dev_info(&dd->pcidev->dev, "BUG: physaddr %lx " + "not 4KB aligned!\n", pa); + return; + } + pa >>= 11; + /* paranoia check */ + if (pa & (7<<29)) + ipath_dev_err(dd, + "BUG: Physical page address 0x%lx " + "has bits set in 31-29\n", pa); + + if (type == 0) + pa |= dd->ipath_tidtemplate; + else /* for now, always full 4KB page */ + pa |= 2 << 29; + } + + /* workaround chip bug 9437 by writing each TID twice + * and holding a spinlock around the writes, so they don't + * intermix with other TID (eager or expected) writes + * Unfortunately, this call can be done from interrupt level + * for the port 0 eager TIDs, so we have to use irqsave + */ + spin_lock_irqsave(&dd->ipath_tid_lock, flags); + ipath_write_kreg(dd, dd->ipath_kregs->kr_scratch, 0xfeeddeaf); + if (dd->ipath_kregbase) + writel(pa, tidp32); + ipath_write_kreg(dd, dd->ipath_kregs->kr_scratch, 0xdeadbeef); + mmiowb(); + spin_unlock_irqrestore(&dd->ipath_tid_lock, flags); +} + +/** + * ipath_pe_clear_tid - clear all TID entries for a port, expected and eager + * @dd: the infinipath device + * @port: the port + * + * clear all TID entries for a port, expected and eager. + * Used from ipath_close(). On PE800, TIDs are only 32 bits, + * not 64, but they are still on 64 bit boundaries, so tidbase + * is declared as u64 * for the pointer math, even though we write 32 bits + */ +static void ipath_pe_clear_tids(struct ipath_devdata *dd, unsigned port) +{ + u64 __iomem *tidbase; + unsigned long tidinv; + int i; + + if (!dd->ipath_kregbase) + return; + + ipath_cdbg(VERBOSE, "Invalidate TIDs for port %u\n", port); + + tidinv = dd->ipath_tidinvalid; + tidbase = (u64 __iomem *) + ((char __iomem *)(dd->ipath_kregbase) + + dd->ipath_rcvtidbase + + port * dd->ipath_rcvtidcnt * sizeof(*tidbase)); + + for (i = 0; i < dd->ipath_rcvtidcnt; i++) + ipath_pe_put_tid(dd, &tidbase[i], 0, tidinv); + + tidbase = (u64 __iomem *) + ((char __iomem *)(dd->ipath_kregbase) + + dd->ipath_rcvegrbase + + port * dd->ipath_rcvegrcnt * sizeof(*tidbase)); + + for (i = 0; i < dd->ipath_rcvegrcnt; i++) + ipath_pe_put_tid(dd, &tidbase[i], 1, tidinv); +} + +/** + * ipath_pe_tidtemplate - setup constants for TID updates + * @dd: the infinipath device + * + * We setup stuff that we use a lot, to avoid calculating each time + */ +static void ipath_pe_tidtemplate(struct ipath_devdata *dd) +{ + u32 egrsize = dd->ipath_rcvegrbufsize; + + /* For now, we always allocate 4KB buffers (at init) so we can + * receive max size packets. We may want a module parameter to + * specify 2KB or 4KB and/or make be per port instead of per device + * for those who want to reduce memory footprint. Note that the + * ipath_rcvhdrentsize size must be large enough to hold the largest + * IB header (currently 96 bytes) that we expect to handle (plus of + * course the 2 dwords of RHF). + */ + if (egrsize == 2048) + dd->ipath_tidtemplate = 1U << 29; + else if (egrsize == 4096) + dd->ipath_tidtemplate = 2U << 29; + else { + egrsize = 4096; + dev_info(&dd->pcidev->dev, "BUG: unsupported egrbufsize " + "%u, using %u\n", dd->ipath_rcvegrbufsize, + egrsize); + dd->ipath_tidtemplate = 2U << 29; + } + dd->ipath_tidinvalid = 0; +} + +static int ipath_pe_early_init(struct ipath_devdata *dd) +{ + dd->ipath_flags |= IPATH_4BYTE_TID; + + /* + * For openib, we need to be able to handle an IB header of 96 bytes + * or 24 dwords. HT-400 has arbitrary sized receive buffers, so we + * made them the same size as the PIO buffers. The PE-800 does not + * handle arbitrary size buffers, so we need the header large enough + * to handle largest IB header, but still have room for a 2KB MTU + * standard IB packet. + */ + dd->ipath_rcvhdrentsize = 24; + dd->ipath_rcvhdrsize = IPATH_DFLT_RCVHDRSIZE; + + /* For HT-400, we allocate a somewhat overly large eager buffer, + * such that we can guarantee that we can receive the largest packet + * that we can send out. To truly support a 4KB MTU, we need to + * bump this to a larger value. We'll do this when I get around to + * testing 4KB sends on the PE-800, which I have not yet done. + */ + dd->ipath_rcvegrbufsize = 2048; + /* + * the min() check here is currently a nop, but it may not always + * be, depending on just how we do ipath_rcvegrbufsize + */ + dd->ipath_ibmaxlen = min(dd->ipath_piosize2k, + dd->ipath_rcvegrbufsize + + (dd->ipath_rcvhdrentsize << 2)); + dd->ipath_init_ibmaxlen = dd->ipath_ibmaxlen; + + /* + * For PE-800, we can request a receive interrupt for 1 or + * more packets from current offset. For now, we set this + * up for a single packet, to match the HT-400 behavior. + */ + dd->ipath_rhdrhead_intr_off = 1ULL<<32; + + return 0; +} + +int __attribute__((weak)) ipath_unordered_wc(void) +{ + return 0; +} + +/** + * ipath_init_pe_get_base_info - set chip-specific flags for user code + * @dd: the infinipath device + * @kbase: ipath_base_info pointer + * + * We set the PCIE flag because the lower bandwidth on PCIe vs + * HyperTransport can affect some user packet algorithims. + */ +static int ipath_pe_get_base_info(struct ipath_portdata *pd, void *kbase) +{ + struct ipath_base_info *kinfo = kbase; + + if (ipath_unordered_wc()) { + kinfo->spi_runtime_flags |= IPATH_RUNTIME_FORCE_WC_ORDER; + ipath_cdbg(PROC, "Intel processor, forcing WC order\n"); + } + else + ipath_cdbg(PROC, "Not Intel processor, WC ordered\n"); + + kinfo->spi_runtime_flags |= IPATH_RUNTIME_PCIE; + + return 0; +} + +/** + * ipath_init_pe800_funcs - set up the chip-specific function pointers + * @dd: the infinipath device + * + * This is global, and is called directly at init to set up the + * chip-specific function pointers for later use. + */ +void ipath_init_pe800_funcs(struct ipath_devdata *dd) +{ + dd->ipath_f_intrsetup = ipath_pe_intconfig; + dd->ipath_f_bus = ipath_setup_pe_config; + dd->ipath_f_reset = ipath_setup_pe_reset; + dd->ipath_f_get_boardname = ipath_pe_boardname; + dd->ipath_f_init_hwerrors = ipath_pe_init_hwerrors; + dd->ipath_f_early_init = ipath_pe_early_init; + dd->ipath_f_handle_hwerrors = ipath_pe_handle_hwerrors; + dd->ipath_f_quiet_serdes = ipath_pe_quiet_serdes; + dd->ipath_f_bringup_serdes = ipath_pe_bringup_serdes; + dd->ipath_f_clear_tids = ipath_pe_clear_tids; + dd->ipath_f_put_tid = ipath_pe_put_tid; + dd->ipath_f_cleanup = ipath_setup_pe_cleanup; + dd->ipath_f_setextled = ipath_setup_pe_setextled; + dd->ipath_f_get_base_info = ipath_pe_get_base_info; + + /* initialize chip-specific variables */ + dd->ipath_f_tidtemplate = ipath_pe_tidtemplate; + + /* + * setup the register offsets, since they are different for each + * chip + */ + dd->ipath_kregs = &ipath_pe_kregs; + dd->ipath_cregs = &ipath_pe_cregs; + + ipath_init_pe_variables(); +} + diff --git a/drivers/infiniband/hw/ipath/ipath_qp.c b/drivers/infiniband/hw/ipath/ipath_qp.c new file mode 100644 index 000000000000..6058d70d7577 --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_qp.c @@ -0,0 +1,913 @@ +/* + * Copyright (c) 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include "ipath_verbs.h" +#include "ips_common.h" + +#define BITS_PER_PAGE (PAGE_SIZE*BITS_PER_BYTE) +#define BITS_PER_PAGE_MASK (BITS_PER_PAGE-1) +#define mk_qpn(qpt, map, off) (((map) - (qpt)->map) * BITS_PER_PAGE + \ + (off)) +#define find_next_offset(map, off) find_next_zero_bit((map)->page, \ + BITS_PER_PAGE, off) + +#define TRANS_INVALID 0 +#define TRANS_ANY2RST 1 +#define TRANS_RST2INIT 2 +#define TRANS_INIT2INIT 3 +#define TRANS_INIT2RTR 4 +#define TRANS_RTR2RTS 5 +#define TRANS_RTS2RTS 6 +#define TRANS_SQERR2RTS 7 +#define TRANS_ANY2ERR 8 +#define TRANS_RTS2SQD 9 /* XXX Wait for expected ACKs & signal event */ +#define TRANS_SQD2SQD 10 /* error if not drained & parameter change */ +#define TRANS_SQD2RTS 11 /* error if not drained */ + +/* + * Convert the AETH credit code into the number of credits. + */ +static u32 credit_table[31] = { + 0, /* 0 */ + 1, /* 1 */ + 2, /* 2 */ + 3, /* 3 */ + 4, /* 4 */ + 6, /* 5 */ + 8, /* 6 */ + 12, /* 7 */ + 16, /* 8 */ + 24, /* 9 */ + 32, /* A */ + 48, /* B */ + 64, /* C */ + 96, /* D */ + 128, /* E */ + 192, /* F */ + 256, /* 10 */ + 384, /* 11 */ + 512, /* 12 */ + 768, /* 13 */ + 1024, /* 14 */ + 1536, /* 15 */ + 2048, /* 16 */ + 3072, /* 17 */ + 4096, /* 18 */ + 6144, /* 19 */ + 8192, /* 1A */ + 12288, /* 1B */ + 16384, /* 1C */ + 24576, /* 1D */ + 32768 /* 1E */ +}; + +static u32 alloc_qpn(struct ipath_qp_table *qpt) +{ + u32 i, offset, max_scan, qpn; + struct qpn_map *map; + u32 ret; + + qpn = qpt->last + 1; + if (qpn >= QPN_MAX) + qpn = 2; + offset = qpn & BITS_PER_PAGE_MASK; + map = &qpt->map[qpn / BITS_PER_PAGE]; + max_scan = qpt->nmaps - !offset; + for (i = 0;;) { + if (unlikely(!map->page)) { + unsigned long page = get_zeroed_page(GFP_KERNEL); + unsigned long flags; + + /* + * Free the page if someone raced with us + * installing it: + */ + spin_lock_irqsave(&qpt->lock, flags); + if (map->page) + free_page(page); + else + map->page = (void *)page; + spin_unlock_irqrestore(&qpt->lock, flags); + if (unlikely(!map->page)) + break; + } + if (likely(atomic_read(&map->n_free))) { + do { + if (!test_and_set_bit(offset, map->page)) { + atomic_dec(&map->n_free); + qpt->last = qpn; + ret = qpn; + goto bail; + } + offset = find_next_offset(map, offset); + qpn = mk_qpn(qpt, map, offset); + /* + * This test differs from alloc_pidmap(). + * If find_next_offset() does find a zero + * bit, we don't need to check for QPN + * wrapping around past our starting QPN. + * We just need to be sure we don't loop + * forever. + */ + } while (offset < BITS_PER_PAGE && qpn < QPN_MAX); + } + /* + * In order to keep the number of pages allocated to a + * minimum, we scan the all existing pages before increasing + * the size of the bitmap table. + */ + if (++i > max_scan) { + if (qpt->nmaps == QPNMAP_ENTRIES) + break; + map = &qpt->map[qpt->nmaps++]; + offset = 0; + } else if (map < &qpt->map[qpt->nmaps]) { + ++map; + offset = 0; + } else { + map = &qpt->map[0]; + offset = 2; + } + qpn = mk_qpn(qpt, map, offset); + } + + ret = 0; + +bail: + return ret; +} + +static void free_qpn(struct ipath_qp_table *qpt, u32 qpn) +{ + struct qpn_map *map; + + map = qpt->map + qpn / BITS_PER_PAGE; + if (map->page) + clear_bit(qpn & BITS_PER_PAGE_MASK, map->page); + atomic_inc(&map->n_free); +} + +/** + * ipath_alloc_qpn - allocate a QP number + * @qpt: the QP table + * @qp: the QP + * @type: the QP type (IB_QPT_SMI and IB_QPT_GSI are special) + * + * Allocate the next available QPN and put the QP into the hash table. + * The hash table holds a reference to the QP. + */ +int ipath_alloc_qpn(struct ipath_qp_table *qpt, struct ipath_qp *qp, + enum ib_qp_type type) +{ + unsigned long flags; + u32 qpn; + int ret; + + if (type == IB_QPT_SMI) + qpn = 0; + else if (type == IB_QPT_GSI) + qpn = 1; + else { + /* Allocate the next available QPN */ + qpn = alloc_qpn(qpt); + if (qpn == 0) { + ret = -ENOMEM; + goto bail; + } + } + qp->ibqp.qp_num = qpn; + + /* Add the QP to the hash table. */ + spin_lock_irqsave(&qpt->lock, flags); + + qpn %= qpt->max; + qp->next = qpt->table[qpn]; + qpt->table[qpn] = qp; + atomic_inc(&qp->refcount); + + spin_unlock_irqrestore(&qpt->lock, flags); + ret = 0; + +bail: + return ret; +} + +/** + * ipath_free_qp - remove a QP from the QP table + * @qpt: the QP table + * @qp: the QP to remove + * + * Remove the QP from the table so it can't be found asynchronously by + * the receive interrupt routine. + */ +void ipath_free_qp(struct ipath_qp_table *qpt, struct ipath_qp *qp) +{ + struct ipath_qp *q, **qpp; + unsigned long flags; + int fnd = 0; + + spin_lock_irqsave(&qpt->lock, flags); + + /* Remove QP from the hash table. */ + qpp = &qpt->table[qp->ibqp.qp_num % qpt->max]; + for (; (q = *qpp) != NULL; qpp = &q->next) { + if (q == qp) { + *qpp = qp->next; + qp->next = NULL; + atomic_dec(&qp->refcount); + fnd = 1; + break; + } + } + + spin_unlock_irqrestore(&qpt->lock, flags); + + if (!fnd) + return; + + /* If QPN is not reserved, mark QPN free in the bitmap. */ + if (qp->ibqp.qp_num > 1) + free_qpn(qpt, qp->ibqp.qp_num); + + wait_event(qp->wait, !atomic_read(&qp->refcount)); +} + +/** + * ipath_free_all_qps - remove all QPs from the table + * @qpt: the QP table to empty + */ +void ipath_free_all_qps(struct ipath_qp_table *qpt) +{ + unsigned long flags; + struct ipath_qp *qp, *nqp; + u32 n; + + for (n = 0; n < qpt->max; n++) { + spin_lock_irqsave(&qpt->lock, flags); + qp = qpt->table[n]; + qpt->table[n] = NULL; + spin_unlock_irqrestore(&qpt->lock, flags); + + while (qp) { + nqp = qp->next; + if (qp->ibqp.qp_num > 1) + free_qpn(qpt, qp->ibqp.qp_num); + if (!atomic_dec_and_test(&qp->refcount) || + !ipath_destroy_qp(&qp->ibqp)) + _VERBS_INFO("QP memory leak!\n"); + qp = nqp; + } + } + + for (n = 0; n < ARRAY_SIZE(qpt->map); n++) { + if (qpt->map[n].page) + free_page((unsigned long)qpt->map[n].page); + } +} + +/** + * ipath_lookup_qpn - return the QP with the given QPN + * @qpt: the QP table + * @qpn: the QP number to look up + * + * The caller is responsible for decrementing the QP reference count + * when done. + */ +struct ipath_qp *ipath_lookup_qpn(struct ipath_qp_table *qpt, u32 qpn) +{ + unsigned long flags; + struct ipath_qp *qp; + + spin_lock_irqsave(&qpt->lock, flags); + + for (qp = qpt->table[qpn % qpt->max]; qp; qp = qp->next) { + if (qp->ibqp.qp_num == qpn) { + atomic_inc(&qp->refcount); + break; + } + } + + spin_unlock_irqrestore(&qpt->lock, flags); + return qp; +} + +/** + * ipath_reset_qp - initialize the QP state to the reset state + * @qp: the QP to reset + */ +static void ipath_reset_qp(struct ipath_qp *qp) +{ + qp->remote_qpn = 0; + qp->qkey = 0; + qp->qp_access_flags = 0; + qp->s_hdrwords = 0; + qp->s_psn = 0; + qp->r_psn = 0; + atomic_set(&qp->msn, 0); + if (qp->ibqp.qp_type == IB_QPT_RC) { + qp->s_state = IB_OPCODE_RC_SEND_LAST; + qp->r_state = IB_OPCODE_RC_SEND_LAST; + } else { + qp->s_state = IB_OPCODE_UC_SEND_LAST; + qp->r_state = IB_OPCODE_UC_SEND_LAST; + } + qp->s_ack_state = IB_OPCODE_RC_ACKNOWLEDGE; + qp->s_nak_state = 0; + qp->s_rnr_timeout = 0; + qp->s_head = 0; + qp->s_tail = 0; + qp->s_cur = 0; + qp->s_last = 0; + qp->s_ssn = 1; + qp->s_lsn = 0; + qp->r_rq.head = 0; + qp->r_rq.tail = 0; + qp->r_reuse_sge = 0; +} + +/** + * ipath_modify_qp - modify the attributes of a queue pair + * @ibqp: the queue pair who's attributes we're modifying + * @attr: the new attributes + * @attr_mask: the mask of attributes to modify + * + * Returns 0 on success, otherwise returns an errno. + */ +int ipath_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, + int attr_mask) +{ + struct ipath_qp *qp = to_iqp(ibqp); + enum ib_qp_state cur_state, new_state; + unsigned long flags; + int ret; + + spin_lock_irqsave(&qp->r_rq.lock, flags); + spin_lock(&qp->s_lock); + + cur_state = attr_mask & IB_QP_CUR_STATE ? + attr->cur_qp_state : qp->state; + new_state = attr_mask & IB_QP_STATE ? attr->qp_state : cur_state; + + if (!ib_modify_qp_is_ok(cur_state, new_state, ibqp->qp_type, + attr_mask)) + goto inval; + + switch (new_state) { + case IB_QPS_RESET: + ipath_reset_qp(qp); + break; + + case IB_QPS_ERR: + ipath_error_qp(qp); + break; + + default: + break; + + } + + if (attr_mask & IB_QP_PKEY_INDEX) { + struct ipath_ibdev *dev = to_idev(ibqp->device); + + if (attr->pkey_index >= ipath_layer_get_npkeys(dev->dd)) + goto inval; + qp->s_pkey_index = attr->pkey_index; + } + + if (attr_mask & IB_QP_DEST_QPN) + qp->remote_qpn = attr->dest_qp_num; + + if (attr_mask & IB_QP_SQ_PSN) { + qp->s_next_psn = attr->sq_psn; + qp->s_last_psn = qp->s_next_psn - 1; + } + + if (attr_mask & IB_QP_RQ_PSN) + qp->r_psn = attr->rq_psn; + + if (attr_mask & IB_QP_ACCESS_FLAGS) + qp->qp_access_flags = attr->qp_access_flags; + + if (attr_mask & IB_QP_AV) { + if (attr->ah_attr.dlid == 0 || + attr->ah_attr.dlid >= IPS_MULTICAST_LID_BASE) + goto inval; + qp->remote_ah_attr = attr->ah_attr; + } + + if (attr_mask & IB_QP_PATH_MTU) + qp->path_mtu = attr->path_mtu; + + if (attr_mask & IB_QP_RETRY_CNT) + qp->s_retry = qp->s_retry_cnt = attr->retry_cnt; + + if (attr_mask & IB_QP_RNR_RETRY) { + qp->s_rnr_retry = attr->rnr_retry; + if (qp->s_rnr_retry > 7) + qp->s_rnr_retry = 7; + qp->s_rnr_retry_cnt = qp->s_rnr_retry; + } + + if (attr_mask & IB_QP_MIN_RNR_TIMER) { + if (attr->min_rnr_timer > 31) + goto inval; + qp->s_min_rnr_timer = attr->min_rnr_timer; + } + + if (attr_mask & IB_QP_QKEY) + qp->qkey = attr->qkey; + + if (attr_mask & IB_QP_PKEY_INDEX) + qp->s_pkey_index = attr->pkey_index; + + qp->state = new_state; + spin_unlock(&qp->s_lock); + spin_unlock_irqrestore(&qp->r_rq.lock, flags); + + /* + * If QP1 changed to the RTS state, try to move to the link to INIT + * even if it was ACTIVE so the SM will reinitialize the SMA's + * state. + */ + if (qp->ibqp.qp_num == 1 && new_state == IB_QPS_RTS) { + struct ipath_ibdev *dev = to_idev(ibqp->device); + + ipath_layer_set_linkstate(dev->dd, IPATH_IB_LINKDOWN); + } + ret = 0; + goto bail; + +inval: + spin_unlock(&qp->s_lock); + spin_unlock_irqrestore(&qp->r_rq.lock, flags); + ret = -EINVAL; + +bail: + return ret; +} + +int ipath_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, + int attr_mask, struct ib_qp_init_attr *init_attr) +{ + struct ipath_qp *qp = to_iqp(ibqp); + + attr->qp_state = qp->state; + attr->cur_qp_state = attr->qp_state; + attr->path_mtu = qp->path_mtu; + attr->path_mig_state = 0; + attr->qkey = qp->qkey; + attr->rq_psn = qp->r_psn; + attr->sq_psn = qp->s_next_psn; + attr->dest_qp_num = qp->remote_qpn; + attr->qp_access_flags = qp->qp_access_flags; + attr->cap.max_send_wr = qp->s_size - 1; + attr->cap.max_recv_wr = qp->r_rq.size - 1; + attr->cap.max_send_sge = qp->s_max_sge; + attr->cap.max_recv_sge = qp->r_rq.max_sge; + attr->cap.max_inline_data = 0; + attr->ah_attr = qp->remote_ah_attr; + memset(&attr->alt_ah_attr, 0, sizeof(attr->alt_ah_attr)); + attr->pkey_index = qp->s_pkey_index; + attr->alt_pkey_index = 0; + attr->en_sqd_async_notify = 0; + attr->sq_draining = 0; + attr->max_rd_atomic = 1; + attr->max_dest_rd_atomic = 1; + attr->min_rnr_timer = qp->s_min_rnr_timer; + attr->port_num = 1; + attr->timeout = 0; + attr->retry_cnt = qp->s_retry_cnt; + attr->rnr_retry = qp->s_rnr_retry; + attr->alt_port_num = 0; + attr->alt_timeout = 0; + + init_attr->event_handler = qp->ibqp.event_handler; + init_attr->qp_context = qp->ibqp.qp_context; + init_attr->send_cq = qp->ibqp.send_cq; + init_attr->recv_cq = qp->ibqp.recv_cq; + init_attr->srq = qp->ibqp.srq; + init_attr->cap = attr->cap; + init_attr->sq_sig_type = + (qp->s_flags & (1 << IPATH_S_SIGNAL_REQ_WR)) + ? IB_SIGNAL_REQ_WR : 0; + init_attr->qp_type = qp->ibqp.qp_type; + init_attr->port_num = 1; + return 0; +} + +/** + * ipath_compute_aeth - compute the AETH (syndrome + MSN) + * @qp: the queue pair to compute the AETH for + * + * Returns the AETH. + * + * The QP s_lock should be held. + */ +__be32 ipath_compute_aeth(struct ipath_qp *qp) +{ + u32 aeth = atomic_read(&qp->msn) & IPS_MSN_MASK; + + if (qp->s_nak_state) { + aeth |= qp->s_nak_state << IPS_AETH_CREDIT_SHIFT; + } else if (qp->ibqp.srq) { + /* + * Shared receive queues don't generate credits. + * Set the credit field to the invalid value. + */ + aeth |= IPS_AETH_CREDIT_INVAL << IPS_AETH_CREDIT_SHIFT; + } else { + u32 min, max, x; + u32 credits; + + /* + * Compute the number of credits available (RWQEs). + * XXX Not holding the r_rq.lock here so there is a small + * chance that the pair of reads are not atomic. + */ + credits = qp->r_rq.head - qp->r_rq.tail; + if ((int)credits < 0) + credits += qp->r_rq.size; + /* + * Binary search the credit table to find the code to + * use. + */ + min = 0; + max = 31; + for (;;) { + x = (min + max) / 2; + if (credit_table[x] == credits) + break; + if (credit_table[x] > credits) + max = x; + else if (min == x) + break; + else + min = x; + } + aeth |= x << IPS_AETH_CREDIT_SHIFT; + } + return cpu_to_be32(aeth); +} + +/** + * ipath_create_qp - create a queue pair for a device + * @ibpd: the protection domain who's device we create the queue pair for + * @init_attr: the attributes of the queue pair + * @udata: unused by InfiniPath + * + * Returns the queue pair on success, otherwise returns an errno. + * + * Called by the ib_create_qp() core verbs function. + */ +struct ib_qp *ipath_create_qp(struct ib_pd *ibpd, + struct ib_qp_init_attr *init_attr, + struct ib_udata *udata) +{ + struct ipath_qp *qp; + int err; + struct ipath_swqe *swq = NULL; + struct ipath_ibdev *dev; + size_t sz; + struct ib_qp *ret; + + if (init_attr->cap.max_send_sge > 255 || + init_attr->cap.max_recv_sge > 255) { + ret = ERR_PTR(-ENOMEM); + goto bail; + } + + switch (init_attr->qp_type) { + case IB_QPT_UC: + case IB_QPT_RC: + sz = sizeof(struct ipath_sge) * + init_attr->cap.max_send_sge + + sizeof(struct ipath_swqe); + swq = vmalloc((init_attr->cap.max_send_wr + 1) * sz); + if (swq == NULL) { + ret = ERR_PTR(-ENOMEM); + goto bail; + } + /* FALLTHROUGH */ + case IB_QPT_UD: + case IB_QPT_SMI: + case IB_QPT_GSI: + qp = kmalloc(sizeof(*qp), GFP_KERNEL); + if (!qp) { + ret = ERR_PTR(-ENOMEM); + goto bail; + } + qp->r_rq.size = init_attr->cap.max_recv_wr + 1; + sz = sizeof(struct ipath_sge) * + init_attr->cap.max_recv_sge + + sizeof(struct ipath_rwqe); + qp->r_rq.wq = vmalloc(qp->r_rq.size * sz); + if (!qp->r_rq.wq) { + kfree(qp); + ret = ERR_PTR(-ENOMEM); + goto bail; + } + + /* + * ib_create_qp() will initialize qp->ibqp + * except for qp->ibqp.qp_num. + */ + spin_lock_init(&qp->s_lock); + spin_lock_init(&qp->r_rq.lock); + atomic_set(&qp->refcount, 0); + init_waitqueue_head(&qp->wait); + tasklet_init(&qp->s_task, + init_attr->qp_type == IB_QPT_RC ? + ipath_do_rc_send : ipath_do_uc_send, + (unsigned long)qp); + qp->piowait.next = LIST_POISON1; + qp->piowait.prev = LIST_POISON2; + qp->timerwait.next = LIST_POISON1; + qp->timerwait.prev = LIST_POISON2; + qp->state = IB_QPS_RESET; + qp->s_wq = swq; + qp->s_size = init_attr->cap.max_send_wr + 1; + qp->s_max_sge = init_attr->cap.max_send_sge; + qp->r_rq.max_sge = init_attr->cap.max_recv_sge; + qp->s_flags = init_attr->sq_sig_type == IB_SIGNAL_REQ_WR ? + 1 << IPATH_S_SIGNAL_REQ_WR : 0; + dev = to_idev(ibpd->device); + err = ipath_alloc_qpn(&dev->qp_table, qp, + init_attr->qp_type); + if (err) { + vfree(swq); + vfree(qp->r_rq.wq); + kfree(qp); + ret = ERR_PTR(err); + goto bail; + } + ipath_reset_qp(qp); + + /* Tell the core driver that the kernel SMA is present. */ + if (qp->ibqp.qp_type == IB_QPT_SMI) + ipath_layer_set_verbs_flags(dev->dd, + IPATH_VERBS_KERNEL_SMA); + break; + + default: + /* Don't support raw QPs */ + ret = ERR_PTR(-ENOSYS); + goto bail; + } + + init_attr->cap.max_inline_data = 0; + + ret = &qp->ibqp; + +bail: + return ret; +} + +/** + * ipath_destroy_qp - destroy a queue pair + * @ibqp: the queue pair to destroy + * + * Returns 0 on success. + * + * Note that this can be called while the QP is actively sending or + * receiving! + */ +int ipath_destroy_qp(struct ib_qp *ibqp) +{ + struct ipath_qp *qp = to_iqp(ibqp); + struct ipath_ibdev *dev = to_idev(ibqp->device); + unsigned long flags; + + /* Tell the core driver that the kernel SMA is gone. */ + if (qp->ibqp.qp_type == IB_QPT_SMI) + ipath_layer_set_verbs_flags(dev->dd, 0); + + spin_lock_irqsave(&qp->r_rq.lock, flags); + spin_lock(&qp->s_lock); + qp->state = IB_QPS_ERR; + spin_unlock(&qp->s_lock); + spin_unlock_irqrestore(&qp->r_rq.lock, flags); + + /* Stop the sending tasklet. */ + tasklet_kill(&qp->s_task); + + /* Make sure the QP isn't on the timeout list. */ + spin_lock_irqsave(&dev->pending_lock, flags); + if (qp->timerwait.next != LIST_POISON1) + list_del(&qp->timerwait); + if (qp->piowait.next != LIST_POISON1) + list_del(&qp->piowait); + spin_unlock_irqrestore(&dev->pending_lock, flags); + + /* + * Make sure that the QP is not in the QPN table so receive + * interrupts will discard packets for this QP. XXX Also remove QP + * from multicast table. + */ + if (atomic_read(&qp->refcount) != 0) + ipath_free_qp(&dev->qp_table, qp); + + vfree(qp->s_wq); + vfree(qp->r_rq.wq); + kfree(qp); + return 0; +} + +/** + * ipath_init_qp_table - initialize the QP table for a device + * @idev: the device who's QP table we're initializing + * @size: the size of the QP table + * + * Returns 0 on success, otherwise returns an errno. + */ +int ipath_init_qp_table(struct ipath_ibdev *idev, int size) +{ + int i; + int ret; + + idev->qp_table.last = 1; /* QPN 0 and 1 are special. */ + idev->qp_table.max = size; + idev->qp_table.nmaps = 1; + idev->qp_table.table = kzalloc(size * sizeof(*idev->qp_table.table), + GFP_KERNEL); + if (idev->qp_table.table == NULL) { + ret = -ENOMEM; + goto bail; + } + + for (i = 0; i < ARRAY_SIZE(idev->qp_table.map); i++) { + atomic_set(&idev->qp_table.map[i].n_free, BITS_PER_PAGE); + idev->qp_table.map[i].page = NULL; + } + + ret = 0; + +bail: + return ret; +} + +/** + * ipath_sqerror_qp - put a QP's send queue into an error state + * @qp: QP who's send queue will be put into an error state + * @wc: the WC responsible for putting the QP in this state + * + * Flushes the send work queue. + * The QP s_lock should be held. + */ + +void ipath_sqerror_qp(struct ipath_qp *qp, struct ib_wc *wc) +{ + struct ipath_ibdev *dev = to_idev(qp->ibqp.device); + struct ipath_swqe *wqe = get_swqe_ptr(qp, qp->s_last); + + _VERBS_INFO("Send queue error on QP%d/%d: err: %d\n", + qp->ibqp.qp_num, qp->remote_qpn, wc->status); + + spin_lock(&dev->pending_lock); + /* XXX What if its already removed by the timeout code? */ + if (qp->timerwait.next != LIST_POISON1) + list_del(&qp->timerwait); + if (qp->piowait.next != LIST_POISON1) + list_del(&qp->piowait); + spin_unlock(&dev->pending_lock); + + ipath_cq_enter(to_icq(qp->ibqp.send_cq), wc, 1); + if (++qp->s_last >= qp->s_size) + qp->s_last = 0; + + wc->status = IB_WC_WR_FLUSH_ERR; + + while (qp->s_last != qp->s_head) { + wc->wr_id = wqe->wr.wr_id; + wc->opcode = ib_ipath_wc_opcode[wqe->wr.opcode]; + ipath_cq_enter(to_icq(qp->ibqp.send_cq), wc, 1); + if (++qp->s_last >= qp->s_size) + qp->s_last = 0; + wqe = get_swqe_ptr(qp, qp->s_last); + } + qp->s_cur = qp->s_tail = qp->s_head; + qp->state = IB_QPS_SQE; +} + +/** + * ipath_error_qp - put a QP into an error state + * @qp: the QP to put into an error state + * + * Flushes both send and receive work queues. + * QP r_rq.lock and s_lock should be held. + */ + +void ipath_error_qp(struct ipath_qp *qp) +{ + struct ipath_ibdev *dev = to_idev(qp->ibqp.device); + struct ib_wc wc; + + _VERBS_INFO("QP%d/%d in error state\n", + qp->ibqp.qp_num, qp->remote_qpn); + + spin_lock(&dev->pending_lock); + /* XXX What if its already removed by the timeout code? */ + if (qp->timerwait.next != LIST_POISON1) + list_del(&qp->timerwait); + if (qp->piowait.next != LIST_POISON1) + list_del(&qp->piowait); + spin_unlock(&dev->pending_lock); + + wc.status = IB_WC_WR_FLUSH_ERR; + wc.vendor_err = 0; + wc.byte_len = 0; + wc.imm_data = 0; + wc.qp_num = qp->ibqp.qp_num; + wc.src_qp = 0; + wc.wc_flags = 0; + wc.pkey_index = 0; + wc.slid = 0; + wc.sl = 0; + wc.dlid_path_bits = 0; + wc.port_num = 0; + + while (qp->s_last != qp->s_head) { + struct ipath_swqe *wqe = get_swqe_ptr(qp, qp->s_last); + + wc.wr_id = wqe->wr.wr_id; + wc.opcode = ib_ipath_wc_opcode[wqe->wr.opcode]; + if (++qp->s_last >= qp->s_size) + qp->s_last = 0; + ipath_cq_enter(to_icq(qp->ibqp.send_cq), &wc, 1); + } + qp->s_cur = qp->s_tail = qp->s_head; + qp->s_hdrwords = 0; + qp->s_ack_state = IB_OPCODE_RC_ACKNOWLEDGE; + + wc.opcode = IB_WC_RECV; + while (qp->r_rq.tail != qp->r_rq.head) { + wc.wr_id = get_rwqe_ptr(&qp->r_rq, qp->r_rq.tail)->wr_id; + if (++qp->r_rq.tail >= qp->r_rq.size) + qp->r_rq.tail = 0; + ipath_cq_enter(to_icq(qp->ibqp.recv_cq), &wc, 1); + } +} + +/** + * ipath_get_credit - flush the send work queue of a QP + * @qp: the qp who's send work queue to flush + * @aeth: the Acknowledge Extended Transport Header + * + * The QP s_lock should be held. + */ +void ipath_get_credit(struct ipath_qp *qp, u32 aeth) +{ + u32 credit = (aeth >> IPS_AETH_CREDIT_SHIFT) & IPS_AETH_CREDIT_MASK; + + /* + * If the credit is invalid, we can send + * as many packets as we like. Otherwise, we have to + * honor the credit field. + */ + if (credit == IPS_AETH_CREDIT_INVAL) { + qp->s_lsn = (u32) -1; + } else if (qp->s_lsn != (u32) -1) { + /* Compute new LSN (i.e., MSN + credit) */ + credit = (aeth + credit_table[credit]) & IPS_MSN_MASK; + if (ipath_cmp24(credit, qp->s_lsn) > 0) + qp->s_lsn = credit; + } + + /* Restart sending if it was blocked due to lack of credits. */ + if (qp->s_cur != qp->s_head && + (qp->s_lsn == (u32) -1 || + ipath_cmp24(get_swqe_ptr(qp, qp->s_cur)->ssn, + qp->s_lsn + 1) <= 0)) + tasklet_hi_schedule(&qp->s_task); +} diff --git a/drivers/infiniband/hw/ipath/ipath_rc.c b/drivers/infiniband/hw/ipath/ipath_rc.c new file mode 100644 index 000000000000..a4055ca00614 --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_rc.c @@ -0,0 +1,1857 @@ +/* + * Copyright (c) 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ipath_verbs.h" +#include "ips_common.h" + +/* cut down ridiculously long IB macro names */ +#define OP(x) IB_OPCODE_RC_##x + +/** + * ipath_init_restart- initialize the qp->s_sge after a restart + * @qp: the QP who's SGE we're restarting + * @wqe: the work queue to initialize the QP's SGE from + * + * The QP s_lock should be held. + */ +static void ipath_init_restart(struct ipath_qp *qp, struct ipath_swqe *wqe) +{ + struct ipath_ibdev *dev; + u32 len; + + len = ((qp->s_psn - wqe->psn) & IPS_PSN_MASK) * + ib_mtu_enum_to_int(qp->path_mtu); + qp->s_sge.sge = wqe->sg_list[0]; + qp->s_sge.sg_list = wqe->sg_list + 1; + qp->s_sge.num_sge = wqe->wr.num_sge; + ipath_skip_sge(&qp->s_sge, len); + qp->s_len = wqe->length - len; + dev = to_idev(qp->ibqp.device); + spin_lock(&dev->pending_lock); + if (qp->timerwait.next == LIST_POISON1) + list_add_tail(&qp->timerwait, + &dev->pending[dev->pending_index]); + spin_unlock(&dev->pending_lock); +} + +/** + * ipath_make_rc_ack - construct a response packet (ACK, NAK, or RDMA read) + * @qp: a pointer to the QP + * @ohdr: a pointer to the IB header being constructed + * @pmtu: the path MTU + * + * Return bth0 if constructed; otherwise, return 0. + * Note the QP s_lock must be held. + */ +static inline u32 ipath_make_rc_ack(struct ipath_qp *qp, + struct ipath_other_headers *ohdr, + u32 pmtu) +{ + struct ipath_sge_state *ss; + u32 hwords; + u32 len; + u32 bth0; + + /* header size in 32-bit words LRH+BTH = (8+12)/4. */ + hwords = 5; + + /* + * Send a response. Note that we are in the responder's + * side of the QP context. + */ + switch (qp->s_ack_state) { + case OP(RDMA_READ_REQUEST): + ss = &qp->s_rdma_sge; + len = qp->s_rdma_len; + if (len > pmtu) { + len = pmtu; + qp->s_ack_state = OP(RDMA_READ_RESPONSE_FIRST); + } + else + qp->s_ack_state = OP(RDMA_READ_RESPONSE_ONLY); + qp->s_rdma_len -= len; + bth0 = qp->s_ack_state << 24; + ohdr->u.aeth = ipath_compute_aeth(qp); + hwords++; + break; + + case OP(RDMA_READ_RESPONSE_FIRST): + qp->s_ack_state = OP(RDMA_READ_RESPONSE_MIDDLE); + /* FALLTHROUGH */ + case OP(RDMA_READ_RESPONSE_MIDDLE): + ss = &qp->s_rdma_sge; + len = qp->s_rdma_len; + if (len > pmtu) + len = pmtu; + else { + ohdr->u.aeth = ipath_compute_aeth(qp); + hwords++; + qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST); + } + qp->s_rdma_len -= len; + bth0 = qp->s_ack_state << 24; + break; + + case OP(RDMA_READ_RESPONSE_LAST): + case OP(RDMA_READ_RESPONSE_ONLY): + /* + * We have to prevent new requests from changing + * the r_sge state while a ipath_verbs_send() + * is in progress. + * Changing r_state allows the receiver + * to continue processing new packets. + * We do it here now instead of above so + * that we are sure the packet was sent before + * changing the state. + */ + qp->r_state = OP(RDMA_READ_RESPONSE_LAST); + qp->s_ack_state = OP(ACKNOWLEDGE); + return 0; + + case OP(COMPARE_SWAP): + case OP(FETCH_ADD): + ss = NULL; + len = 0; + qp->r_state = OP(SEND_LAST); + qp->s_ack_state = OP(ACKNOWLEDGE); + bth0 = IB_OPCODE_ATOMIC_ACKNOWLEDGE << 24; + ohdr->u.at.aeth = ipath_compute_aeth(qp); + ohdr->u.at.atomic_ack_eth = cpu_to_be64(qp->s_ack_atomic); + hwords += sizeof(ohdr->u.at) / 4; + break; + + default: + /* Send a regular ACK. */ + ss = NULL; + len = 0; + qp->s_ack_state = OP(ACKNOWLEDGE); + bth0 = qp->s_ack_state << 24; + ohdr->u.aeth = ipath_compute_aeth(qp); + hwords++; + } + qp->s_hdrwords = hwords; + qp->s_cur_sge = ss; + qp->s_cur_size = len; + + return bth0; +} + +/** + * ipath_make_rc_req - construct a request packet (SEND, RDMA r/w, ATOMIC) + * @qp: a pointer to the QP + * @ohdr: a pointer to the IB header being constructed + * @pmtu: the path MTU + * @bth0p: pointer to the BTH opcode word + * @bth2p: pointer to the BTH PSN word + * + * Return 1 if constructed; otherwise, return 0. + * Note the QP s_lock must be held. + */ +static inline int ipath_make_rc_req(struct ipath_qp *qp, + struct ipath_other_headers *ohdr, + u32 pmtu, u32 *bth0p, u32 *bth2p) +{ + struct ipath_ibdev *dev = to_idev(qp->ibqp.device); + struct ipath_sge_state *ss; + struct ipath_swqe *wqe; + u32 hwords; + u32 len; + u32 bth0; + u32 bth2; + char newreq; + + if (!(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_SEND_OK) || + qp->s_rnr_timeout) + goto done; + + /* header size in 32-bit words LRH+BTH = (8+12)/4. */ + hwords = 5; + bth0 = 0; + + /* Send a request. */ + wqe = get_swqe_ptr(qp, qp->s_cur); + switch (qp->s_state) { + default: + /* + * Resend an old request or start a new one. + * + * We keep track of the current SWQE so that + * we don't reset the "furthest progress" state + * if we need to back up. + */ + newreq = 0; + if (qp->s_cur == qp->s_tail) { + /* Check if send work queue is empty. */ + if (qp->s_tail == qp->s_head) + goto done; + qp->s_psn = wqe->psn = qp->s_next_psn; + newreq = 1; + } + /* + * Note that we have to be careful not to modify the + * original work request since we may need to resend + * it. + */ + qp->s_sge.sge = wqe->sg_list[0]; + qp->s_sge.sg_list = wqe->sg_list + 1; + qp->s_sge.num_sge = wqe->wr.num_sge; + qp->s_len = len = wqe->length; + ss = &qp->s_sge; + bth2 = 0; + switch (wqe->wr.opcode) { + case IB_WR_SEND: + case IB_WR_SEND_WITH_IMM: + /* If no credit, return. */ + if (qp->s_lsn != (u32) -1 && + ipath_cmp24(wqe->ssn, qp->s_lsn + 1) > 0) + goto done; + wqe->lpsn = wqe->psn; + if (len > pmtu) { + wqe->lpsn += (len - 1) / pmtu; + qp->s_state = OP(SEND_FIRST); + len = pmtu; + break; + } + if (wqe->wr.opcode == IB_WR_SEND) + qp->s_state = OP(SEND_ONLY); + else { + qp->s_state = OP(SEND_ONLY_WITH_IMMEDIATE); + /* Immediate data comes after the BTH */ + ohdr->u.imm_data = wqe->wr.imm_data; + hwords += 1; + } + if (wqe->wr.send_flags & IB_SEND_SOLICITED) + bth0 |= 1 << 23; + bth2 = 1 << 31; /* Request ACK. */ + if (++qp->s_cur == qp->s_size) + qp->s_cur = 0; + break; + + case IB_WR_RDMA_WRITE: + if (newreq) + qp->s_lsn++; + /* FALLTHROUGH */ + case IB_WR_RDMA_WRITE_WITH_IMM: + /* If no credit, return. */ + if (qp->s_lsn != (u32) -1 && + ipath_cmp24(wqe->ssn, qp->s_lsn + 1) > 0) + goto done; + ohdr->u.rc.reth.vaddr = + cpu_to_be64(wqe->wr.wr.rdma.remote_addr); + ohdr->u.rc.reth.rkey = + cpu_to_be32(wqe->wr.wr.rdma.rkey); + ohdr->u.rc.reth.length = cpu_to_be32(len); + hwords += sizeof(struct ib_reth) / 4; + wqe->lpsn = wqe->psn; + if (len > pmtu) { + wqe->lpsn += (len - 1) / pmtu; + qp->s_state = OP(RDMA_WRITE_FIRST); + len = pmtu; + break; + } + if (wqe->wr.opcode == IB_WR_RDMA_WRITE) + qp->s_state = OP(RDMA_WRITE_ONLY); + else { + qp->s_state = + OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE); + /* Immediate data comes + * after RETH */ + ohdr->u.rc.imm_data = wqe->wr.imm_data; + hwords += 1; + if (wqe->wr.send_flags & IB_SEND_SOLICITED) + bth0 |= 1 << 23; + } + bth2 = 1 << 31; /* Request ACK. */ + if (++qp->s_cur == qp->s_size) + qp->s_cur = 0; + break; + + case IB_WR_RDMA_READ: + ohdr->u.rc.reth.vaddr = + cpu_to_be64(wqe->wr.wr.rdma.remote_addr); + ohdr->u.rc.reth.rkey = + cpu_to_be32(wqe->wr.wr.rdma.rkey); + ohdr->u.rc.reth.length = cpu_to_be32(len); + qp->s_state = OP(RDMA_READ_REQUEST); + hwords += sizeof(ohdr->u.rc.reth) / 4; + if (newreq) { + qp->s_lsn++; + /* + * Adjust s_next_psn to count the + * expected number of responses. + */ + if (len > pmtu) + qp->s_next_psn += (len - 1) / pmtu; + wqe->lpsn = qp->s_next_psn++; + } + ss = NULL; + len = 0; + if (++qp->s_cur == qp->s_size) + qp->s_cur = 0; + break; + + case IB_WR_ATOMIC_CMP_AND_SWP: + case IB_WR_ATOMIC_FETCH_AND_ADD: + if (wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP) + qp->s_state = OP(COMPARE_SWAP); + else + qp->s_state = OP(FETCH_ADD); + ohdr->u.atomic_eth.vaddr = cpu_to_be64( + wqe->wr.wr.atomic.remote_addr); + ohdr->u.atomic_eth.rkey = cpu_to_be32( + wqe->wr.wr.atomic.rkey); + ohdr->u.atomic_eth.swap_data = cpu_to_be64( + wqe->wr.wr.atomic.swap); + ohdr->u.atomic_eth.compare_data = cpu_to_be64( + wqe->wr.wr.atomic.compare_add); + hwords += sizeof(struct ib_atomic_eth) / 4; + if (newreq) { + qp->s_lsn++; + wqe->lpsn = wqe->psn; + } + if (++qp->s_cur == qp->s_size) + qp->s_cur = 0; + ss = NULL; + len = 0; + break; + + default: + goto done; + } + if (newreq) { + qp->s_tail++; + if (qp->s_tail >= qp->s_size) + qp->s_tail = 0; + } + bth2 |= qp->s_psn++ & IPS_PSN_MASK; + if ((int)(qp->s_psn - qp->s_next_psn) > 0) + qp->s_next_psn = qp->s_psn; + spin_lock(&dev->pending_lock); + if (qp->timerwait.next == LIST_POISON1) + list_add_tail(&qp->timerwait, + &dev->pending[dev->pending_index]); + spin_unlock(&dev->pending_lock); + break; + + case OP(RDMA_READ_RESPONSE_FIRST): + /* + * This case can only happen if a send is restarted. See + * ipath_restart_rc(). + */ + ipath_init_restart(qp, wqe); + /* FALLTHROUGH */ + case OP(SEND_FIRST): + qp->s_state = OP(SEND_MIDDLE); + /* FALLTHROUGH */ + case OP(SEND_MIDDLE): + bth2 = qp->s_psn++ & IPS_PSN_MASK; + if ((int)(qp->s_psn - qp->s_next_psn) > 0) + qp->s_next_psn = qp->s_psn; + ss = &qp->s_sge; + len = qp->s_len; + if (len > pmtu) { + /* + * Request an ACK every 1/2 MB to avoid retransmit + * timeouts. + */ + if (((wqe->length - len) % (512 * 1024)) == 0) + bth2 |= 1 << 31; + len = pmtu; + break; + } + if (wqe->wr.opcode == IB_WR_SEND) + qp->s_state = OP(SEND_LAST); + else { + qp->s_state = OP(SEND_LAST_WITH_IMMEDIATE); + /* Immediate data comes after the BTH */ + ohdr->u.imm_data = wqe->wr.imm_data; + hwords += 1; + } + if (wqe->wr.send_flags & IB_SEND_SOLICITED) + bth0 |= 1 << 23; + bth2 |= 1 << 31; /* Request ACK. */ + qp->s_cur++; + if (qp->s_cur >= qp->s_size) + qp->s_cur = 0; + break; + + case OP(RDMA_READ_RESPONSE_LAST): + /* + * This case can only happen if a RDMA write is restarted. + * See ipath_restart_rc(). + */ + ipath_init_restart(qp, wqe); + /* FALLTHROUGH */ + case OP(RDMA_WRITE_FIRST): + qp->s_state = OP(RDMA_WRITE_MIDDLE); + /* FALLTHROUGH */ + case OP(RDMA_WRITE_MIDDLE): + bth2 = qp->s_psn++ & IPS_PSN_MASK; + if ((int)(qp->s_psn - qp->s_next_psn) > 0) + qp->s_next_psn = qp->s_psn; + ss = &qp->s_sge; + len = qp->s_len; + if (len > pmtu) { + /* + * Request an ACK every 1/2 MB to avoid retransmit + * timeouts. + */ + if (((wqe->length - len) % (512 * 1024)) == 0) + bth2 |= 1 << 31; + len = pmtu; + break; + } + if (wqe->wr.opcode == IB_WR_RDMA_WRITE) + qp->s_state = OP(RDMA_WRITE_LAST); + else { + qp->s_state = OP(RDMA_WRITE_LAST_WITH_IMMEDIATE); + /* Immediate data comes after the BTH */ + ohdr->u.imm_data = wqe->wr.imm_data; + hwords += 1; + if (wqe->wr.send_flags & IB_SEND_SOLICITED) + bth0 |= 1 << 23; + } + bth2 |= 1 << 31; /* Request ACK. */ + qp->s_cur++; + if (qp->s_cur >= qp->s_size) + qp->s_cur = 0; + break; + + case OP(RDMA_READ_RESPONSE_MIDDLE): + /* + * This case can only happen if a RDMA read is restarted. + * See ipath_restart_rc(). + */ + ipath_init_restart(qp, wqe); + len = ((qp->s_psn - wqe->psn) & IPS_PSN_MASK) * pmtu; + ohdr->u.rc.reth.vaddr = + cpu_to_be64(wqe->wr.wr.rdma.remote_addr + len); + ohdr->u.rc.reth.rkey = + cpu_to_be32(wqe->wr.wr.rdma.rkey); + ohdr->u.rc.reth.length = cpu_to_be32(qp->s_len); + qp->s_state = OP(RDMA_READ_REQUEST); + hwords += sizeof(ohdr->u.rc.reth) / 4; + bth2 = qp->s_psn++ & IPS_PSN_MASK; + if ((int)(qp->s_psn - qp->s_next_psn) > 0) + qp->s_next_psn = qp->s_psn; + ss = NULL; + len = 0; + qp->s_cur++; + if (qp->s_cur == qp->s_size) + qp->s_cur = 0; + break; + + case OP(RDMA_READ_REQUEST): + case OP(COMPARE_SWAP): + case OP(FETCH_ADD): + /* + * We shouldn't start anything new until this request is + * finished. The ACK will handle rescheduling us. XXX The + * number of outstanding ones is negotiated at connection + * setup time (see pg. 258,289)? XXX Also, if we support + * multiple outstanding requests, we need to check the WQE + * IB_SEND_FENCE flag and not send a new request if a RDMA + * read or atomic is pending. + */ + goto done; + } + qp->s_len -= len; + qp->s_hdrwords = hwords; + qp->s_cur_sge = ss; + qp->s_cur_size = len; + *bth0p = bth0 | (qp->s_state << 24); + *bth2p = bth2; + return 1; + +done: + return 0; +} + +static inline void ipath_make_rc_grh(struct ipath_qp *qp, + struct ib_global_route *grh, + u32 nwords) +{ + struct ipath_ibdev *dev = to_idev(qp->ibqp.device); + + /* GRH header size in 32-bit words. */ + qp->s_hdrwords += 10; + qp->s_hdr.u.l.grh.version_tclass_flow = + cpu_to_be32((6 << 28) | + (grh->traffic_class << 20) | + grh->flow_label); + qp->s_hdr.u.l.grh.paylen = + cpu_to_be16(((qp->s_hdrwords - 12) + nwords + + SIZE_OF_CRC) << 2); + /* next_hdr is defined by C8-7 in ch. 8.4.1 */ + qp->s_hdr.u.l.grh.next_hdr = 0x1B; + qp->s_hdr.u.l.grh.hop_limit = grh->hop_limit; + /* The SGID is 32-bit aligned. */ + qp->s_hdr.u.l.grh.sgid.global.subnet_prefix = dev->gid_prefix; + qp->s_hdr.u.l.grh.sgid.global.interface_id = + ipath_layer_get_guid(dev->dd); + qp->s_hdr.u.l.grh.dgid = grh->dgid; +} + +/** + * ipath_do_rc_send - perform a send on an RC QP + * @data: contains a pointer to the QP + * + * Process entries in the send work queue until credit or queue is + * exhausted. Only allow one CPU to send a packet per QP (tasklet). + * Otherwise, after we drop the QP s_lock, two threads could send + * packets out of order. + */ +void ipath_do_rc_send(unsigned long data) +{ + struct ipath_qp *qp = (struct ipath_qp *)data; + struct ipath_ibdev *dev = to_idev(qp->ibqp.device); + unsigned long flags; + u16 lrh0; + u32 nwords; + u32 extra_bytes; + u32 bth0; + u32 bth2; + u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu); + struct ipath_other_headers *ohdr; + + if (test_and_set_bit(IPATH_S_BUSY, &qp->s_flags)) + goto bail; + + if (unlikely(qp->remote_ah_attr.dlid == + ipath_layer_get_lid(dev->dd))) { + struct ib_wc wc; + + /* + * Pass in an uninitialized ib_wc to be consistent with + * other places where ipath_ruc_loopback() is called. + */ + ipath_ruc_loopback(qp, &wc); + goto clear; + } + + ohdr = &qp->s_hdr.u.oth; + if (qp->remote_ah_attr.ah_flags & IB_AH_GRH) + ohdr = &qp->s_hdr.u.l.oth; + +again: + /* Check for a constructed packet to be sent. */ + if (qp->s_hdrwords != 0) { + /* + * If no PIO bufs are available, return. An interrupt will + * call ipath_ib_piobufavail() when one is available. + */ + _VERBS_INFO("h %u %p\n", qp->s_hdrwords, &qp->s_hdr); + _VERBS_INFO("d %u %p %u %p %u %u %u %u\n", qp->s_cur_size, + qp->s_cur_sge->sg_list, + qp->s_cur_sge->num_sge, + qp->s_cur_sge->sge.vaddr, + qp->s_cur_sge->sge.sge_length, + qp->s_cur_sge->sge.length, + qp->s_cur_sge->sge.m, + qp->s_cur_sge->sge.n); + if (ipath_verbs_send(dev->dd, qp->s_hdrwords, + (u32 *) &qp->s_hdr, qp->s_cur_size, + qp->s_cur_sge)) { + ipath_no_bufs_available(qp, dev); + goto bail; + } + dev->n_unicast_xmit++; + /* Record that we sent the packet and s_hdr is empty. */ + qp->s_hdrwords = 0; + } + + /* + * The lock is needed to synchronize between setting + * qp->s_ack_state, resend timer, and post_send(). + */ + spin_lock_irqsave(&qp->s_lock, flags); + + /* Sending responses has higher priority over sending requests. */ + if (qp->s_ack_state != OP(ACKNOWLEDGE) && + (bth0 = ipath_make_rc_ack(qp, ohdr, pmtu)) != 0) + bth2 = qp->s_ack_psn++ & IPS_PSN_MASK; + else if (!ipath_make_rc_req(qp, ohdr, pmtu, &bth0, &bth2)) + goto done; + + spin_unlock_irqrestore(&qp->s_lock, flags); + + /* Construct the header. */ + extra_bytes = (4 - qp->s_cur_size) & 3; + nwords = (qp->s_cur_size + extra_bytes) >> 2; + lrh0 = IPS_LRH_BTH; + if (unlikely(qp->remote_ah_attr.ah_flags & IB_AH_GRH)) { + ipath_make_rc_grh(qp, &qp->remote_ah_attr.grh, nwords); + lrh0 = IPS_LRH_GRH; + } + lrh0 |= qp->remote_ah_attr.sl << 4; + qp->s_hdr.lrh[0] = cpu_to_be16(lrh0); + qp->s_hdr.lrh[1] = cpu_to_be16(qp->remote_ah_attr.dlid); + qp->s_hdr.lrh[2] = cpu_to_be16(qp->s_hdrwords + nwords + + SIZE_OF_CRC); + qp->s_hdr.lrh[3] = cpu_to_be16(ipath_layer_get_lid(dev->dd)); + bth0 |= ipath_layer_get_pkey(dev->dd, qp->s_pkey_index); + bth0 |= extra_bytes << 20; + ohdr->bth[0] = cpu_to_be32(bth0); + ohdr->bth[1] = cpu_to_be32(qp->remote_qpn); + ohdr->bth[2] = cpu_to_be32(bth2); + + /* Check for more work to do. */ + goto again; + +done: + spin_unlock_irqrestore(&qp->s_lock, flags); +clear: + clear_bit(IPATH_S_BUSY, &qp->s_flags); +bail: + return; +} + +static void send_rc_ack(struct ipath_qp *qp) +{ + struct ipath_ibdev *dev = to_idev(qp->ibqp.device); + u16 lrh0; + u32 bth0; + struct ipath_other_headers *ohdr; + + /* Construct the header. */ + ohdr = &qp->s_hdr.u.oth; + lrh0 = IPS_LRH_BTH; + /* header size in 32-bit words LRH+BTH+AETH = (8+12+4)/4. */ + qp->s_hdrwords = 6; + if (unlikely(qp->remote_ah_attr.ah_flags & IB_AH_GRH)) { + ipath_make_rc_grh(qp, &qp->remote_ah_attr.grh, 0); + ohdr = &qp->s_hdr.u.l.oth; + lrh0 = IPS_LRH_GRH; + } + bth0 = ipath_layer_get_pkey(dev->dd, qp->s_pkey_index); + ohdr->u.aeth = ipath_compute_aeth(qp); + if (qp->s_ack_state >= OP(COMPARE_SWAP)) { + bth0 |= IB_OPCODE_ATOMIC_ACKNOWLEDGE << 24; + ohdr->u.at.atomic_ack_eth = cpu_to_be64(qp->s_ack_atomic); + qp->s_hdrwords += sizeof(ohdr->u.at.atomic_ack_eth) / 4; + } + else + bth0 |= OP(ACKNOWLEDGE) << 24; + lrh0 |= qp->remote_ah_attr.sl << 4; + qp->s_hdr.lrh[0] = cpu_to_be16(lrh0); + qp->s_hdr.lrh[1] = cpu_to_be16(qp->remote_ah_attr.dlid); + qp->s_hdr.lrh[2] = cpu_to_be16(qp->s_hdrwords + SIZE_OF_CRC); + qp->s_hdr.lrh[3] = cpu_to_be16(ipath_layer_get_lid(dev->dd)); + ohdr->bth[0] = cpu_to_be32(bth0); + ohdr->bth[1] = cpu_to_be32(qp->remote_qpn); + ohdr->bth[2] = cpu_to_be32(qp->s_ack_psn & IPS_PSN_MASK); + + /* + * If we can send the ACK, clear the ACK state. + */ + if (ipath_verbs_send(dev->dd, qp->s_hdrwords, (u32 *) &qp->s_hdr, + 0, NULL) == 0) { + qp->s_ack_state = OP(ACKNOWLEDGE); + dev->n_rc_qacks++; + dev->n_unicast_xmit++; + } +} + +/** + * ipath_restart_rc - back up requester to resend the last un-ACKed request + * @qp: the QP to restart + * @psn: packet sequence number for the request + * @wc: the work completion request + * + * The QP s_lock should be held. + */ +void ipath_restart_rc(struct ipath_qp *qp, u32 psn, struct ib_wc *wc) +{ + struct ipath_swqe *wqe = get_swqe_ptr(qp, qp->s_last); + struct ipath_ibdev *dev; + u32 n; + + /* + * If there are no requests pending, we are done. + */ + if (ipath_cmp24(psn, qp->s_next_psn) >= 0 || + qp->s_last == qp->s_tail) + goto done; + + if (qp->s_retry == 0) { + wc->wr_id = wqe->wr.wr_id; + wc->status = IB_WC_RETRY_EXC_ERR; + wc->opcode = ib_ipath_wc_opcode[wqe->wr.opcode]; + wc->vendor_err = 0; + wc->byte_len = 0; + wc->qp_num = qp->ibqp.qp_num; + wc->src_qp = qp->remote_qpn; + wc->pkey_index = 0; + wc->slid = qp->remote_ah_attr.dlid; + wc->sl = qp->remote_ah_attr.sl; + wc->dlid_path_bits = 0; + wc->port_num = 0; + ipath_sqerror_qp(qp, wc); + goto bail; + } + qp->s_retry--; + + /* + * Remove the QP from the timeout queue. + * Note: it may already have been removed by ipath_ib_timer(). + */ + dev = to_idev(qp->ibqp.device); + spin_lock(&dev->pending_lock); + if (qp->timerwait.next != LIST_POISON1) + list_del(&qp->timerwait); + spin_unlock(&dev->pending_lock); + + if (wqe->wr.opcode == IB_WR_RDMA_READ) + dev->n_rc_resends++; + else + dev->n_rc_resends += (int)qp->s_psn - (int)psn; + + /* + * If we are starting the request from the beginning, let the normal + * send code handle initialization. + */ + qp->s_cur = qp->s_last; + if (ipath_cmp24(psn, wqe->psn) <= 0) { + qp->s_state = OP(SEND_LAST); + qp->s_psn = wqe->psn; + } else { + n = qp->s_cur; + for (;;) { + if (++n == qp->s_size) + n = 0; + if (n == qp->s_tail) { + if (ipath_cmp24(psn, qp->s_next_psn) >= 0) { + qp->s_cur = n; + wqe = get_swqe_ptr(qp, n); + } + break; + } + wqe = get_swqe_ptr(qp, n); + if (ipath_cmp24(psn, wqe->psn) < 0) + break; + qp->s_cur = n; + } + qp->s_psn = psn; + + /* + * Reset the state to restart in the middle of a request. + * Don't change the s_sge, s_cur_sge, or s_cur_size. + * See ipath_do_rc_send(). + */ + switch (wqe->wr.opcode) { + case IB_WR_SEND: + case IB_WR_SEND_WITH_IMM: + qp->s_state = OP(RDMA_READ_RESPONSE_FIRST); + break; + + case IB_WR_RDMA_WRITE: + case IB_WR_RDMA_WRITE_WITH_IMM: + qp->s_state = OP(RDMA_READ_RESPONSE_LAST); + break; + + case IB_WR_RDMA_READ: + qp->s_state = + OP(RDMA_READ_RESPONSE_MIDDLE); + break; + + default: + /* + * This case shouldn't happen since its only + * one PSN per req. + */ + qp->s_state = OP(SEND_LAST); + } + } + +done: + tasklet_hi_schedule(&qp->s_task); + +bail: + return; +} + +/** + * reset_psn - reset the QP state to send starting from PSN + * @qp: the QP + * @psn: the packet sequence number to restart at + * + * This is called from ipath_rc_rcv() to process an incoming RC ACK + * for the given QP. + * Called at interrupt level with the QP s_lock held. + */ +static void reset_psn(struct ipath_qp *qp, u32 psn) +{ + struct ipath_swqe *wqe; + u32 n; + + n = qp->s_cur; + wqe = get_swqe_ptr(qp, n); + for (;;) { + if (++n == qp->s_size) + n = 0; + if (n == qp->s_tail) { + if (ipath_cmp24(psn, qp->s_next_psn) >= 0) { + qp->s_cur = n; + wqe = get_swqe_ptr(qp, n); + } + break; + } + wqe = get_swqe_ptr(qp, n); + if (ipath_cmp24(psn, wqe->psn) < 0) + break; + qp->s_cur = n; + } + qp->s_psn = psn; + + /* + * Set the state to restart in the middle of a + * request. Don't change the s_sge, s_cur_sge, or + * s_cur_size. See ipath_do_rc_send(). + */ + switch (wqe->wr.opcode) { + case IB_WR_SEND: + case IB_WR_SEND_WITH_IMM: + qp->s_state = OP(RDMA_READ_RESPONSE_FIRST); + break; + + case IB_WR_RDMA_WRITE: + case IB_WR_RDMA_WRITE_WITH_IMM: + qp->s_state = OP(RDMA_READ_RESPONSE_LAST); + break; + + case IB_WR_RDMA_READ: + qp->s_state = OP(RDMA_READ_RESPONSE_MIDDLE); + break; + + default: + /* + * This case shouldn't happen since its only + * one PSN per req. + */ + qp->s_state = OP(SEND_LAST); + } +} + +/** + * do_rc_ack - process an incoming RC ACK + * @qp: the QP the ACK came in on + * @psn: the packet sequence number of the ACK + * @opcode: the opcode of the request that resulted in the ACK + * + * This is called from ipath_rc_rcv() to process an incoming RC ACK + * for the given QP. + * Called at interrupt level with the QP s_lock held. + * Returns 1 if OK, 0 if current operation should be aborted (NAK). + */ +static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode) +{ + struct ipath_ibdev *dev = to_idev(qp->ibqp.device); + struct ib_wc wc; + struct ipath_swqe *wqe; + int ret = 0; + + /* + * Remove the QP from the timeout queue (or RNR timeout queue). + * If ipath_ib_timer() has already removed it, + * it's OK since we hold the QP s_lock and ipath_restart_rc() + * just won't find anything to restart if we ACK everything. + */ + spin_lock(&dev->pending_lock); + if (qp->timerwait.next != LIST_POISON1) + list_del(&qp->timerwait); + spin_unlock(&dev->pending_lock); + + /* + * Note that NAKs implicitly ACK outstanding SEND and RDMA write + * requests and implicitly NAK RDMA read and atomic requests issued + * before the NAK'ed request. The MSN won't include the NAK'ed + * request but will include an ACK'ed request(s). + */ + wqe = get_swqe_ptr(qp, qp->s_last); + + /* Nothing is pending to ACK/NAK. */ + if (qp->s_last == qp->s_tail) + goto bail; + + /* + * The MSN might be for a later WQE than the PSN indicates so + * only complete WQEs that the PSN finishes. + */ + while (ipath_cmp24(psn, wqe->lpsn) >= 0) { + /* If we are ACKing a WQE, the MSN should be >= the SSN. */ + if (ipath_cmp24(aeth, wqe->ssn) < 0) + break; + /* + * If this request is a RDMA read or atomic, and the ACK is + * for a later operation, this ACK NAKs the RDMA read or + * atomic. In other words, only a RDMA_READ_LAST or ONLY + * can ACK a RDMA read and likewise for atomic ops. Note + * that the NAK case can only happen if relaxed ordering is + * used and requests are sent after an RDMA read or atomic + * is sent but before the response is received. + */ + if ((wqe->wr.opcode == IB_WR_RDMA_READ && + opcode != OP(RDMA_READ_RESPONSE_LAST)) || + ((wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP || + wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) && + (opcode != OP(ATOMIC_ACKNOWLEDGE) || + ipath_cmp24(wqe->psn, psn) != 0))) { + /* + * The last valid PSN seen is the previous + * request's. + */ + qp->s_last_psn = wqe->psn - 1; + /* Retry this request. */ + ipath_restart_rc(qp, wqe->psn, &wc); + /* + * No need to process the ACK/NAK since we are + * restarting an earlier request. + */ + goto bail; + } + /* Post a send completion queue entry if requested. */ + if (!test_bit(IPATH_S_SIGNAL_REQ_WR, &qp->s_flags) || + (wqe->wr.send_flags & IB_SEND_SIGNALED)) { + wc.wr_id = wqe->wr.wr_id; + wc.status = IB_WC_SUCCESS; + wc.opcode = ib_ipath_wc_opcode[wqe->wr.opcode]; + wc.vendor_err = 0; + wc.byte_len = wqe->length; + wc.qp_num = qp->ibqp.qp_num; + wc.src_qp = qp->remote_qpn; + wc.pkey_index = 0; + wc.slid = qp->remote_ah_attr.dlid; + wc.sl = qp->remote_ah_attr.sl; + wc.dlid_path_bits = 0; + wc.port_num = 0; + ipath_cq_enter(to_icq(qp->ibqp.send_cq), &wc, 0); + } + qp->s_retry = qp->s_retry_cnt; + /* + * If we are completing a request which is in the process of + * being resent, we can stop resending it since we know the + * responder has already seen it. + */ + if (qp->s_last == qp->s_cur) { + if (++qp->s_cur >= qp->s_size) + qp->s_cur = 0; + wqe = get_swqe_ptr(qp, qp->s_cur); + qp->s_state = OP(SEND_LAST); + qp->s_psn = wqe->psn; + } + if (++qp->s_last >= qp->s_size) + qp->s_last = 0; + wqe = get_swqe_ptr(qp, qp->s_last); + if (qp->s_last == qp->s_tail) + break; + } + + switch (aeth >> 29) { + case 0: /* ACK */ + dev->n_rc_acks++; + /* If this is a partial ACK, reset the retransmit timer. */ + if (qp->s_last != qp->s_tail) { + spin_lock(&dev->pending_lock); + list_add_tail(&qp->timerwait, + &dev->pending[dev->pending_index]); + spin_unlock(&dev->pending_lock); + } + ipath_get_credit(qp, aeth); + qp->s_rnr_retry = qp->s_rnr_retry_cnt; + qp->s_retry = qp->s_retry_cnt; + qp->s_last_psn = psn; + ret = 1; + goto bail; + + case 1: /* RNR NAK */ + dev->n_rnr_naks++; + if (qp->s_rnr_retry == 0) { + if (qp->s_last == qp->s_tail) + goto bail; + + wc.status = IB_WC_RNR_RETRY_EXC_ERR; + goto class_b; + } + if (qp->s_rnr_retry_cnt < 7) + qp->s_rnr_retry--; + if (qp->s_last == qp->s_tail) + goto bail; + + /* The last valid PSN seen is the previous request's. */ + qp->s_last_psn = wqe->psn - 1; + + dev->n_rc_resends += (int)qp->s_psn - (int)psn; + + /* + * If we are starting the request from the beginning, let + * the normal send code handle initialization. + */ + qp->s_cur = qp->s_last; + wqe = get_swqe_ptr(qp, qp->s_cur); + if (ipath_cmp24(psn, wqe->psn) <= 0) { + qp->s_state = OP(SEND_LAST); + qp->s_psn = wqe->psn; + } else + reset_psn(qp, psn); + + qp->s_rnr_timeout = + ib_ipath_rnr_table[(aeth >> IPS_AETH_CREDIT_SHIFT) & + IPS_AETH_CREDIT_MASK]; + ipath_insert_rnr_queue(qp); + goto bail; + + case 3: /* NAK */ + /* The last valid PSN seen is the previous request's. */ + if (qp->s_last != qp->s_tail) + qp->s_last_psn = wqe->psn - 1; + switch ((aeth >> IPS_AETH_CREDIT_SHIFT) & + IPS_AETH_CREDIT_MASK) { + case 0: /* PSN sequence error */ + dev->n_seq_naks++; + /* + * Back up to the responder's expected PSN. XXX + * Note that we might get a NAK in the middle of an + * RDMA READ response which terminates the RDMA + * READ. + */ + if (qp->s_last == qp->s_tail) + break; + + if (ipath_cmp24(psn, wqe->psn) < 0) + break; + + /* Retry the request. */ + ipath_restart_rc(qp, psn, &wc); + break; + + case 1: /* Invalid Request */ + wc.status = IB_WC_REM_INV_REQ_ERR; + dev->n_other_naks++; + goto class_b; + + case 2: /* Remote Access Error */ + wc.status = IB_WC_REM_ACCESS_ERR; + dev->n_other_naks++; + goto class_b; + + case 3: /* Remote Operation Error */ + wc.status = IB_WC_REM_OP_ERR; + dev->n_other_naks++; + class_b: + wc.wr_id = wqe->wr.wr_id; + wc.opcode = ib_ipath_wc_opcode[wqe->wr.opcode]; + wc.vendor_err = 0; + wc.byte_len = 0; + wc.qp_num = qp->ibqp.qp_num; + wc.src_qp = qp->remote_qpn; + wc.pkey_index = 0; + wc.slid = qp->remote_ah_attr.dlid; + wc.sl = qp->remote_ah_attr.sl; + wc.dlid_path_bits = 0; + wc.port_num = 0; + ipath_sqerror_qp(qp, &wc); + break; + + default: + /* Ignore other reserved NAK error codes */ + goto reserved; + } + qp->s_rnr_retry = qp->s_rnr_retry_cnt; + goto bail; + + default: /* 2: reserved */ + reserved: + /* Ignore reserved NAK codes. */ + goto bail; + } + +bail: + return ret; +} + +/** + * ipath_rc_rcv_resp - process an incoming RC response packet + * @dev: the device this packet came in on + * @ohdr: the other headers for this packet + * @data: the packet data + * @tlen: the packet length + * @qp: the QP for this packet + * @opcode: the opcode for this packet + * @psn: the packet sequence number for this packet + * @hdrsize: the header length + * @pmtu: the path MTU + * @header_in_data: true if part of the header data is in the data buffer + * + * This is called from ipath_rc_rcv() to process an incoming RC response + * packet for the given QP. + * Called at interrupt level. + */ +static inline void ipath_rc_rcv_resp(struct ipath_ibdev *dev, + struct ipath_other_headers *ohdr, + void *data, u32 tlen, + struct ipath_qp *qp, + u32 opcode, + u32 psn, u32 hdrsize, u32 pmtu, + int header_in_data) +{ + unsigned long flags; + struct ib_wc wc; + int diff; + u32 pad; + u32 aeth; + + spin_lock_irqsave(&qp->s_lock, flags); + + /* Ignore invalid responses. */ + if (ipath_cmp24(psn, qp->s_next_psn) >= 0) + goto ack_done; + + /* Ignore duplicate responses. */ + diff = ipath_cmp24(psn, qp->s_last_psn); + if (unlikely(diff <= 0)) { + /* Update credits for "ghost" ACKs */ + if (diff == 0 && opcode == OP(ACKNOWLEDGE)) { + if (!header_in_data) + aeth = be32_to_cpu(ohdr->u.aeth); + else { + aeth = be32_to_cpu(((__be32 *) data)[0]); + data += sizeof(__be32); + } + if ((aeth >> 29) == 0) + ipath_get_credit(qp, aeth); + } + goto ack_done; + } + + switch (opcode) { + case OP(ACKNOWLEDGE): + case OP(ATOMIC_ACKNOWLEDGE): + case OP(RDMA_READ_RESPONSE_FIRST): + if (!header_in_data) + aeth = be32_to_cpu(ohdr->u.aeth); + else { + aeth = be32_to_cpu(((__be32 *) data)[0]); + data += sizeof(__be32); + } + if (opcode == OP(ATOMIC_ACKNOWLEDGE)) + *(u64 *) qp->s_sge.sge.vaddr = *(u64 *) data; + if (!do_rc_ack(qp, aeth, psn, opcode) || + opcode != OP(RDMA_READ_RESPONSE_FIRST)) + goto ack_done; + hdrsize += 4; + /* + * do_rc_ack() has already checked the PSN so skip + * the sequence check. + */ + goto rdma_read; + + case OP(RDMA_READ_RESPONSE_MIDDLE): + /* no AETH, no ACK */ + if (unlikely(ipath_cmp24(psn, qp->s_last_psn + 1))) { + dev->n_rdma_seq++; + ipath_restart_rc(qp, qp->s_last_psn + 1, &wc); + goto ack_done; + } + rdma_read: + if (unlikely(qp->s_state != OP(RDMA_READ_REQUEST))) + goto ack_done; + if (unlikely(tlen != (hdrsize + pmtu + 4))) + goto ack_done; + if (unlikely(pmtu >= qp->s_len)) + goto ack_done; + /* We got a response so update the timeout. */ + if (unlikely(qp->s_last == qp->s_tail || + get_swqe_ptr(qp, qp->s_last)->wr.opcode != + IB_WR_RDMA_READ)) + goto ack_done; + spin_lock(&dev->pending_lock); + if (qp->s_rnr_timeout == 0 && + qp->timerwait.next != LIST_POISON1) + list_move_tail(&qp->timerwait, + &dev->pending[dev->pending_index]); + spin_unlock(&dev->pending_lock); + /* + * Update the RDMA receive state but do the copy w/o holding the + * locks and blocking interrupts. XXX Yet another place that + * affects relaxed RDMA order since we don't want s_sge modified. + */ + qp->s_len -= pmtu; + qp->s_last_psn = psn; + spin_unlock_irqrestore(&qp->s_lock, flags); + ipath_copy_sge(&qp->s_sge, data, pmtu); + goto bail; + + case OP(RDMA_READ_RESPONSE_LAST): + /* ACKs READ req. */ + if (unlikely(ipath_cmp24(psn, qp->s_last_psn + 1))) { + dev->n_rdma_seq++; + ipath_restart_rc(qp, qp->s_last_psn + 1, &wc); + goto ack_done; + } + /* FALLTHROUGH */ + case OP(RDMA_READ_RESPONSE_ONLY): + if (unlikely(qp->s_state != OP(RDMA_READ_REQUEST))) + goto ack_done; + /* + * Get the number of bytes the message was padded by. + */ + pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3; + /* + * Check that the data size is >= 1 && <= pmtu. + * Remember to account for the AETH header (4) and + * ICRC (4). + */ + if (unlikely(tlen <= (hdrsize + pad + 8))) { + /* + * XXX Need to generate an error CQ + * entry. + */ + goto ack_done; + } + tlen -= hdrsize + pad + 8; + if (unlikely(tlen != qp->s_len)) { + /* + * XXX Need to generate an error CQ + * entry. + */ + goto ack_done; + } + if (!header_in_data) + aeth = be32_to_cpu(ohdr->u.aeth); + else { + aeth = be32_to_cpu(((__be32 *) data)[0]); + data += sizeof(__be32); + } + ipath_copy_sge(&qp->s_sge, data, tlen); + if (do_rc_ack(qp, aeth, psn, OP(RDMA_READ_RESPONSE_LAST))) { + /* + * Change the state so we contimue + * processing new requests. + */ + qp->s_state = OP(SEND_LAST); + } + goto ack_done; + } + +ack_done: + spin_unlock_irqrestore(&qp->s_lock, flags); +bail: + return; +} + +/** + * ipath_rc_rcv_error - process an incoming duplicate or error RC packet + * @dev: the device this packet came in on + * @ohdr: the other headers for this packet + * @data: the packet data + * @qp: the QP for this packet + * @opcode: the opcode for this packet + * @psn: the packet sequence number for this packet + * @diff: the difference between the PSN and the expected PSN + * @header_in_data: true if part of the header data is in the data buffer + * + * This is called from ipath_rc_rcv() to process an unexpected + * incoming RC packet for the given QP. + * Called at interrupt level. + * Return 1 if no more processing is needed; otherwise return 0 to + * schedule a response to be sent and the s_lock unlocked. + */ +static inline int ipath_rc_rcv_error(struct ipath_ibdev *dev, + struct ipath_other_headers *ohdr, + void *data, + struct ipath_qp *qp, + u32 opcode, + u32 psn, + int diff, + int header_in_data) +{ + struct ib_reth *reth; + + if (diff > 0) { + /* + * Packet sequence error. + * A NAK will ACK earlier sends and RDMA writes. + * Don't queue the NAK if a RDMA read, atomic, or + * NAK is pending though. + */ + spin_lock(&qp->s_lock); + if ((qp->s_ack_state >= OP(RDMA_READ_REQUEST) && + qp->s_ack_state != IB_OPCODE_ACKNOWLEDGE) || + qp->s_nak_state != 0) { + spin_unlock(&qp->s_lock); + goto done; + } + qp->s_ack_state = OP(SEND_ONLY); + qp->s_nak_state = IB_NAK_PSN_ERROR; + /* Use the expected PSN. */ + qp->s_ack_psn = qp->r_psn; + goto resched; + } + + /* + * Handle a duplicate request. Don't re-execute SEND, RDMA + * write or atomic op. Don't NAK errors, just silently drop + * the duplicate request. Note that r_sge, r_len, and + * r_rcv_len may be in use so don't modify them. + * + * We are supposed to ACK the earliest duplicate PSN but we + * can coalesce an outstanding duplicate ACK. We have to + * send the earliest so that RDMA reads can be restarted at + * the requester's expected PSN. + */ + spin_lock(&qp->s_lock); + if (qp->s_ack_state != IB_OPCODE_ACKNOWLEDGE && + ipath_cmp24(psn, qp->s_ack_psn) >= 0) { + if (qp->s_ack_state < IB_OPCODE_RDMA_READ_REQUEST) + qp->s_ack_psn = psn; + spin_unlock(&qp->s_lock); + goto done; + } + switch (opcode) { + case OP(RDMA_READ_REQUEST): + /* + * We have to be careful to not change s_rdma_sge + * while ipath_do_rc_send() is using it and not + * holding the s_lock. + */ + if (qp->s_ack_state != OP(ACKNOWLEDGE) && + qp->s_ack_state >= IB_OPCODE_RDMA_READ_REQUEST) { + spin_unlock(&qp->s_lock); + dev->n_rdma_dup_busy++; + goto done; + } + /* RETH comes after BTH */ + if (!header_in_data) + reth = &ohdr->u.rc.reth; + else { + reth = (struct ib_reth *)data; + data += sizeof(*reth); + } + qp->s_rdma_len = be32_to_cpu(reth->length); + if (qp->s_rdma_len != 0) { + u32 rkey = be32_to_cpu(reth->rkey); + u64 vaddr = be64_to_cpu(reth->vaddr); + int ok; + + /* + * Address range must be a subset of the original + * request and start on pmtu boundaries. + */ + ok = ipath_rkey_ok(dev, &qp->s_rdma_sge, + qp->s_rdma_len, vaddr, rkey, + IB_ACCESS_REMOTE_READ); + if (unlikely(!ok)) + goto done; + } else { + qp->s_rdma_sge.sg_list = NULL; + qp->s_rdma_sge.num_sge = 0; + qp->s_rdma_sge.sge.mr = NULL; + qp->s_rdma_sge.sge.vaddr = NULL; + qp->s_rdma_sge.sge.length = 0; + qp->s_rdma_sge.sge.sge_length = 0; + } + break; + + case OP(COMPARE_SWAP): + case OP(FETCH_ADD): + /* + * Check for the PSN of the last atomic operations + * performed and resend the result if found. + */ + if ((psn & IPS_PSN_MASK) != qp->r_atomic_psn) { + spin_unlock(&qp->s_lock); + goto done; + } + qp->s_ack_atomic = qp->r_atomic_data; + break; + } + qp->s_ack_state = opcode; + qp->s_nak_state = 0; + qp->s_ack_psn = psn; +resched: + return 0; + +done: + return 1; +} + +/** + * ipath_rc_rcv - process an incoming RC packet + * @dev: the device this packet came in on + * @hdr: the header of this packet + * @has_grh: true if the header has a GRH + * @data: the packet data + * @tlen: the packet length + * @qp: the QP for this packet + * + * This is called from ipath_qp_rcv() to process an incoming RC packet + * for the given QP. + * Called at interrupt level. + */ +void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, + int has_grh, void *data, u32 tlen, struct ipath_qp *qp) +{ + struct ipath_other_headers *ohdr; + u32 opcode; + u32 hdrsize; + u32 psn; + u32 pad; + unsigned long flags; + struct ib_wc wc; + u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu); + int diff; + struct ib_reth *reth; + int header_in_data; + + /* Check for GRH */ + if (!has_grh) { + ohdr = &hdr->u.oth; + hdrsize = 8 + 12; /* LRH + BTH */ + psn = be32_to_cpu(ohdr->bth[2]); + header_in_data = 0; + } else { + ohdr = &hdr->u.l.oth; + hdrsize = 8 + 40 + 12; /* LRH + GRH + BTH */ + /* + * The header with GRH is 60 bytes and the core driver sets + * the eager header buffer size to 56 bytes so the last 4 + * bytes of the BTH header (PSN) is in the data buffer. + */ + header_in_data = + ipath_layer_get_rcvhdrentsize(dev->dd) == 16; + if (header_in_data) { + psn = be32_to_cpu(((__be32 *) data)[0]); + data += sizeof(__be32); + } else + psn = be32_to_cpu(ohdr->bth[2]); + } + /* + * The opcode is in the low byte when its in network order + * (top byte when in host order). + */ + opcode = be32_to_cpu(ohdr->bth[0]) >> 24; + + /* + * Process responses (ACKs) before anything else. Note that the + * packet sequence number will be for something in the send work + * queue rather than the expected receive packet sequence number. + * In other words, this QP is the requester. + */ + if (opcode >= OP(RDMA_READ_RESPONSE_FIRST) && + opcode <= OP(ATOMIC_ACKNOWLEDGE)) { + ipath_rc_rcv_resp(dev, ohdr, data, tlen, qp, opcode, psn, + hdrsize, pmtu, header_in_data); + goto bail; + } + + spin_lock_irqsave(&qp->r_rq.lock, flags); + + /* Compute 24 bits worth of difference. */ + diff = ipath_cmp24(psn, qp->r_psn); + if (unlikely(diff)) { + if (ipath_rc_rcv_error(dev, ohdr, data, qp, opcode, + psn, diff, header_in_data)) + goto done; + goto resched; + } + + /* Check for opcode sequence errors. */ + switch (qp->r_state) { + case OP(SEND_FIRST): + case OP(SEND_MIDDLE): + if (opcode == OP(SEND_MIDDLE) || + opcode == OP(SEND_LAST) || + opcode == OP(SEND_LAST_WITH_IMMEDIATE)) + break; + nack_inv: + /* + * A NAK will ACK earlier sends and RDMA writes. Don't queue the + * NAK if a RDMA read, atomic, or NAK is pending though. + */ + spin_lock(&qp->s_lock); + if (qp->s_ack_state >= OP(RDMA_READ_REQUEST) && + qp->s_ack_state != IB_OPCODE_ACKNOWLEDGE) { + spin_unlock(&qp->s_lock); + goto done; + } + /* XXX Flush WQEs */ + qp->state = IB_QPS_ERR; + qp->s_ack_state = OP(SEND_ONLY); + qp->s_nak_state = IB_NAK_INVALID_REQUEST; + qp->s_ack_psn = qp->r_psn; + goto resched; + + case OP(RDMA_WRITE_FIRST): + case OP(RDMA_WRITE_MIDDLE): + if (opcode == OP(RDMA_WRITE_MIDDLE) || + opcode == OP(RDMA_WRITE_LAST) || + opcode == OP(RDMA_WRITE_LAST_WITH_IMMEDIATE)) + break; + goto nack_inv; + + case OP(RDMA_READ_REQUEST): + case OP(COMPARE_SWAP): + case OP(FETCH_ADD): + /* + * Drop all new requests until a response has been sent. A + * new request then ACKs the RDMA response we sent. Relaxed + * ordering would allow new requests to be processed but we + * would need to keep a queue of rwqe's for all that are in + * progress. Note that we can't RNR NAK this request since + * the RDMA READ or atomic response is already queued to be + * sent (unless we implement a response send queue). + */ + goto done; + + default: + if (opcode == OP(SEND_MIDDLE) || + opcode == OP(SEND_LAST) || + opcode == OP(SEND_LAST_WITH_IMMEDIATE) || + opcode == OP(RDMA_WRITE_MIDDLE) || + opcode == OP(RDMA_WRITE_LAST) || + opcode == OP(RDMA_WRITE_LAST_WITH_IMMEDIATE)) + goto nack_inv; + break; + } + + wc.imm_data = 0; + wc.wc_flags = 0; + + /* OK, process the packet. */ + switch (opcode) { + case OP(SEND_FIRST): + if (!ipath_get_rwqe(qp, 0)) { + rnr_nak: + /* + * A RNR NAK will ACK earlier sends and RDMA writes. + * Don't queue the NAK if a RDMA read or atomic + * is pending though. + */ + spin_lock(&qp->s_lock); + if (qp->s_ack_state >= + OP(RDMA_READ_REQUEST) && + qp->s_ack_state != IB_OPCODE_ACKNOWLEDGE) { + spin_unlock(&qp->s_lock); + goto done; + } + qp->s_ack_state = OP(SEND_ONLY); + qp->s_nak_state = IB_RNR_NAK | qp->s_min_rnr_timer; + qp->s_ack_psn = qp->r_psn; + goto resched; + } + qp->r_rcv_len = 0; + /* FALLTHROUGH */ + case OP(SEND_MIDDLE): + case OP(RDMA_WRITE_MIDDLE): + send_middle: + /* Check for invalid length PMTU or posted rwqe len. */ + if (unlikely(tlen != (hdrsize + pmtu + 4))) + goto nack_inv; + qp->r_rcv_len += pmtu; + if (unlikely(qp->r_rcv_len > qp->r_len)) + goto nack_inv; + ipath_copy_sge(&qp->r_sge, data, pmtu); + break; + + case OP(RDMA_WRITE_LAST_WITH_IMMEDIATE): + /* consume RWQE */ + if (!ipath_get_rwqe(qp, 1)) + goto rnr_nak; + goto send_last_imm; + + case OP(SEND_ONLY): + case OP(SEND_ONLY_WITH_IMMEDIATE): + if (!ipath_get_rwqe(qp, 0)) + goto rnr_nak; + qp->r_rcv_len = 0; + if (opcode == OP(SEND_ONLY)) + goto send_last; + /* FALLTHROUGH */ + case OP(SEND_LAST_WITH_IMMEDIATE): + send_last_imm: + if (header_in_data) { + wc.imm_data = *(__be32 *) data; + data += sizeof(__be32); + } else { + /* Immediate data comes after BTH */ + wc.imm_data = ohdr->u.imm_data; + } + hdrsize += 4; + wc.wc_flags = IB_WC_WITH_IMM; + /* FALLTHROUGH */ + case OP(SEND_LAST): + case OP(RDMA_WRITE_LAST): + send_last: + /* Get the number of bytes the message was padded by. */ + pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3; + /* Check for invalid length. */ + /* XXX LAST len should be >= 1 */ + if (unlikely(tlen < (hdrsize + pad + 4))) + goto nack_inv; + /* Don't count the CRC. */ + tlen -= (hdrsize + pad + 4); + wc.byte_len = tlen + qp->r_rcv_len; + if (unlikely(wc.byte_len > qp->r_len)) + goto nack_inv; + ipath_copy_sge(&qp->r_sge, data, tlen); + atomic_inc(&qp->msn); + if (opcode == OP(RDMA_WRITE_LAST) || + opcode == OP(RDMA_WRITE_ONLY)) + break; + wc.wr_id = qp->r_wr_id; + wc.status = IB_WC_SUCCESS; + wc.opcode = IB_WC_RECV; + wc.vendor_err = 0; + wc.qp_num = qp->ibqp.qp_num; + wc.src_qp = qp->remote_qpn; + wc.pkey_index = 0; + wc.slid = qp->remote_ah_attr.dlid; + wc.sl = qp->remote_ah_attr.sl; + wc.dlid_path_bits = 0; + wc.port_num = 0; + /* Signal completion event if the solicited bit is set. */ + ipath_cq_enter(to_icq(qp->ibqp.recv_cq), &wc, + (ohdr->bth[0] & + __constant_cpu_to_be32(1 << 23)) != 0); + break; + + case OP(RDMA_WRITE_FIRST): + case OP(RDMA_WRITE_ONLY): + case OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE): + /* consume RWQE */ + /* RETH comes after BTH */ + if (!header_in_data) + reth = &ohdr->u.rc.reth; + else { + reth = (struct ib_reth *)data; + data += sizeof(*reth); + } + hdrsize += sizeof(*reth); + qp->r_len = be32_to_cpu(reth->length); + qp->r_rcv_len = 0; + if (qp->r_len != 0) { + u32 rkey = be32_to_cpu(reth->rkey); + u64 vaddr = be64_to_cpu(reth->vaddr); + int ok; + + /* Check rkey & NAK */ + ok = ipath_rkey_ok(dev, &qp->r_sge, + qp->r_len, vaddr, rkey, + IB_ACCESS_REMOTE_WRITE); + if (unlikely(!ok)) { + nack_acc: + /* + * A NAK will ACK earlier sends and RDMA + * writes. Don't queue the NAK if a RDMA + * read, atomic, or NAK is pending though. + */ + spin_lock(&qp->s_lock); + if (qp->s_ack_state >= + OP(RDMA_READ_REQUEST) && + qp->s_ack_state != + IB_OPCODE_ACKNOWLEDGE) { + spin_unlock(&qp->s_lock); + goto done; + } + /* XXX Flush WQEs */ + qp->state = IB_QPS_ERR; + qp->s_ack_state = OP(RDMA_WRITE_ONLY); + qp->s_nak_state = + IB_NAK_REMOTE_ACCESS_ERROR; + qp->s_ack_psn = qp->r_psn; + goto resched; + } + } else { + qp->r_sge.sg_list = NULL; + qp->r_sge.sge.mr = NULL; + qp->r_sge.sge.vaddr = NULL; + qp->r_sge.sge.length = 0; + qp->r_sge.sge.sge_length = 0; + } + if (unlikely(!(qp->qp_access_flags & + IB_ACCESS_REMOTE_WRITE))) + goto nack_acc; + if (opcode == OP(RDMA_WRITE_FIRST)) + goto send_middle; + else if (opcode == OP(RDMA_WRITE_ONLY)) + goto send_last; + if (!ipath_get_rwqe(qp, 1)) + goto rnr_nak; + goto send_last_imm; + + case OP(RDMA_READ_REQUEST): + /* RETH comes after BTH */ + if (!header_in_data) + reth = &ohdr->u.rc.reth; + else { + reth = (struct ib_reth *)data; + data += sizeof(*reth); + } + spin_lock(&qp->s_lock); + if (qp->s_ack_state != OP(ACKNOWLEDGE) && + qp->s_ack_state >= IB_OPCODE_RDMA_READ_REQUEST) { + spin_unlock(&qp->s_lock); + goto done; + } + qp->s_rdma_len = be32_to_cpu(reth->length); + if (qp->s_rdma_len != 0) { + u32 rkey = be32_to_cpu(reth->rkey); + u64 vaddr = be64_to_cpu(reth->vaddr); + int ok; + + /* Check rkey & NAK */ + ok = ipath_rkey_ok(dev, &qp->s_rdma_sge, + qp->s_rdma_len, vaddr, rkey, + IB_ACCESS_REMOTE_READ); + if (unlikely(!ok)) { + spin_unlock(&qp->s_lock); + goto nack_acc; + } + /* + * Update the next expected PSN. We add 1 later + * below, so only add the remainder here. + */ + if (qp->s_rdma_len > pmtu) + qp->r_psn += (qp->s_rdma_len - 1) / pmtu; + } else { + qp->s_rdma_sge.sg_list = NULL; + qp->s_rdma_sge.num_sge = 0; + qp->s_rdma_sge.sge.mr = NULL; + qp->s_rdma_sge.sge.vaddr = NULL; + qp->s_rdma_sge.sge.length = 0; + qp->s_rdma_sge.sge.sge_length = 0; + } + if (unlikely(!(qp->qp_access_flags & + IB_ACCESS_REMOTE_READ))) + goto nack_acc; + /* + * We need to increment the MSN here instead of when we + * finish sending the result since a duplicate request would + * increment it more than once. + */ + atomic_inc(&qp->msn); + qp->s_ack_state = opcode; + qp->s_nak_state = 0; + qp->s_ack_psn = psn; + qp->r_psn++; + qp->r_state = opcode; + goto rdmadone; + + case OP(COMPARE_SWAP): + case OP(FETCH_ADD): { + struct ib_atomic_eth *ateth; + u64 vaddr; + u64 sdata; + u32 rkey; + + if (!header_in_data) + ateth = &ohdr->u.atomic_eth; + else { + ateth = (struct ib_atomic_eth *)data; + data += sizeof(*ateth); + } + vaddr = be64_to_cpu(ateth->vaddr); + if (unlikely(vaddr & (sizeof(u64) - 1))) + goto nack_inv; + rkey = be32_to_cpu(ateth->rkey); + /* Check rkey & NAK */ + if (unlikely(!ipath_rkey_ok(dev, &qp->r_sge, + sizeof(u64), vaddr, rkey, + IB_ACCESS_REMOTE_ATOMIC))) + goto nack_acc; + if (unlikely(!(qp->qp_access_flags & + IB_ACCESS_REMOTE_ATOMIC))) + goto nack_acc; + /* Perform atomic OP and save result. */ + sdata = be64_to_cpu(ateth->swap_data); + spin_lock(&dev->pending_lock); + qp->r_atomic_data = *(u64 *) qp->r_sge.sge.vaddr; + if (opcode == OP(FETCH_ADD)) + *(u64 *) qp->r_sge.sge.vaddr = + qp->r_atomic_data + sdata; + else if (qp->r_atomic_data == + be64_to_cpu(ateth->compare_data)) + *(u64 *) qp->r_sge.sge.vaddr = sdata; + spin_unlock(&dev->pending_lock); + atomic_inc(&qp->msn); + qp->r_atomic_psn = psn & IPS_PSN_MASK; + psn |= 1 << 31; + break; + } + + default: + /* Drop packet for unknown opcodes. */ + goto done; + } + qp->r_psn++; + qp->r_state = opcode; + /* Send an ACK if requested or required. */ + if (psn & (1 << 31)) { + /* + * Coalesce ACKs unless there is a RDMA READ or + * ATOMIC pending. + */ + spin_lock(&qp->s_lock); + if (qp->s_ack_state == OP(ACKNOWLEDGE) || + qp->s_ack_state < IB_OPCODE_RDMA_READ_REQUEST) { + qp->s_ack_state = opcode; + qp->s_nak_state = 0; + qp->s_ack_psn = psn; + qp->s_ack_atomic = qp->r_atomic_data; + goto resched; + } + spin_unlock(&qp->s_lock); + } +done: + spin_unlock_irqrestore(&qp->r_rq.lock, flags); + goto bail; + +resched: + /* + * Try to send ACK right away but not if ipath_do_rc_send() is + * active. + */ + if (qp->s_hdrwords == 0 && + (qp->s_ack_state < IB_OPCODE_RDMA_READ_REQUEST || + qp->s_ack_state >= IB_OPCODE_COMPARE_SWAP)) + send_rc_ack(qp); + +rdmadone: + spin_unlock(&qp->s_lock); + spin_unlock_irqrestore(&qp->r_rq.lock, flags); + + /* Call ipath_do_rc_send() in another thread. */ + tasklet_hi_schedule(&qp->s_task); + +bail: + return; +} diff --git a/drivers/infiniband/hw/ipath/ipath_registers.h b/drivers/infiniband/hw/ipath/ipath_registers.h new file mode 100644 index 000000000000..1e59750c5f63 --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_registers.h @@ -0,0 +1,446 @@ +/* + * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _IPATH_REGISTERS_H +#define _IPATH_REGISTERS_H + +/* + * This file should only be included by kernel source, and by the diags. + * It defines the registers, and their contents, for the InfiniPath HT-400 chip + */ + +/* + * These are the InfiniPath register and buffer bit definitions, + * that are visible to software, and needed only by the kernel + * and diag code. A few, that are visible to protocol and user + * code are in ipath_common.h. Some bits are specific + * to a given chip implementation, and have been moved to the + * chip-specific source file + */ + +/* kr_revision bits */ +#define INFINIPATH_R_CHIPREVMINOR_MASK 0xFF +#define INFINIPATH_R_CHIPREVMINOR_SHIFT 0 +#define INFINIPATH_R_CHIPREVMAJOR_MASK 0xFF +#define INFINIPATH_R_CHIPREVMAJOR_SHIFT 8 +#define INFINIPATH_R_ARCH_MASK 0xFF +#define INFINIPATH_R_ARCH_SHIFT 16 +#define INFINIPATH_R_SOFTWARE_MASK 0xFF +#define INFINIPATH_R_SOFTWARE_SHIFT 24 +#define INFINIPATH_R_BOARDID_MASK 0xFF +#define INFINIPATH_R_BOARDID_SHIFT 32 + +/* kr_control bits */ +#define INFINIPATH_C_FREEZEMODE 0x00000002 +#define INFINIPATH_C_LINKENABLE 0x00000004 +#define INFINIPATH_C_RESET 0x00000001 + +/* kr_sendctrl bits */ +#define INFINIPATH_S_DISARMPIOBUF_SHIFT 16 + +#define IPATH_S_ABORT 0 +#define IPATH_S_PIOINTBUFAVAIL 1 +#define IPATH_S_PIOBUFAVAILUPD 2 +#define IPATH_S_PIOENABLE 3 +#define IPATH_S_DISARM 31 + +#define INFINIPATH_S_ABORT (1U << IPATH_S_ABORT) +#define INFINIPATH_S_PIOINTBUFAVAIL (1U << IPATH_S_PIOINTBUFAVAIL) +#define INFINIPATH_S_PIOBUFAVAILUPD (1U << IPATH_S_PIOBUFAVAILUPD) +#define INFINIPATH_S_PIOENABLE (1U << IPATH_S_PIOENABLE) +#define INFINIPATH_S_DISARM (1U << IPATH_S_DISARM) + +/* kr_rcvctrl bits */ +#define INFINIPATH_R_PORTENABLE_SHIFT 0 +#define INFINIPATH_R_INTRAVAIL_SHIFT 16 +#define INFINIPATH_R_TAILUPD 0x80000000 + +/* kr_intstatus, kr_intclear, kr_intmask bits */ +#define INFINIPATH_I_RCVURG_SHIFT 0 +#define INFINIPATH_I_RCVAVAIL_SHIFT 12 +#define INFINIPATH_I_ERROR 0x80000000 +#define INFINIPATH_I_SPIOSENT 0x40000000 +#define INFINIPATH_I_SPIOBUFAVAIL 0x20000000 +#define INFINIPATH_I_GPIO 0x10000000 + +/* kr_errorstatus, kr_errorclear, kr_errormask bits */ +#define INFINIPATH_E_RFORMATERR 0x0000000000000001ULL +#define INFINIPATH_E_RVCRC 0x0000000000000002ULL +#define INFINIPATH_E_RICRC 0x0000000000000004ULL +#define INFINIPATH_E_RMINPKTLEN 0x0000000000000008ULL +#define INFINIPATH_E_RMAXPKTLEN 0x0000000000000010ULL +#define INFINIPATH_E_RLONGPKTLEN 0x0000000000000020ULL +#define INFINIPATH_E_RSHORTPKTLEN 0x0000000000000040ULL +#define INFINIPATH_E_RUNEXPCHAR 0x0000000000000080ULL +#define INFINIPATH_E_RUNSUPVL 0x0000000000000100ULL +#define INFINIPATH_E_REBP 0x0000000000000200ULL +#define INFINIPATH_E_RIBFLOW 0x0000000000000400ULL +#define INFINIPATH_E_RBADVERSION 0x0000000000000800ULL +#define INFINIPATH_E_RRCVEGRFULL 0x0000000000001000ULL +#define INFINIPATH_E_RRCVHDRFULL 0x0000000000002000ULL +#define INFINIPATH_E_RBADTID 0x0000000000004000ULL +#define INFINIPATH_E_RHDRLEN 0x0000000000008000ULL +#define INFINIPATH_E_RHDR 0x0000000000010000ULL +#define INFINIPATH_E_RIBLOSTLINK 0x0000000000020000ULL +#define INFINIPATH_E_SMINPKTLEN 0x0000000020000000ULL +#define INFINIPATH_E_SMAXPKTLEN 0x0000000040000000ULL +#define INFINIPATH_E_SUNDERRUN 0x0000000080000000ULL +#define INFINIPATH_E_SPKTLEN 0x0000000100000000ULL +#define INFINIPATH_E_SDROPPEDSMPPKT 0x0000000200000000ULL +#define INFINIPATH_E_SDROPPEDDATAPKT 0x0000000400000000ULL +#define INFINIPATH_E_SPIOARMLAUNCH 0x0000000800000000ULL +#define INFINIPATH_E_SUNEXPERRPKTNUM 0x0000001000000000ULL +#define INFINIPATH_E_SUNSUPVL 0x0000002000000000ULL +#define INFINIPATH_E_IBSTATUSCHANGED 0x0001000000000000ULL +#define INFINIPATH_E_INVALIDADDR 0x0002000000000000ULL +#define INFINIPATH_E_RESET 0x0004000000000000ULL +#define INFINIPATH_E_HARDWARE 0x0008000000000000ULL + +/* kr_hwerrclear, kr_hwerrmask, kr_hwerrstatus, bits */ +/* TXEMEMPARITYERR bit 0: PIObuf, 1: PIOpbc, 2: launchfifo + * RXEMEMPARITYERR bit 0: rcvbuf, 1: lookupq, 2: eagerTID, 3: expTID + * bit 4: flag buffer, 5: datainfo, 6: header info */ +#define INFINIPATH_HWE_TXEMEMPARITYERR_MASK 0xFULL +#define INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT 40 +#define INFINIPATH_HWE_RXEMEMPARITYERR_MASK 0x7FULL +#define INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT 44 +#define INFINIPATH_HWE_RXDSYNCMEMPARITYERR 0x0000000400000000ULL +#define INFINIPATH_HWE_MEMBISTFAILED 0x0040000000000000ULL +#define INFINIPATH_HWE_IBCBUSTOSPCPARITYERR 0x4000000000000000ULL +#define INFINIPATH_HWE_IBCBUSFRSPCPARITYERR 0x8000000000000000ULL + +/* kr_hwdiagctrl bits */ +#define INFINIPATH_DC_FORCETXEMEMPARITYERR_MASK 0xFULL +#define INFINIPATH_DC_FORCETXEMEMPARITYERR_SHIFT 40 +#define INFINIPATH_DC_FORCERXEMEMPARITYERR_MASK 0x7FULL +#define INFINIPATH_DC_FORCERXEMEMPARITYERR_SHIFT 44 +#define INFINIPATH_DC_FORCERXDSYNCMEMPARITYERR 0x0000000400000000ULL +#define INFINIPATH_DC_COUNTERDISABLE 0x1000000000000000ULL +#define INFINIPATH_DC_COUNTERWREN 0x2000000000000000ULL +#define INFINIPATH_DC_FORCEIBCBUSTOSPCPARITYERR 0x4000000000000000ULL +#define INFINIPATH_DC_FORCEIBCBUSFRSPCPARITYERR 0x8000000000000000ULL + +/* kr_ibcctrl bits */ +#define INFINIPATH_IBCC_FLOWCTRLPERIOD_MASK 0xFFULL +#define INFINIPATH_IBCC_FLOWCTRLPERIOD_SHIFT 0 +#define INFINIPATH_IBCC_FLOWCTRLWATERMARK_MASK 0xFFULL +#define INFINIPATH_IBCC_FLOWCTRLWATERMARK_SHIFT 8 +#define INFINIPATH_IBCC_LINKINITCMD_MASK 0x3ULL +#define INFINIPATH_IBCC_LINKINITCMD_DISABLE 1 +#define INFINIPATH_IBCC_LINKINITCMD_POLL 2 /* cycle through TS1/TS2 till OK */ +#define INFINIPATH_IBCC_LINKINITCMD_SLEEP 3 /* wait for TS1, then go on */ +#define INFINIPATH_IBCC_LINKINITCMD_SHIFT 16 +#define INFINIPATH_IBCC_LINKCMD_MASK 0x3ULL +#define INFINIPATH_IBCC_LINKCMD_INIT 1 /* move to 0x11 */ +#define INFINIPATH_IBCC_LINKCMD_ARMED 2 /* move to 0x21 */ +#define INFINIPATH_IBCC_LINKCMD_ACTIVE 3 /* move to 0x31 */ +#define INFINIPATH_IBCC_LINKCMD_SHIFT 18 +#define INFINIPATH_IBCC_MAXPKTLEN_MASK 0x7FFULL +#define INFINIPATH_IBCC_MAXPKTLEN_SHIFT 20 +#define INFINIPATH_IBCC_PHYERRTHRESHOLD_MASK 0xFULL +#define INFINIPATH_IBCC_PHYERRTHRESHOLD_SHIFT 32 +#define INFINIPATH_IBCC_OVERRUNTHRESHOLD_MASK 0xFULL +#define INFINIPATH_IBCC_OVERRUNTHRESHOLD_SHIFT 36 +#define INFINIPATH_IBCC_CREDITSCALE_MASK 0x7ULL +#define INFINIPATH_IBCC_CREDITSCALE_SHIFT 40 +#define INFINIPATH_IBCC_LOOPBACK 0x8000000000000000ULL +#define INFINIPATH_IBCC_LINKDOWNDEFAULTSTATE 0x4000000000000000ULL + +/* kr_ibcstatus bits */ +#define INFINIPATH_IBCS_LINKTRAININGSTATE_MASK 0xF +#define INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT 0 +#define INFINIPATH_IBCS_LINKSTATE_MASK 0x7 +#define INFINIPATH_IBCS_LINKSTATE_SHIFT 4 +#define INFINIPATH_IBCS_TXREADY 0x40000000 +#define INFINIPATH_IBCS_TXCREDITOK 0x80000000 +/* link training states (shift by INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT) */ +#define INFINIPATH_IBCS_LT_STATE_DISABLED 0x00 +#define INFINIPATH_IBCS_LT_STATE_LINKUP 0x01 +#define INFINIPATH_IBCS_LT_STATE_POLLACTIVE 0x02 +#define INFINIPATH_IBCS_LT_STATE_POLLQUIET 0x03 +#define INFINIPATH_IBCS_LT_STATE_SLEEPDELAY 0x04 +#define INFINIPATH_IBCS_LT_STATE_SLEEPQUIET 0x05 +#define INFINIPATH_IBCS_LT_STATE_CFGDEBOUNCE 0x08 +#define INFINIPATH_IBCS_LT_STATE_CFGRCVFCFG 0x09 +#define INFINIPATH_IBCS_LT_STATE_CFGWAITRMT 0x0a +#define INFINIPATH_IBCS_LT_STATE_CFGIDLE 0x0b +#define INFINIPATH_IBCS_LT_STATE_RECOVERRETRAIN 0x0c +#define INFINIPATH_IBCS_LT_STATE_RECOVERWAITRMT 0x0e +#define INFINIPATH_IBCS_LT_STATE_RECOVERIDLE 0x0f +/* link state machine states (shift by INFINIPATH_IBCS_LINKSTATE_SHIFT) */ +#define INFINIPATH_IBCS_L_STATE_DOWN 0x0 +#define INFINIPATH_IBCS_L_STATE_INIT 0x1 +#define INFINIPATH_IBCS_L_STATE_ARM 0x2 +#define INFINIPATH_IBCS_L_STATE_ACTIVE 0x3 +#define INFINIPATH_IBCS_L_STATE_ACT_DEFER 0x4 + +/* combination link status states that we use with some frequency */ +#define IPATH_IBSTATE_MASK ((INFINIPATH_IBCS_LINKTRAININGSTATE_MASK \ + << INFINIPATH_IBCS_LINKSTATE_SHIFT) | \ + (INFINIPATH_IBCS_LINKSTATE_MASK \ + <ibqp.device); + unsigned long flags; + + spin_lock_irqsave(&dev->pending_lock, flags); + if (list_empty(&dev->rnrwait)) + list_add(&qp->timerwait, &dev->rnrwait); + else { + struct list_head *l = &dev->rnrwait; + struct ipath_qp *nqp = list_entry(l->next, struct ipath_qp, + timerwait); + + while (qp->s_rnr_timeout >= nqp->s_rnr_timeout) { + qp->s_rnr_timeout -= nqp->s_rnr_timeout; + l = l->next; + if (l->next == &dev->rnrwait) + break; + nqp = list_entry(l->next, struct ipath_qp, + timerwait); + } + list_add(&qp->timerwait, l); + } + spin_unlock_irqrestore(&dev->pending_lock, flags); +} + +/** + * ipath_get_rwqe - copy the next RWQE into the QP's RWQE + * @qp: the QP + * @wr_id_only: update wr_id only, not SGEs + * + * Return 0 if no RWQE is available, otherwise return 1. + * + * Called at interrupt level with the QP r_rq.lock held. + */ +int ipath_get_rwqe(struct ipath_qp *qp, int wr_id_only) +{ + struct ipath_rq *rq; + struct ipath_srq *srq; + struct ipath_rwqe *wqe; + int ret; + + if (!qp->ibqp.srq) { + rq = &qp->r_rq; + if (unlikely(rq->tail == rq->head)) { + ret = 0; + goto bail; + } + wqe = get_rwqe_ptr(rq, rq->tail); + qp->r_wr_id = wqe->wr_id; + if (!wr_id_only) { + qp->r_sge.sge = wqe->sg_list[0]; + qp->r_sge.sg_list = wqe->sg_list + 1; + qp->r_sge.num_sge = wqe->num_sge; + qp->r_len = wqe->length; + } + if (++rq->tail >= rq->size) + rq->tail = 0; + ret = 1; + goto bail; + } + + srq = to_isrq(qp->ibqp.srq); + rq = &srq->rq; + spin_lock(&rq->lock); + if (unlikely(rq->tail == rq->head)) { + spin_unlock(&rq->lock); + ret = 0; + goto bail; + } + wqe = get_rwqe_ptr(rq, rq->tail); + qp->r_wr_id = wqe->wr_id; + if (!wr_id_only) { + qp->r_sge.sge = wqe->sg_list[0]; + qp->r_sge.sg_list = wqe->sg_list + 1; + qp->r_sge.num_sge = wqe->num_sge; + qp->r_len = wqe->length; + } + if (++rq->tail >= rq->size) + rq->tail = 0; + if (srq->ibsrq.event_handler) { + struct ib_event ev; + u32 n; + + if (rq->head < rq->tail) + n = rq->size + rq->head - rq->tail; + else + n = rq->head - rq->tail; + if (n < srq->limit) { + srq->limit = 0; + spin_unlock(&rq->lock); + ev.device = qp->ibqp.device; + ev.element.srq = qp->ibqp.srq; + ev.event = IB_EVENT_SRQ_LIMIT_REACHED; + srq->ibsrq.event_handler(&ev, + srq->ibsrq.srq_context); + } else + spin_unlock(&rq->lock); + } else + spin_unlock(&rq->lock); + ret = 1; + +bail: + return ret; +} + +/** + * ipath_ruc_loopback - handle UC and RC lookback requests + * @sqp: the loopback QP + * @wc: the work completion entry + * + * This is called from ipath_do_uc_send() or ipath_do_rc_send() to + * forward a WQE addressed to the same HCA. + * Note that although we are single threaded due to the tasklet, we still + * have to protect against post_send(). We don't have to worry about + * receive interrupts since this is a connected protocol and all packets + * will pass through here. + */ +void ipath_ruc_loopback(struct ipath_qp *sqp, struct ib_wc *wc) +{ + struct ipath_ibdev *dev = to_idev(sqp->ibqp.device); + struct ipath_qp *qp; + struct ipath_swqe *wqe; + struct ipath_sge *sge; + unsigned long flags; + u64 sdata; + + qp = ipath_lookup_qpn(&dev->qp_table, sqp->remote_qpn); + if (!qp) { + dev->n_pkt_drops++; + return; + } + +again: + spin_lock_irqsave(&sqp->s_lock, flags); + + if (!(ib_ipath_state_ops[sqp->state] & IPATH_PROCESS_SEND_OK)) { + spin_unlock_irqrestore(&sqp->s_lock, flags); + goto done; + } + + /* Get the next send request. */ + if (sqp->s_last == sqp->s_head) { + /* Send work queue is empty. */ + spin_unlock_irqrestore(&sqp->s_lock, flags); + goto done; + } + + /* + * We can rely on the entry not changing without the s_lock + * being held until we update s_last. + */ + wqe = get_swqe_ptr(sqp, sqp->s_last); + spin_unlock_irqrestore(&sqp->s_lock, flags); + + wc->wc_flags = 0; + wc->imm_data = 0; + + sqp->s_sge.sge = wqe->sg_list[0]; + sqp->s_sge.sg_list = wqe->sg_list + 1; + sqp->s_sge.num_sge = wqe->wr.num_sge; + sqp->s_len = wqe->length; + switch (wqe->wr.opcode) { + case IB_WR_SEND_WITH_IMM: + wc->wc_flags = IB_WC_WITH_IMM; + wc->imm_data = wqe->wr.imm_data; + /* FALLTHROUGH */ + case IB_WR_SEND: + spin_lock_irqsave(&qp->r_rq.lock, flags); + if (!ipath_get_rwqe(qp, 0)) { + rnr_nak: + spin_unlock_irqrestore(&qp->r_rq.lock, flags); + /* Handle RNR NAK */ + if (qp->ibqp.qp_type == IB_QPT_UC) + goto send_comp; + if (sqp->s_rnr_retry == 0) { + wc->status = IB_WC_RNR_RETRY_EXC_ERR; + goto err; + } + if (sqp->s_rnr_retry_cnt < 7) + sqp->s_rnr_retry--; + dev->n_rnr_naks++; + sqp->s_rnr_timeout = + ib_ipath_rnr_table[sqp->s_min_rnr_timer]; + ipath_insert_rnr_queue(sqp); + goto done; + } + spin_unlock_irqrestore(&qp->r_rq.lock, flags); + break; + + case IB_WR_RDMA_WRITE_WITH_IMM: + wc->wc_flags = IB_WC_WITH_IMM; + wc->imm_data = wqe->wr.imm_data; + spin_lock_irqsave(&qp->r_rq.lock, flags); + if (!ipath_get_rwqe(qp, 1)) + goto rnr_nak; + spin_unlock_irqrestore(&qp->r_rq.lock, flags); + /* FALLTHROUGH */ + case IB_WR_RDMA_WRITE: + if (wqe->length == 0) + break; + if (unlikely(!ipath_rkey_ok(dev, &qp->r_sge, wqe->length, + wqe->wr.wr.rdma.remote_addr, + wqe->wr.wr.rdma.rkey, + IB_ACCESS_REMOTE_WRITE))) { + acc_err: + wc->status = IB_WC_REM_ACCESS_ERR; + err: + wc->wr_id = wqe->wr.wr_id; + wc->opcode = ib_ipath_wc_opcode[wqe->wr.opcode]; + wc->vendor_err = 0; + wc->byte_len = 0; + wc->qp_num = sqp->ibqp.qp_num; + wc->src_qp = sqp->remote_qpn; + wc->pkey_index = 0; + wc->slid = sqp->remote_ah_attr.dlid; + wc->sl = sqp->remote_ah_attr.sl; + wc->dlid_path_bits = 0; + wc->port_num = 0; + ipath_sqerror_qp(sqp, wc); + goto done; + } + break; + + case IB_WR_RDMA_READ: + if (unlikely(!ipath_rkey_ok(dev, &sqp->s_sge, wqe->length, + wqe->wr.wr.rdma.remote_addr, + wqe->wr.wr.rdma.rkey, + IB_ACCESS_REMOTE_READ))) + goto acc_err; + if (unlikely(!(qp->qp_access_flags & + IB_ACCESS_REMOTE_READ))) + goto acc_err; + qp->r_sge.sge = wqe->sg_list[0]; + qp->r_sge.sg_list = wqe->sg_list + 1; + qp->r_sge.num_sge = wqe->wr.num_sge; + break; + + case IB_WR_ATOMIC_CMP_AND_SWP: + case IB_WR_ATOMIC_FETCH_AND_ADD: + if (unlikely(!ipath_rkey_ok(dev, &qp->r_sge, sizeof(u64), + wqe->wr.wr.rdma.remote_addr, + wqe->wr.wr.rdma.rkey, + IB_ACCESS_REMOTE_ATOMIC))) + goto acc_err; + /* Perform atomic OP and save result. */ + sdata = wqe->wr.wr.atomic.swap; + spin_lock_irqsave(&dev->pending_lock, flags); + qp->r_atomic_data = *(u64 *) qp->r_sge.sge.vaddr; + if (wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) + *(u64 *) qp->r_sge.sge.vaddr = + qp->r_atomic_data + sdata; + else if (qp->r_atomic_data == wqe->wr.wr.atomic.compare_add) + *(u64 *) qp->r_sge.sge.vaddr = sdata; + spin_unlock_irqrestore(&dev->pending_lock, flags); + *(u64 *) sqp->s_sge.sge.vaddr = qp->r_atomic_data; + goto send_comp; + + default: + goto done; + } + + sge = &sqp->s_sge.sge; + while (sqp->s_len) { + u32 len = sqp->s_len; + + if (len > sge->length) + len = sge->length; + BUG_ON(len == 0); + ipath_copy_sge(&qp->r_sge, sge->vaddr, len); + sge->vaddr += len; + sge->length -= len; + sge->sge_length -= len; + if (sge->sge_length == 0) { + if (--sqp->s_sge.num_sge) + *sge = *sqp->s_sge.sg_list++; + } else if (sge->length == 0 && sge->mr != NULL) { + if (++sge->n >= IPATH_SEGSZ) { + if (++sge->m >= sge->mr->mapsz) + break; + sge->n = 0; + } + sge->vaddr = + sge->mr->map[sge->m]->segs[sge->n].vaddr; + sge->length = + sge->mr->map[sge->m]->segs[sge->n].length; + } + sqp->s_len -= len; + } + + if (wqe->wr.opcode == IB_WR_RDMA_WRITE || + wqe->wr.opcode == IB_WR_RDMA_READ) + goto send_comp; + + if (wqe->wr.opcode == IB_WR_RDMA_WRITE_WITH_IMM) + wc->opcode = IB_WC_RECV_RDMA_WITH_IMM; + else + wc->opcode = IB_WC_RECV; + wc->wr_id = qp->r_wr_id; + wc->status = IB_WC_SUCCESS; + wc->vendor_err = 0; + wc->byte_len = wqe->length; + wc->qp_num = qp->ibqp.qp_num; + wc->src_qp = qp->remote_qpn; + /* XXX do we know which pkey matched? Only needed for GSI. */ + wc->pkey_index = 0; + wc->slid = qp->remote_ah_attr.dlid; + wc->sl = qp->remote_ah_attr.sl; + wc->dlid_path_bits = 0; + /* Signal completion event if the solicited bit is set. */ + ipath_cq_enter(to_icq(qp->ibqp.recv_cq), wc, + wqe->wr.send_flags & IB_SEND_SOLICITED); + +send_comp: + sqp->s_rnr_retry = sqp->s_rnr_retry_cnt; + + if (!test_bit(IPATH_S_SIGNAL_REQ_WR, &sqp->s_flags) || + (wqe->wr.send_flags & IB_SEND_SIGNALED)) { + wc->wr_id = wqe->wr.wr_id; + wc->status = IB_WC_SUCCESS; + wc->opcode = ib_ipath_wc_opcode[wqe->wr.opcode]; + wc->vendor_err = 0; + wc->byte_len = wqe->length; + wc->qp_num = sqp->ibqp.qp_num; + wc->src_qp = 0; + wc->pkey_index = 0; + wc->slid = 0; + wc->sl = 0; + wc->dlid_path_bits = 0; + wc->port_num = 0; + ipath_cq_enter(to_icq(sqp->ibqp.send_cq), wc, 0); + } + + /* Update s_last now that we are finished with the SWQE */ + spin_lock_irqsave(&sqp->s_lock, flags); + if (++sqp->s_last >= sqp->s_size) + sqp->s_last = 0; + spin_unlock_irqrestore(&sqp->s_lock, flags); + goto again; + +done: + if (atomic_dec_and_test(&qp->refcount)) + wake_up(&qp->wait); +} + +/** + * ipath_no_bufs_available - tell the layer driver we need buffers + * @qp: the QP that caused the problem + * @dev: the device we ran out of buffers on + * + * Called when we run out of PIO buffers. + */ +void ipath_no_bufs_available(struct ipath_qp *qp, struct ipath_ibdev *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->pending_lock, flags); + if (qp->piowait.next == LIST_POISON1) + list_add_tail(&qp->piowait, &dev->piowait); + spin_unlock_irqrestore(&dev->pending_lock, flags); + /* + * Note that as soon as ipath_layer_want_buffer() is called and + * possibly before it returns, ipath_ib_piobufavail() + * could be called. If we are still in the tasklet function, + * tasklet_hi_schedule() will not call us until the next time + * tasklet_hi_schedule() is called. + * We clear the tasklet flag now since we are committing to return + * from the tasklet function. + */ + clear_bit(IPATH_S_BUSY, &qp->s_flags); + tasklet_unlock(&qp->s_task); + ipath_layer_want_buffer(dev->dd); + dev->n_piowait++; +} + +/** + * ipath_post_rc_send - post RC and UC sends + * @qp: the QP to post on + * @wr: the work request to send + */ +int ipath_post_rc_send(struct ipath_qp *qp, struct ib_send_wr *wr) +{ + struct ipath_swqe *wqe; + unsigned long flags; + u32 next; + int i, j; + int acc; + int ret; + + /* + * Don't allow RDMA reads or atomic operations on UC or + * undefined operations. + * Make sure buffer is large enough to hold the result for atomics. + */ + if (qp->ibqp.qp_type == IB_QPT_UC) { + if ((unsigned) wr->opcode >= IB_WR_RDMA_READ) { + ret = -EINVAL; + goto bail; + } + } else if ((unsigned) wr->opcode > IB_WR_ATOMIC_FETCH_AND_ADD) { + ret = -EINVAL; + goto bail; + } else if (wr->opcode >= IB_WR_ATOMIC_CMP_AND_SWP && + (wr->num_sge == 0 || + wr->sg_list[0].length < sizeof(u64) || + wr->sg_list[0].addr & (sizeof(u64) - 1))) { + ret = -EINVAL; + goto bail; + } + /* IB spec says that num_sge == 0 is OK. */ + if (wr->num_sge > qp->s_max_sge) { + ret = -ENOMEM; + goto bail; + } + spin_lock_irqsave(&qp->s_lock, flags); + next = qp->s_head + 1; + if (next >= qp->s_size) + next = 0; + if (next == qp->s_last) { + spin_unlock_irqrestore(&qp->s_lock, flags); + ret = -EINVAL; + goto bail; + } + + wqe = get_swqe_ptr(qp, qp->s_head); + wqe->wr = *wr; + wqe->ssn = qp->s_ssn++; + wqe->sg_list[0].mr = NULL; + wqe->sg_list[0].vaddr = NULL; + wqe->sg_list[0].length = 0; + wqe->sg_list[0].sge_length = 0; + wqe->length = 0; + acc = wr->opcode >= IB_WR_RDMA_READ ? IB_ACCESS_LOCAL_WRITE : 0; + for (i = 0, j = 0; i < wr->num_sge; i++) { + if (to_ipd(qp->ibqp.pd)->user && wr->sg_list[i].lkey == 0) { + spin_unlock_irqrestore(&qp->s_lock, flags); + ret = -EINVAL; + goto bail; + } + if (wr->sg_list[i].length == 0) + continue; + if (!ipath_lkey_ok(&to_idev(qp->ibqp.device)->lk_table, + &wqe->sg_list[j], &wr->sg_list[i], + acc)) { + spin_unlock_irqrestore(&qp->s_lock, flags); + ret = -EINVAL; + goto bail; + } + wqe->length += wr->sg_list[i].length; + j++; + } + wqe->wr.num_sge = j; + qp->s_head = next; + /* + * Wake up the send tasklet if the QP is not waiting + * for an RNR timeout. + */ + next = qp->s_rnr_timeout; + spin_unlock_irqrestore(&qp->s_lock, flags); + + if (next == 0) { + if (qp->ibqp.qp_type == IB_QPT_UC) + ipath_do_uc_send((unsigned long) qp); + else + ipath_do_rc_send((unsigned long) qp); + } + + ret = 0; + +bail: + return ret; +} diff --git a/drivers/infiniband/hw/ipath/ipath_srq.c b/drivers/infiniband/hw/ipath/ipath_srq.c new file mode 100644 index 000000000000..01c4c6c56118 --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_srq.c @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include "ipath_verbs.h" + +/** + * ipath_post_srq_receive - post a receive on a shared receive queue + * @ibsrq: the SRQ to post the receive on + * @wr: the list of work requests to post + * @bad_wr: the first WR to cause a problem is put here + * + * This may be called from interrupt context. + */ +int ipath_post_srq_receive(struct ib_srq *ibsrq, struct ib_recv_wr *wr, + struct ib_recv_wr **bad_wr) +{ + struct ipath_srq *srq = to_isrq(ibsrq); + struct ipath_ibdev *dev = to_idev(ibsrq->device); + unsigned long flags; + int ret; + + for (; wr; wr = wr->next) { + struct ipath_rwqe *wqe; + u32 next; + int i, j; + + if (wr->num_sge > srq->rq.max_sge) { + *bad_wr = wr; + ret = -ENOMEM; + goto bail; + } + + spin_lock_irqsave(&srq->rq.lock, flags); + next = srq->rq.head + 1; + if (next >= srq->rq.size) + next = 0; + if (next == srq->rq.tail) { + spin_unlock_irqrestore(&srq->rq.lock, flags); + *bad_wr = wr; + ret = -ENOMEM; + goto bail; + } + + wqe = get_rwqe_ptr(&srq->rq, srq->rq.head); + wqe->wr_id = wr->wr_id; + wqe->sg_list[0].mr = NULL; + wqe->sg_list[0].vaddr = NULL; + wqe->sg_list[0].length = 0; + wqe->sg_list[0].sge_length = 0; + wqe->length = 0; + for (i = 0, j = 0; i < wr->num_sge; i++) { + /* Check LKEY */ + if (to_ipd(srq->ibsrq.pd)->user && + wr->sg_list[i].lkey == 0) { + spin_unlock_irqrestore(&srq->rq.lock, + flags); + *bad_wr = wr; + ret = -EINVAL; + goto bail; + } + if (wr->sg_list[i].length == 0) + continue; + if (!ipath_lkey_ok(&dev->lk_table, + &wqe->sg_list[j], + &wr->sg_list[i], + IB_ACCESS_LOCAL_WRITE)) { + spin_unlock_irqrestore(&srq->rq.lock, + flags); + *bad_wr = wr; + ret = -EINVAL; + goto bail; + } + wqe->length += wr->sg_list[i].length; + j++; + } + wqe->num_sge = j; + srq->rq.head = next; + spin_unlock_irqrestore(&srq->rq.lock, flags); + } + ret = 0; + +bail: + return ret; +} + +/** + * ipath_create_srq - create a shared receive queue + * @ibpd: the protection domain of the SRQ to create + * @attr: the attributes of the SRQ + * @udata: not used by the InfiniPath verbs driver + */ +struct ib_srq *ipath_create_srq(struct ib_pd *ibpd, + struct ib_srq_init_attr *srq_init_attr, + struct ib_udata *udata) +{ + struct ipath_srq *srq; + u32 sz; + struct ib_srq *ret; + + if (srq_init_attr->attr.max_sge < 1) { + ret = ERR_PTR(-EINVAL); + goto bail; + } + + srq = kmalloc(sizeof(*srq), GFP_KERNEL); + if (!srq) { + ret = ERR_PTR(-ENOMEM); + goto bail; + } + + /* + * Need to use vmalloc() if we want to support large #s of entries. + */ + srq->rq.size = srq_init_attr->attr.max_wr + 1; + sz = sizeof(struct ipath_sge) * srq_init_attr->attr.max_sge + + sizeof(struct ipath_rwqe); + srq->rq.wq = vmalloc(srq->rq.size * sz); + if (!srq->rq.wq) { + kfree(srq); + ret = ERR_PTR(-ENOMEM); + goto bail; + } + + /* + * ib_create_srq() will initialize srq->ibsrq. + */ + spin_lock_init(&srq->rq.lock); + srq->rq.head = 0; + srq->rq.tail = 0; + srq->rq.max_sge = srq_init_attr->attr.max_sge; + srq->limit = srq_init_attr->attr.srq_limit; + + ret = &srq->ibsrq; + +bail: + return ret; +} + +/** + * ipath_modify_srq - modify a shared receive queue + * @ibsrq: the SRQ to modify + * @attr: the new attributes of the SRQ + * @attr_mask: indicates which attributes to modify + */ +int ipath_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr, + enum ib_srq_attr_mask attr_mask) +{ + struct ipath_srq *srq = to_isrq(ibsrq); + unsigned long flags; + int ret; + + if (attr_mask & IB_SRQ_LIMIT) { + spin_lock_irqsave(&srq->rq.lock, flags); + srq->limit = attr->srq_limit; + spin_unlock_irqrestore(&srq->rq.lock, flags); + } + if (attr_mask & IB_SRQ_MAX_WR) { + u32 size = attr->max_wr + 1; + struct ipath_rwqe *wq, *p; + u32 n; + u32 sz; + + if (attr->max_sge < srq->rq.max_sge) { + ret = -EINVAL; + goto bail; + } + + sz = sizeof(struct ipath_rwqe) + + attr->max_sge * sizeof(struct ipath_sge); + wq = vmalloc(size * sz); + if (!wq) { + ret = -ENOMEM; + goto bail; + } + + spin_lock_irqsave(&srq->rq.lock, flags); + if (srq->rq.head < srq->rq.tail) + n = srq->rq.size + srq->rq.head - srq->rq.tail; + else + n = srq->rq.head - srq->rq.tail; + if (size <= n || size <= srq->limit) { + spin_unlock_irqrestore(&srq->rq.lock, flags); + vfree(wq); + ret = -EINVAL; + goto bail; + } + n = 0; + p = wq; + while (srq->rq.tail != srq->rq.head) { + struct ipath_rwqe *wqe; + int i; + + wqe = get_rwqe_ptr(&srq->rq, srq->rq.tail); + p->wr_id = wqe->wr_id; + p->length = wqe->length; + p->num_sge = wqe->num_sge; + for (i = 0; i < wqe->num_sge; i++) + p->sg_list[i] = wqe->sg_list[i]; + n++; + p = (struct ipath_rwqe *)((char *) p + sz); + if (++srq->rq.tail >= srq->rq.size) + srq->rq.tail = 0; + } + vfree(srq->rq.wq); + srq->rq.wq = wq; + srq->rq.size = size; + srq->rq.head = n; + srq->rq.tail = 0; + srq->rq.max_sge = attr->max_sge; + spin_unlock_irqrestore(&srq->rq.lock, flags); + } + + ret = 0; + +bail: + return ret; +} + +int ipath_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr) +{ + struct ipath_srq *srq = to_isrq(ibsrq); + + attr->max_wr = srq->rq.size - 1; + attr->max_sge = srq->rq.max_sge; + attr->srq_limit = srq->limit; + return 0; +} + +/** + * ipath_destroy_srq - destroy a shared receive queue + * @ibsrq: the SRQ to destroy + */ +int ipath_destroy_srq(struct ib_srq *ibsrq) +{ + struct ipath_srq *srq = to_isrq(ibsrq); + + vfree(srq->rq.wq); + kfree(srq); + + return 0; +} diff --git a/drivers/infiniband/hw/ipath/ipath_stats.c b/drivers/infiniband/hw/ipath/ipath_stats.c new file mode 100644 index 000000000000..fe209137ee74 --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_stats.c @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include "ipath_kernel.h" + +struct infinipath_stats ipath_stats; + +/** + * ipath_snap_cntr - snapshot a chip counter + * @dd: the infinipath device + * @creg: the counter to snapshot + * + * called from add_timer and user counter read calls, to deal with + * counters that wrap in "human time". The words sent and received, and + * the packets sent and received are all that we worry about. For now, + * at least, we don't worry about error counters, because if they wrap + * that quickly, we probably don't care. We may eventually just make this + * handle all the counters. word counters can wrap in about 20 seconds + * of full bandwidth traffic, packet counters in a few hours. + */ + +u64 ipath_snap_cntr(struct ipath_devdata *dd, ipath_creg creg) +{ + u32 val, reg64 = 0; + u64 val64; + unsigned long t0, t1; + u64 ret; + + t0 = jiffies; + /* If fast increment counters are only 32 bits, snapshot them, + * and maintain them as 64bit values in the driver */ + if (!(dd->ipath_flags & IPATH_32BITCOUNTERS) && + (creg == dd->ipath_cregs->cr_wordsendcnt || + creg == dd->ipath_cregs->cr_wordrcvcnt || + creg == dd->ipath_cregs->cr_pktsendcnt || + creg == dd->ipath_cregs->cr_pktrcvcnt)) { + val64 = ipath_read_creg(dd, creg); + val = val64 == ~0ULL ? ~0U : 0; + reg64 = 1; + } else /* val64 just to keep gcc quiet... */ + val64 = val = ipath_read_creg32(dd, creg); + /* + * See if a second has passed. This is just a way to detect things + * that are quite broken. Normally this should take just a few + * cycles (the check is for long enough that we don't care if we get + * pre-empted.) An Opteron HT O read timeout is 4 seconds with + * normal NB values + */ + t1 = jiffies; + if (time_before(t0 + HZ, t1) && val == -1) { + ipath_dev_err(dd, "Error! Read counter 0x%x timed out\n", + creg); + ret = 0ULL; + goto bail; + } + if (reg64) { + ret = val64; + goto bail; + } + + if (creg == dd->ipath_cregs->cr_wordsendcnt) { + if (val != dd->ipath_lastsword) { + dd->ipath_sword += val - dd->ipath_lastsword; + dd->ipath_lastsword = val; + } + val64 = dd->ipath_sword; + } else if (creg == dd->ipath_cregs->cr_wordrcvcnt) { + if (val != dd->ipath_lastrword) { + dd->ipath_rword += val - dd->ipath_lastrword; + dd->ipath_lastrword = val; + } + val64 = dd->ipath_rword; + } else if (creg == dd->ipath_cregs->cr_pktsendcnt) { + if (val != dd->ipath_lastspkts) { + dd->ipath_spkts += val - dd->ipath_lastspkts; + dd->ipath_lastspkts = val; + } + val64 = dd->ipath_spkts; + } else if (creg == dd->ipath_cregs->cr_pktrcvcnt) { + if (val != dd->ipath_lastrpkts) { + dd->ipath_rpkts += val - dd->ipath_lastrpkts; + dd->ipath_lastrpkts = val; + } + val64 = dd->ipath_rpkts; + } else + val64 = (u64) val; + + ret = val64; + +bail: + return ret; +} + +/** + * ipath_qcheck - print delta of egrfull/hdrqfull errors for kernel ports + * @dd: the infinipath device + * + * print the delta of egrfull/hdrqfull errors for kernel ports no more than + * every 5 seconds. User processes are printed at close, but kernel doesn't + * close, so... Separate routine so may call from other places someday, and + * so function name when printed by _IPATH_INFO is meaningfull + */ +static void ipath_qcheck(struct ipath_devdata *dd) +{ + static u64 last_tot_hdrqfull; + size_t blen = 0; + char buf[128]; + + *buf = 0; + if (dd->ipath_pd[0]->port_hdrqfull != dd->ipath_p0_hdrqfull) { + blen = snprintf(buf, sizeof buf, "port 0 hdrqfull %u", + dd->ipath_pd[0]->port_hdrqfull - + dd->ipath_p0_hdrqfull); + dd->ipath_p0_hdrqfull = dd->ipath_pd[0]->port_hdrqfull; + } + if (ipath_stats.sps_etidfull != dd->ipath_last_tidfull) { + blen += snprintf(buf + blen, sizeof buf - blen, + "%srcvegrfull %llu", + blen ? ", " : "", + (unsigned long long) + (ipath_stats.sps_etidfull - + dd->ipath_last_tidfull)); + dd->ipath_last_tidfull = ipath_stats.sps_etidfull; + } + + /* + * this is actually the number of hdrq full interrupts, not actual + * events, but at the moment that's mostly what I'm interested in. + * Actual count, etc. is in the counters, if needed. For production + * users this won't ordinarily be printed. + */ + + if ((ipath_debug & (__IPATH_PKTDBG | __IPATH_DBG)) && + ipath_stats.sps_hdrqfull != last_tot_hdrqfull) { + blen += snprintf(buf + blen, sizeof buf - blen, + "%shdrqfull %llu (all ports)", + blen ? ", " : "", + (unsigned long long) + (ipath_stats.sps_hdrqfull - + last_tot_hdrqfull)); + last_tot_hdrqfull = ipath_stats.sps_hdrqfull; + } + if (blen) + ipath_dbg("%s\n", buf); + + if (dd->ipath_port0head != (u32) + le64_to_cpu(*dd->ipath_hdrqtailptr)) { + if (dd->ipath_lastport0rcv_cnt == + ipath_stats.sps_port0pkts) { + ipath_cdbg(PKT, "missing rcv interrupts? " + "port0 hd=%llx tl=%x; port0pkts %llx\n", + (unsigned long long) + le64_to_cpu(*dd->ipath_hdrqtailptr), + dd->ipath_port0head, + (unsigned long long) + ipath_stats.sps_port0pkts); + ipath_kreceive(dd); + } + dd->ipath_lastport0rcv_cnt = ipath_stats.sps_port0pkts; + } +} + +/** + * ipath_get_faststats - get word counters from chip before they overflow + * @opaque - contains a pointer to the infinipath device ipath_devdata + * + * called from add_timer + */ +void ipath_get_faststats(unsigned long opaque) +{ + struct ipath_devdata *dd = (struct ipath_devdata *) opaque; + u32 val; + static unsigned cnt; + + /* + * don't access the chip while running diags, or memory diags can + * fail + */ + if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_PRESENT) || + ipath_diag_inuse) + /* but re-arm the timer, for diags case; won't hurt other */ + goto done; + + if (dd->ipath_flags & IPATH_32BITCOUNTERS) { + ipath_snap_cntr(dd, dd->ipath_cregs->cr_wordsendcnt); + ipath_snap_cntr(dd, dd->ipath_cregs->cr_wordrcvcnt); + ipath_snap_cntr(dd, dd->ipath_cregs->cr_pktsendcnt); + ipath_snap_cntr(dd, dd->ipath_cregs->cr_pktrcvcnt); + } + + ipath_qcheck(dd); + + /* + * deal with repeat error suppression. Doesn't really matter if + * last error was almost a full interval ago, or just a few usecs + * ago; still won't get more than 2 per interval. We may want + * longer intervals for this eventually, could do with mod, counter + * or separate timer. Also see code in ipath_handle_errors() and + * ipath_handle_hwerrors(). + */ + + if (dd->ipath_lasterror) + dd->ipath_lasterror = 0; + if (dd->ipath_lasthwerror) + dd->ipath_lasthwerror = 0; + if ((dd->ipath_maskederrs & ~dd->ipath_ignorederrs) + && time_after(jiffies, dd->ipath_unmasktime)) { + char ebuf[256]; + ipath_decode_err(ebuf, sizeof ebuf, + (dd->ipath_maskederrs & ~dd-> + ipath_ignorederrs)); + if ((dd->ipath_maskederrs & ~dd->ipath_ignorederrs) & + ~(INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL)) + ipath_dev_err(dd, "Re-enabling masked errors " + "(%s)\n", ebuf); + else { + /* + * rcvegrfull and rcvhdrqfull are "normal", for some + * types of processes (mostly benchmarks) that send + * huge numbers of messages, while not processing + * them. So only complain about these at debug + * level. + */ + ipath_dbg("Disabling frequent queue full errors " + "(%s)\n", ebuf); + } + dd->ipath_maskederrs = dd->ipath_ignorederrs; + ipath_write_kreg(dd, dd->ipath_kregs->kr_errormask, + ~dd->ipath_maskederrs); + } + + /* limit qfull messages to ~one per minute per port */ + if ((++cnt & 0x10)) { + for (val = dd->ipath_cfgports - 1; ((int)val) >= 0; + val--) { + if (dd->ipath_lastegrheads[val] != -1) + dd->ipath_lastegrheads[val] = -1; + if (dd->ipath_lastrcvhdrqtails[val] != -1) + dd->ipath_lastrcvhdrqtails[val] = -1; + } + } + + if (dd->ipath_nosma_bufs) { + dd->ipath_nosma_secs += 5; + if (dd->ipath_nosma_secs >= 30) { + ipath_cdbg(SMA, "No SMA bufs avail %u seconds; " + "cancelling pending sends\n", + dd->ipath_nosma_secs); + /* + * issue an abort as well, in case we have a packet + * stuck in launch fifo. This could corrupt an + * outgoing user packet in the worst case, + * but this is a pretty catastrophic, anyway. + */ + ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, + INFINIPATH_S_ABORT); + ipath_disarm_piobufs(dd, dd->ipath_lastport_piobuf, + dd->ipath_piobcnt2k + + dd->ipath_piobcnt4k - + dd->ipath_lastport_piobuf); + /* start again, if necessary */ + dd->ipath_nosma_secs = 0; + } else + ipath_cdbg(SMA, "No SMA bufs avail %u tries, " + "after %u seconds\n", + dd->ipath_nosma_bufs, + dd->ipath_nosma_secs); + } + +done: + mod_timer(&dd->ipath_stats_timer, jiffies + HZ * 5); +} diff --git a/drivers/infiniband/hw/ipath/ipath_sysfs.c b/drivers/infiniband/hw/ipath/ipath_sysfs.c new file mode 100644 index 000000000000..32acd8048b49 --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_sysfs.c @@ -0,0 +1,778 @@ +/* + * Copyright (c) 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include "ipath_kernel.h" +#include "ips_common.h" +#include "ipath_layer.h" + +/** + * ipath_parse_ushort - parse an unsigned short value in an arbitrary base + * @str: the string containing the number + * @valp: where to put the result + * + * returns the number of bytes consumed, or negative value on error + */ +int ipath_parse_ushort(const char *str, unsigned short *valp) +{ + unsigned long val; + char *end; + int ret; + + if (!isdigit(str[0])) { + ret = -EINVAL; + goto bail; + } + + val = simple_strtoul(str, &end, 0); + + if (val > 0xffff) { + ret = -EINVAL; + goto bail; + } + + *valp = val; + + ret = end + 1 - str; + if (ret == 0) + ret = -EINVAL; + +bail: + return ret; +} + +static ssize_t show_version(struct device_driver *dev, char *buf) +{ + /* The string printed here is already newline-terminated. */ + return scnprintf(buf, PAGE_SIZE, "%s", ipath_core_version); +} + +static ssize_t show_num_units(struct device_driver *dev, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d\n", + ipath_count_units(NULL, NULL, NULL)); +} + +#define DRIVER_STAT(name, attr) \ + static ssize_t show_stat_##name(struct device_driver *dev, \ + char *buf) \ + { \ + return scnprintf( \ + buf, PAGE_SIZE, "%llu\n", \ + (unsigned long long) ipath_stats.sps_ ##attr); \ + } \ + static DRIVER_ATTR(name, S_IRUGO, show_stat_##name, NULL) + +DRIVER_STAT(intrs, ints); +DRIVER_STAT(err_intrs, errints); +DRIVER_STAT(errs, errs); +DRIVER_STAT(pkt_errs, pkterrs); +DRIVER_STAT(crc_errs, crcerrs); +DRIVER_STAT(hw_errs, hwerrs); +DRIVER_STAT(ib_link, iblink); +DRIVER_STAT(port0_pkts, port0pkts); +DRIVER_STAT(ether_spkts, ether_spkts); +DRIVER_STAT(ether_rpkts, ether_rpkts); +DRIVER_STAT(sma_spkts, sma_spkts); +DRIVER_STAT(sma_rpkts, sma_rpkts); +DRIVER_STAT(hdrq_full, hdrqfull); +DRIVER_STAT(etid_full, etidfull); +DRIVER_STAT(no_piobufs, nopiobufs); +DRIVER_STAT(ports, ports); +DRIVER_STAT(pkey0, pkeys[0]); +DRIVER_STAT(pkey1, pkeys[1]); +DRIVER_STAT(pkey2, pkeys[2]); +DRIVER_STAT(pkey3, pkeys[3]); +/* XXX fix the following when dynamic table of devices used */ +DRIVER_STAT(lid0, lid[0]); +DRIVER_STAT(lid1, lid[1]); +DRIVER_STAT(lid2, lid[2]); +DRIVER_STAT(lid3, lid[3]); + +DRIVER_STAT(nports, nports); +DRIVER_STAT(null_intr, nullintr); +DRIVER_STAT(max_pkts_call, maxpkts_call); +DRIVER_STAT(avg_pkts_call, avgpkts_call); +DRIVER_STAT(page_locks, pagelocks); +DRIVER_STAT(page_unlocks, pageunlocks); +DRIVER_STAT(krdrops, krdrops); +/* XXX fix the following when dynamic table of devices used */ +DRIVER_STAT(mlid0, mlid[0]); +DRIVER_STAT(mlid1, mlid[1]); +DRIVER_STAT(mlid2, mlid[2]); +DRIVER_STAT(mlid3, mlid[3]); + +static struct attribute *driver_stat_attributes[] = { + &driver_attr_intrs.attr, + &driver_attr_err_intrs.attr, + &driver_attr_errs.attr, + &driver_attr_pkt_errs.attr, + &driver_attr_crc_errs.attr, + &driver_attr_hw_errs.attr, + &driver_attr_ib_link.attr, + &driver_attr_port0_pkts.attr, + &driver_attr_ether_spkts.attr, + &driver_attr_ether_rpkts.attr, + &driver_attr_sma_spkts.attr, + &driver_attr_sma_rpkts.attr, + &driver_attr_hdrq_full.attr, + &driver_attr_etid_full.attr, + &driver_attr_no_piobufs.attr, + &driver_attr_ports.attr, + &driver_attr_pkey0.attr, + &driver_attr_pkey1.attr, + &driver_attr_pkey2.attr, + &driver_attr_pkey3.attr, + &driver_attr_lid0.attr, + &driver_attr_lid1.attr, + &driver_attr_lid2.attr, + &driver_attr_lid3.attr, + &driver_attr_nports.attr, + &driver_attr_null_intr.attr, + &driver_attr_max_pkts_call.attr, + &driver_attr_avg_pkts_call.attr, + &driver_attr_page_locks.attr, + &driver_attr_page_unlocks.attr, + &driver_attr_krdrops.attr, + &driver_attr_mlid0.attr, + &driver_attr_mlid1.attr, + &driver_attr_mlid2.attr, + &driver_attr_mlid3.attr, + NULL +}; + +static struct attribute_group driver_stat_attr_group = { + .name = "stats", + .attrs = driver_stat_attributes +}; + +static ssize_t show_status(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ipath_devdata *dd = dev_get_drvdata(dev); + ssize_t ret; + + if (!dd->ipath_statusp) { + ret = -EINVAL; + goto bail; + } + + ret = scnprintf(buf, PAGE_SIZE, "0x%llx\n", + (unsigned long long) *(dd->ipath_statusp)); + +bail: + return ret; +} + +static const char *ipath_status_str[] = { + "Initted", + "Disabled", + "Admin_Disabled", + "OIB_SMA", + "SMA", + "Present", + "IB_link_up", + "IB_configured", + "NoIBcable", + "Fatal_Hardware_Error", + NULL, +}; + +static ssize_t show_status_str(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ipath_devdata *dd = dev_get_drvdata(dev); + int i, any; + u64 s; + ssize_t ret; + + if (!dd->ipath_statusp) { + ret = -EINVAL; + goto bail; + } + + s = *(dd->ipath_statusp); + *buf = '\0'; + for (any = i = 0; s && ipath_status_str[i]; i++) { + if (s & 1) { + if (any && strlcat(buf, " ", PAGE_SIZE) >= + PAGE_SIZE) + /* overflow */ + break; + if (strlcat(buf, ipath_status_str[i], + PAGE_SIZE) >= PAGE_SIZE) + break; + any = 1; + } + s >>= 1; + } + if (any) + strlcat(buf, "\n", PAGE_SIZE); + + ret = strlen(buf); + +bail: + return ret; +} + +static ssize_t show_boardversion(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ipath_devdata *dd = dev_get_drvdata(dev); + /* The string printed here is already newline-terminated. */ + return scnprintf(buf, PAGE_SIZE, "%s", dd->ipath_boardversion); +} + +static ssize_t show_lid(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ipath_devdata *dd = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "0x%x\n", dd->ipath_lid); +} + +static ssize_t store_lid(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct ipath_devdata *dd = dev_get_drvdata(dev); + u16 lid; + int ret; + + ret = ipath_parse_ushort(buf, &lid); + if (ret < 0) + goto invalid; + + if (lid == 0 || lid >= 0xc000) { + ret = -EINVAL; + goto invalid; + } + + ipath_set_sps_lid(dd, lid, 0); + + goto bail; +invalid: + ipath_dev_err(dd, "attempt to set invalid LID\n"); +bail: + return ret; +} + +static ssize_t show_mlid(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ipath_devdata *dd = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "0x%x\n", dd->ipath_mlid); +} + +static ssize_t store_mlid(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct ipath_devdata *dd = dev_get_drvdata(dev); + int unit; + u16 mlid; + int ret; + + ret = ipath_parse_ushort(buf, &mlid); + if (ret < 0) + goto invalid; + + unit = dd->ipath_unit; + + dd->ipath_mlid = mlid; + ipath_stats.sps_mlid[unit] = mlid; + ipath_layer_intr(dd, IPATH_LAYER_INT_BCAST); + + goto bail; +invalid: + ipath_dev_err(dd, "attempt to set invalid MLID\n"); +bail: + return ret; +} + +static ssize_t show_guid(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ipath_devdata *dd = dev_get_drvdata(dev); + u8 *guid; + + guid = (u8 *) & (dd->ipath_guid); + + return scnprintf(buf, PAGE_SIZE, + "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + guid[0], guid[1], guid[2], guid[3], + guid[4], guid[5], guid[6], guid[7]); +} + +static ssize_t store_guid(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct ipath_devdata *dd = dev_get_drvdata(dev); + ssize_t ret; + unsigned short guid[8]; + __be64 nguid; + u8 *ng; + int i; + + if (sscanf(buf, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx", + &guid[0], &guid[1], &guid[2], &guid[3], + &guid[4], &guid[5], &guid[6], &guid[7]) != 8) + goto invalid; + + ng = (u8 *) &nguid; + + for (i = 0; i < 8; i++) { + if (guid[i] > 0xff) + goto invalid; + ng[i] = guid[i]; + } + + dd->ipath_guid = nguid; + dd->ipath_nguid = 1; + + ret = strlen(buf); + goto bail; + +invalid: + ipath_dev_err(dd, "attempt to set invalid GUID\n"); + ret = -EINVAL; + +bail: + return ret; +} + +static ssize_t show_nguid(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ipath_devdata *dd = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%u\n", dd->ipath_nguid); +} + +static ssize_t show_serial(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ipath_devdata *dd = dev_get_drvdata(dev); + + buf[sizeof dd->ipath_serial] = '\0'; + memcpy(buf, dd->ipath_serial, sizeof dd->ipath_serial); + strcat(buf, "\n"); + return strlen(buf); +} + +static ssize_t show_unit(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ipath_devdata *dd = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%u\n", dd->ipath_unit); +} + +#define DEVICE_COUNTER(name, attr) \ + static ssize_t show_counter_##name(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ + { \ + struct ipath_devdata *dd = dev_get_drvdata(dev); \ + return scnprintf(\ + buf, PAGE_SIZE, "%llu\n", (unsigned long long) \ + ipath_snap_cntr( \ + dd, offsetof(struct infinipath_counters, \ + attr) / sizeof(u64))); \ + } \ + static DEVICE_ATTR(name, S_IRUGO, show_counter_##name, NULL); + +DEVICE_COUNTER(ib_link_downeds, IBLinkDownedCnt); +DEVICE_COUNTER(ib_link_err_recoveries, IBLinkErrRecoveryCnt); +DEVICE_COUNTER(ib_status_changes, IBStatusChangeCnt); +DEVICE_COUNTER(ib_symbol_errs, IBSymbolErrCnt); +DEVICE_COUNTER(lb_flow_stalls, LBFlowStallCnt); +DEVICE_COUNTER(lb_ints, LBIntCnt); +DEVICE_COUNTER(rx_bad_formats, RxBadFormatCnt); +DEVICE_COUNTER(rx_buf_ovfls, RxBufOvflCnt); +DEVICE_COUNTER(rx_data_pkts, RxDataPktCnt); +DEVICE_COUNTER(rx_dropped_pkts, RxDroppedPktCnt); +DEVICE_COUNTER(rx_dwords, RxDwordCnt); +DEVICE_COUNTER(rx_ebps, RxEBPCnt); +DEVICE_COUNTER(rx_flow_ctrl_errs, RxFlowCtrlErrCnt); +DEVICE_COUNTER(rx_flow_pkts, RxFlowPktCnt); +DEVICE_COUNTER(rx_icrc_errs, RxICRCErrCnt); +DEVICE_COUNTER(rx_len_errs, RxLenErrCnt); +DEVICE_COUNTER(rx_link_problems, RxLinkProblemCnt); +DEVICE_COUNTER(rx_lpcrc_errs, RxLPCRCErrCnt); +DEVICE_COUNTER(rx_max_min_len_errs, RxMaxMinLenErrCnt); +DEVICE_COUNTER(rx_p0_hdr_egr_ovfls, RxP0HdrEgrOvflCnt); +DEVICE_COUNTER(rx_p1_hdr_egr_ovfls, RxP1HdrEgrOvflCnt); +DEVICE_COUNTER(rx_p2_hdr_egr_ovfls, RxP2HdrEgrOvflCnt); +DEVICE_COUNTER(rx_p3_hdr_egr_ovfls, RxP3HdrEgrOvflCnt); +DEVICE_COUNTER(rx_p4_hdr_egr_ovfls, RxP4HdrEgrOvflCnt); +DEVICE_COUNTER(rx_p5_hdr_egr_ovfls, RxP5HdrEgrOvflCnt); +DEVICE_COUNTER(rx_p6_hdr_egr_ovfls, RxP6HdrEgrOvflCnt); +DEVICE_COUNTER(rx_p7_hdr_egr_ovfls, RxP7HdrEgrOvflCnt); +DEVICE_COUNTER(rx_p8_hdr_egr_ovfls, RxP8HdrEgrOvflCnt); +DEVICE_COUNTER(rx_pkey_mismatches, RxPKeyMismatchCnt); +DEVICE_COUNTER(rx_tid_full_errs, RxTIDFullErrCnt); +DEVICE_COUNTER(rx_tid_valid_errs, RxTIDValidErrCnt); +DEVICE_COUNTER(rx_vcrc_errs, RxVCRCErrCnt); +DEVICE_COUNTER(tx_data_pkts, TxDataPktCnt); +DEVICE_COUNTER(tx_dropped_pkts, TxDroppedPktCnt); +DEVICE_COUNTER(tx_dwords, TxDwordCnt); +DEVICE_COUNTER(tx_flow_pkts, TxFlowPktCnt); +DEVICE_COUNTER(tx_flow_stalls, TxFlowStallCnt); +DEVICE_COUNTER(tx_len_errs, TxLenErrCnt); +DEVICE_COUNTER(tx_max_min_len_errs, TxMaxMinLenErrCnt); +DEVICE_COUNTER(tx_underruns, TxUnderrunCnt); +DEVICE_COUNTER(tx_unsup_vl_errs, TxUnsupVLErrCnt); + +static struct attribute *dev_counter_attributes[] = { + &dev_attr_ib_link_downeds.attr, + &dev_attr_ib_link_err_recoveries.attr, + &dev_attr_ib_status_changes.attr, + &dev_attr_ib_symbol_errs.attr, + &dev_attr_lb_flow_stalls.attr, + &dev_attr_lb_ints.attr, + &dev_attr_rx_bad_formats.attr, + &dev_attr_rx_buf_ovfls.attr, + &dev_attr_rx_data_pkts.attr, + &dev_attr_rx_dropped_pkts.attr, + &dev_attr_rx_dwords.attr, + &dev_attr_rx_ebps.attr, + &dev_attr_rx_flow_ctrl_errs.attr, + &dev_attr_rx_flow_pkts.attr, + &dev_attr_rx_icrc_errs.attr, + &dev_attr_rx_len_errs.attr, + &dev_attr_rx_link_problems.attr, + &dev_attr_rx_lpcrc_errs.attr, + &dev_attr_rx_max_min_len_errs.attr, + &dev_attr_rx_p0_hdr_egr_ovfls.attr, + &dev_attr_rx_p1_hdr_egr_ovfls.attr, + &dev_attr_rx_p2_hdr_egr_ovfls.attr, + &dev_attr_rx_p3_hdr_egr_ovfls.attr, + &dev_attr_rx_p4_hdr_egr_ovfls.attr, + &dev_attr_rx_p5_hdr_egr_ovfls.attr, + &dev_attr_rx_p6_hdr_egr_ovfls.attr, + &dev_attr_rx_p7_hdr_egr_ovfls.attr, + &dev_attr_rx_p8_hdr_egr_ovfls.attr, + &dev_attr_rx_pkey_mismatches.attr, + &dev_attr_rx_tid_full_errs.attr, + &dev_attr_rx_tid_valid_errs.attr, + &dev_attr_rx_vcrc_errs.attr, + &dev_attr_tx_data_pkts.attr, + &dev_attr_tx_dropped_pkts.attr, + &dev_attr_tx_dwords.attr, + &dev_attr_tx_flow_pkts.attr, + &dev_attr_tx_flow_stalls.attr, + &dev_attr_tx_len_errs.attr, + &dev_attr_tx_max_min_len_errs.attr, + &dev_attr_tx_underruns.attr, + &dev_attr_tx_unsup_vl_errs.attr, + NULL +}; + +static struct attribute_group dev_counter_attr_group = { + .name = "counters", + .attrs = dev_counter_attributes +}; + +static ssize_t store_reset(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct ipath_devdata *dd = dev_get_drvdata(dev); + int ret; + + if (count < 5 || memcmp(buf, "reset", 5)) { + ret = -EINVAL; + goto bail; + } + + if (dd->ipath_flags & IPATH_DISABLED) { + /* + * post-reset init would re-enable interrupts, etc. + * so don't allow reset on disabled devices. Not + * perfect error, but about the best choice. + */ + dev_info(dev,"Unit %d is disabled, can't reset\n", + dd->ipath_unit); + ret = -EINVAL; + } + ret = ipath_reset_device(dd->ipath_unit); +bail: + return ret<0 ? ret : count; +} + +static ssize_t store_link_state(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct ipath_devdata *dd = dev_get_drvdata(dev); + int ret, r; + u16 state; + + ret = ipath_parse_ushort(buf, &state); + if (ret < 0) + goto invalid; + + r = ipath_layer_set_linkstate(dd, state); + if (r < 0) { + ret = r; + goto bail; + } + + goto bail; +invalid: + ipath_dev_err(dd, "attempt to set invalid link state\n"); +bail: + return ret; +} + +static ssize_t show_mtu(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ipath_devdata *dd = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%u\n", dd->ipath_ibmtu); +} + +static ssize_t store_mtu(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct ipath_devdata *dd = dev_get_drvdata(dev); + ssize_t ret; + u16 mtu = 0; + int r; + + ret = ipath_parse_ushort(buf, &mtu); + if (ret < 0) + goto invalid; + + r = ipath_layer_set_mtu(dd, mtu); + if (r < 0) + ret = r; + + goto bail; +invalid: + ipath_dev_err(dd, "attempt to set invalid MTU\n"); +bail: + return ret; +} + +static ssize_t show_enabled(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ipath_devdata *dd = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%u\n", + (dd->ipath_flags & IPATH_DISABLED) ? 0 : 1); +} + +static ssize_t store_enabled(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct ipath_devdata *dd = dev_get_drvdata(dev); + ssize_t ret; + u16 enable = 0; + + ret = ipath_parse_ushort(buf, &enable); + if (ret < 0) { + ipath_dev_err(dd, "attempt to use non-numeric on enable\n"); + goto bail; + } + + if (enable) { + if (!(dd->ipath_flags & IPATH_DISABLED)) + goto bail; + + dev_info(dev, "Enabling unit %d\n", dd->ipath_unit); + /* same as post-reset */ + ret = ipath_init_chip(dd, 1); + if (ret) + ipath_dev_err(dd, "Failed to enable unit %d\n", + dd->ipath_unit); + else { + dd->ipath_flags &= ~IPATH_DISABLED; + *dd->ipath_statusp &= ~IPATH_STATUS_ADMIN_DISABLED; + } + } + else if (!(dd->ipath_flags & IPATH_DISABLED)) { + dev_info(dev, "Disabling unit %d\n", dd->ipath_unit); + ipath_shutdown_device(dd); + dd->ipath_flags |= IPATH_DISABLED; + *dd->ipath_statusp |= IPATH_STATUS_ADMIN_DISABLED; + } + +bail: + return ret; +} + +static DRIVER_ATTR(num_units, S_IRUGO, show_num_units, NULL); +static DRIVER_ATTR(version, S_IRUGO, show_version, NULL); + +static struct attribute *driver_attributes[] = { + &driver_attr_num_units.attr, + &driver_attr_version.attr, + NULL +}; + +static struct attribute_group driver_attr_group = { + .attrs = driver_attributes +}; + +static DEVICE_ATTR(guid, S_IWUSR | S_IRUGO, show_guid, store_guid); +static DEVICE_ATTR(lid, S_IWUSR | S_IRUGO, show_lid, store_lid); +static DEVICE_ATTR(link_state, S_IWUSR, NULL, store_link_state); +static DEVICE_ATTR(mlid, S_IWUSR | S_IRUGO, show_mlid, store_mlid); +static DEVICE_ATTR(mtu, S_IWUSR | S_IRUGO, show_mtu, store_mtu); +static DEVICE_ATTR(enabled, S_IWUSR | S_IRUGO, show_enabled, store_enabled); +static DEVICE_ATTR(nguid, S_IRUGO, show_nguid, NULL); +static DEVICE_ATTR(reset, S_IWUSR, NULL, store_reset); +static DEVICE_ATTR(serial, S_IRUGO, show_serial, NULL); +static DEVICE_ATTR(status, S_IRUGO, show_status, NULL); +static DEVICE_ATTR(status_str, S_IRUGO, show_status_str, NULL); +static DEVICE_ATTR(boardversion, S_IRUGO, show_boardversion, NULL); +static DEVICE_ATTR(unit, S_IRUGO, show_unit, NULL); + +static struct attribute *dev_attributes[] = { + &dev_attr_guid.attr, + &dev_attr_lid.attr, + &dev_attr_link_state.attr, + &dev_attr_mlid.attr, + &dev_attr_mtu.attr, + &dev_attr_nguid.attr, + &dev_attr_serial.attr, + &dev_attr_status.attr, + &dev_attr_status_str.attr, + &dev_attr_boardversion.attr, + &dev_attr_unit.attr, + &dev_attr_enabled.attr, + NULL +}; + +static struct attribute_group dev_attr_group = { + .attrs = dev_attributes +}; + +/** + * ipath_expose_reset - create a device reset file + * @dev: the device structure + * + * Only expose a file that lets us reset the device after someone + * enters diag mode. A device reset is quite likely to crash the + * machine entirely, so we don't want to normally make it + * available. + */ +int ipath_expose_reset(struct device *dev) +{ + return device_create_file(dev, &dev_attr_reset); +} + +int ipath_driver_create_group(struct device_driver *drv) +{ + int ret; + + ret = sysfs_create_group(&drv->kobj, &driver_attr_group); + if (ret) + goto bail; + + ret = sysfs_create_group(&drv->kobj, &driver_stat_attr_group); + if (ret) + sysfs_remove_group(&drv->kobj, &driver_attr_group); + +bail: + return ret; +} + +void ipath_driver_remove_group(struct device_driver *drv) +{ + sysfs_remove_group(&drv->kobj, &driver_stat_attr_group); + sysfs_remove_group(&drv->kobj, &driver_attr_group); +} + +int ipath_device_create_group(struct device *dev, struct ipath_devdata *dd) +{ + int ret; + char unit[5]; + + ret = sysfs_create_group(&dev->kobj, &dev_attr_group); + if (ret) + goto bail; + + ret = sysfs_create_group(&dev->kobj, &dev_counter_attr_group); + if (ret) + goto bail_attrs; + + snprintf(unit, sizeof(unit), "%02d", dd->ipath_unit); + ret = sysfs_create_link(&dev->driver->kobj, &dev->kobj, unit); + if (ret == 0) + goto bail; + + sysfs_remove_group(&dev->kobj, &dev_counter_attr_group); +bail_attrs: + sysfs_remove_group(&dev->kobj, &dev_attr_group); +bail: + return ret; +} + +void ipath_device_remove_group(struct device *dev, struct ipath_devdata *dd) +{ + char unit[5]; + + snprintf(unit, sizeof(unit), "%02d", dd->ipath_unit); + sysfs_remove_link(&dev->driver->kobj, unit); + + sysfs_remove_group(&dev->kobj, &dev_counter_attr_group); + sysfs_remove_group(&dev->kobj, &dev_attr_group); + + device_remove_file(dev, &dev_attr_reset); +} diff --git a/drivers/infiniband/hw/ipath/ipath_uc.c b/drivers/infiniband/hw/ipath/ipath_uc.c new file mode 100644 index 000000000000..0d6dbc0a541e --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_uc.c @@ -0,0 +1,645 @@ +/* + * Copyright (c) 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ipath_verbs.h" +#include "ips_common.h" + +/* cut down ridiculously long IB macro names */ +#define OP(x) IB_OPCODE_UC_##x + +static void complete_last_send(struct ipath_qp *qp, struct ipath_swqe *wqe, + struct ib_wc *wc) +{ + if (++qp->s_last == qp->s_size) + qp->s_last = 0; + if (!test_bit(IPATH_S_SIGNAL_REQ_WR, &qp->s_flags) || + (wqe->wr.send_flags & IB_SEND_SIGNALED)) { + wc->wr_id = wqe->wr.wr_id; + wc->status = IB_WC_SUCCESS; + wc->opcode = ib_ipath_wc_opcode[wqe->wr.opcode]; + wc->vendor_err = 0; + wc->byte_len = wqe->length; + wc->qp_num = qp->ibqp.qp_num; + wc->src_qp = qp->remote_qpn; + wc->pkey_index = 0; + wc->slid = qp->remote_ah_attr.dlid; + wc->sl = qp->remote_ah_attr.sl; + wc->dlid_path_bits = 0; + wc->port_num = 0; + ipath_cq_enter(to_icq(qp->ibqp.send_cq), wc, 0); + } + wqe = get_swqe_ptr(qp, qp->s_last); +} + +/** + * ipath_do_uc_send - do a send on a UC queue + * @data: contains a pointer to the QP to send on + * + * Process entries in the send work queue until the queue is exhausted. + * Only allow one CPU to send a packet per QP (tasklet). + * Otherwise, after we drop the QP lock, two threads could send + * packets out of order. + * This is similar to ipath_do_rc_send() below except we don't have + * timeouts or resends. + */ +void ipath_do_uc_send(unsigned long data) +{ + struct ipath_qp *qp = (struct ipath_qp *)data; + struct ipath_ibdev *dev = to_idev(qp->ibqp.device); + struct ipath_swqe *wqe; + unsigned long flags; + u16 lrh0; + u32 hwords; + u32 nwords; + u32 extra_bytes; + u32 bth0; + u32 bth2; + u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu); + u32 len; + struct ipath_other_headers *ohdr; + struct ib_wc wc; + + if (test_and_set_bit(IPATH_S_BUSY, &qp->s_flags)) + goto bail; + + if (unlikely(qp->remote_ah_attr.dlid == + ipath_layer_get_lid(dev->dd))) { + /* Pass in an uninitialized ib_wc to save stack space. */ + ipath_ruc_loopback(qp, &wc); + clear_bit(IPATH_S_BUSY, &qp->s_flags); + goto bail; + } + + ohdr = &qp->s_hdr.u.oth; + if (qp->remote_ah_attr.ah_flags & IB_AH_GRH) + ohdr = &qp->s_hdr.u.l.oth; + +again: + /* Check for a constructed packet to be sent. */ + if (qp->s_hdrwords != 0) { + /* + * If no PIO bufs are available, return. + * An interrupt will call ipath_ib_piobufavail() + * when one is available. + */ + if (ipath_verbs_send(dev->dd, qp->s_hdrwords, + (u32 *) &qp->s_hdr, + qp->s_cur_size, + qp->s_cur_sge)) { + ipath_no_bufs_available(qp, dev); + goto bail; + } + dev->n_unicast_xmit++; + /* Record that we sent the packet and s_hdr is empty. */ + qp->s_hdrwords = 0; + } + + lrh0 = IPS_LRH_BTH; + /* header size in 32-bit words LRH+BTH = (8+12)/4. */ + hwords = 5; + + /* + * The lock is needed to synchronize between + * setting qp->s_ack_state and post_send(). + */ + spin_lock_irqsave(&qp->s_lock, flags); + + if (!(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_SEND_OK)) + goto done; + + bth0 = ipath_layer_get_pkey(dev->dd, qp->s_pkey_index); + + /* Send a request. */ + wqe = get_swqe_ptr(qp, qp->s_last); + switch (qp->s_state) { + default: + /* + * Signal the completion of the last send (if there is + * one). + */ + if (qp->s_last != qp->s_tail) + complete_last_send(qp, wqe, &wc); + + /* Check if send work queue is empty. */ + if (qp->s_tail == qp->s_head) + goto done; + /* + * Start a new request. + */ + qp->s_psn = wqe->psn = qp->s_next_psn; + qp->s_sge.sge = wqe->sg_list[0]; + qp->s_sge.sg_list = wqe->sg_list + 1; + qp->s_sge.num_sge = wqe->wr.num_sge; + qp->s_len = len = wqe->length; + switch (wqe->wr.opcode) { + case IB_WR_SEND: + case IB_WR_SEND_WITH_IMM: + if (len > pmtu) { + qp->s_state = OP(SEND_FIRST); + len = pmtu; + break; + } + if (wqe->wr.opcode == IB_WR_SEND) + qp->s_state = OP(SEND_ONLY); + else { + qp->s_state = + OP(SEND_ONLY_WITH_IMMEDIATE); + /* Immediate data comes after the BTH */ + ohdr->u.imm_data = wqe->wr.imm_data; + hwords += 1; + } + if (wqe->wr.send_flags & IB_SEND_SOLICITED) + bth0 |= 1 << 23; + break; + + case IB_WR_RDMA_WRITE: + case IB_WR_RDMA_WRITE_WITH_IMM: + ohdr->u.rc.reth.vaddr = + cpu_to_be64(wqe->wr.wr.rdma.remote_addr); + ohdr->u.rc.reth.rkey = + cpu_to_be32(wqe->wr.wr.rdma.rkey); + ohdr->u.rc.reth.length = cpu_to_be32(len); + hwords += sizeof(struct ib_reth) / 4; + if (len > pmtu) { + qp->s_state = OP(RDMA_WRITE_FIRST); + len = pmtu; + break; + } + if (wqe->wr.opcode == IB_WR_RDMA_WRITE) + qp->s_state = OP(RDMA_WRITE_ONLY); + else { + qp->s_state = + OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE); + /* Immediate data comes after the RETH */ + ohdr->u.rc.imm_data = wqe->wr.imm_data; + hwords += 1; + if (wqe->wr.send_flags & IB_SEND_SOLICITED) + bth0 |= 1 << 23; + } + break; + + default: + goto done; + } + if (++qp->s_tail >= qp->s_size) + qp->s_tail = 0; + break; + + case OP(SEND_FIRST): + qp->s_state = OP(SEND_MIDDLE); + /* FALLTHROUGH */ + case OP(SEND_MIDDLE): + len = qp->s_len; + if (len > pmtu) { + len = pmtu; + break; + } + if (wqe->wr.opcode == IB_WR_SEND) + qp->s_state = OP(SEND_LAST); + else { + qp->s_state = OP(SEND_LAST_WITH_IMMEDIATE); + /* Immediate data comes after the BTH */ + ohdr->u.imm_data = wqe->wr.imm_data; + hwords += 1; + } + if (wqe->wr.send_flags & IB_SEND_SOLICITED) + bth0 |= 1 << 23; + break; + + case OP(RDMA_WRITE_FIRST): + qp->s_state = OP(RDMA_WRITE_MIDDLE); + /* FALLTHROUGH */ + case OP(RDMA_WRITE_MIDDLE): + len = qp->s_len; + if (len > pmtu) { + len = pmtu; + break; + } + if (wqe->wr.opcode == IB_WR_RDMA_WRITE) + qp->s_state = OP(RDMA_WRITE_LAST); + else { + qp->s_state = + OP(RDMA_WRITE_LAST_WITH_IMMEDIATE); + /* Immediate data comes after the BTH */ + ohdr->u.imm_data = wqe->wr.imm_data; + hwords += 1; + if (wqe->wr.send_flags & IB_SEND_SOLICITED) + bth0 |= 1 << 23; + } + break; + } + bth2 = qp->s_next_psn++ & IPS_PSN_MASK; + qp->s_len -= len; + bth0 |= qp->s_state << 24; + + spin_unlock_irqrestore(&qp->s_lock, flags); + + /* Construct the header. */ + extra_bytes = (4 - len) & 3; + nwords = (len + extra_bytes) >> 2; + if (unlikely(qp->remote_ah_attr.ah_flags & IB_AH_GRH)) { + /* Header size in 32-bit words. */ + hwords += 10; + lrh0 = IPS_LRH_GRH; + qp->s_hdr.u.l.grh.version_tclass_flow = + cpu_to_be32((6 << 28) | + (qp->remote_ah_attr.grh.traffic_class + << 20) | + qp->remote_ah_attr.grh.flow_label); + qp->s_hdr.u.l.grh.paylen = + cpu_to_be16(((hwords - 12) + nwords + + SIZE_OF_CRC) << 2); + /* next_hdr is defined by C8-7 in ch. 8.4.1 */ + qp->s_hdr.u.l.grh.next_hdr = 0x1B; + qp->s_hdr.u.l.grh.hop_limit = + qp->remote_ah_attr.grh.hop_limit; + /* The SGID is 32-bit aligned. */ + qp->s_hdr.u.l.grh.sgid.global.subnet_prefix = + dev->gid_prefix; + qp->s_hdr.u.l.grh.sgid.global.interface_id = + ipath_layer_get_guid(dev->dd); + qp->s_hdr.u.l.grh.dgid = qp->remote_ah_attr.grh.dgid; + } + qp->s_hdrwords = hwords; + qp->s_cur_sge = &qp->s_sge; + qp->s_cur_size = len; + lrh0 |= qp->remote_ah_attr.sl << 4; + qp->s_hdr.lrh[0] = cpu_to_be16(lrh0); + /* DEST LID */ + qp->s_hdr.lrh[1] = cpu_to_be16(qp->remote_ah_attr.dlid); + qp->s_hdr.lrh[2] = cpu_to_be16(hwords + nwords + SIZE_OF_CRC); + qp->s_hdr.lrh[3] = cpu_to_be16(ipath_layer_get_lid(dev->dd)); + bth0 |= extra_bytes << 20; + ohdr->bth[0] = cpu_to_be32(bth0); + ohdr->bth[1] = cpu_to_be32(qp->remote_qpn); + ohdr->bth[2] = cpu_to_be32(bth2); + + /* Check for more work to do. */ + goto again; + +done: + spin_unlock_irqrestore(&qp->s_lock, flags); + clear_bit(IPATH_S_BUSY, &qp->s_flags); + +bail: + return; +} + +/** + * ipath_uc_rcv - handle an incoming UC packet + * @dev: the device the packet came in on + * @hdr: the header of the packet + * @has_grh: true if the packet has a GRH + * @data: the packet data + * @tlen: the length of the packet + * @qp: the QP for this packet. + * + * This is called from ipath_qp_rcv() to process an incoming UC packet + * for the given QP. + * Called at interrupt level. + */ +void ipath_uc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, + int has_grh, void *data, u32 tlen, struct ipath_qp *qp) +{ + struct ipath_other_headers *ohdr; + int opcode; + u32 hdrsize; + u32 psn; + u32 pad; + unsigned long flags; + struct ib_wc wc; + u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu); + struct ib_reth *reth; + int header_in_data; + + /* Check for GRH */ + if (!has_grh) { + ohdr = &hdr->u.oth; + hdrsize = 8 + 12; /* LRH + BTH */ + psn = be32_to_cpu(ohdr->bth[2]); + header_in_data = 0; + } else { + ohdr = &hdr->u.l.oth; + hdrsize = 8 + 40 + 12; /* LRH + GRH + BTH */ + /* + * The header with GRH is 60 bytes and the + * core driver sets the eager header buffer + * size to 56 bytes so the last 4 bytes of + * the BTH header (PSN) is in the data buffer. + */ + header_in_data = + ipath_layer_get_rcvhdrentsize(dev->dd) == 16; + if (header_in_data) { + psn = be32_to_cpu(((__be32 *) data)[0]); + data += sizeof(__be32); + } else + psn = be32_to_cpu(ohdr->bth[2]); + } + /* + * The opcode is in the low byte when its in network order + * (top byte when in host order). + */ + opcode = be32_to_cpu(ohdr->bth[0]) >> 24; + + wc.imm_data = 0; + wc.wc_flags = 0; + + spin_lock_irqsave(&qp->r_rq.lock, flags); + + /* Compare the PSN verses the expected PSN. */ + if (unlikely(ipath_cmp24(psn, qp->r_psn) != 0)) { + /* + * Handle a sequence error. + * Silently drop any current message. + */ + qp->r_psn = psn; + inv: + qp->r_state = OP(SEND_LAST); + switch (opcode) { + case OP(SEND_FIRST): + case OP(SEND_ONLY): + case OP(SEND_ONLY_WITH_IMMEDIATE): + goto send_first; + + case OP(RDMA_WRITE_FIRST): + case OP(RDMA_WRITE_ONLY): + case OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE): + goto rdma_first; + + default: + dev->n_pkt_drops++; + goto done; + } + } + + /* Check for opcode sequence errors. */ + switch (qp->r_state) { + case OP(SEND_FIRST): + case OP(SEND_MIDDLE): + if (opcode == OP(SEND_MIDDLE) || + opcode == OP(SEND_LAST) || + opcode == OP(SEND_LAST_WITH_IMMEDIATE)) + break; + goto inv; + + case OP(RDMA_WRITE_FIRST): + case OP(RDMA_WRITE_MIDDLE): + if (opcode == OP(RDMA_WRITE_MIDDLE) || + opcode == OP(RDMA_WRITE_LAST) || + opcode == OP(RDMA_WRITE_LAST_WITH_IMMEDIATE)) + break; + goto inv; + + default: + if (opcode == OP(SEND_FIRST) || + opcode == OP(SEND_ONLY) || + opcode == OP(SEND_ONLY_WITH_IMMEDIATE) || + opcode == OP(RDMA_WRITE_FIRST) || + opcode == OP(RDMA_WRITE_ONLY) || + opcode == OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE)) + break; + goto inv; + } + + /* OK, process the packet. */ + switch (opcode) { + case OP(SEND_FIRST): + case OP(SEND_ONLY): + case OP(SEND_ONLY_WITH_IMMEDIATE): + send_first: + if (qp->r_reuse_sge) { + qp->r_reuse_sge = 0; + qp->r_sge = qp->s_rdma_sge; + } else if (!ipath_get_rwqe(qp, 0)) { + dev->n_pkt_drops++; + goto done; + } + /* Save the WQE so we can reuse it in case of an error. */ + qp->s_rdma_sge = qp->r_sge; + qp->r_rcv_len = 0; + if (opcode == OP(SEND_ONLY)) + goto send_last; + else if (opcode == OP(SEND_ONLY_WITH_IMMEDIATE)) + goto send_last_imm; + /* FALLTHROUGH */ + case OP(SEND_MIDDLE): + /* Check for invalid length PMTU or posted rwqe len. */ + if (unlikely(tlen != (hdrsize + pmtu + 4))) { + qp->r_reuse_sge = 1; + dev->n_pkt_drops++; + goto done; + } + qp->r_rcv_len += pmtu; + if (unlikely(qp->r_rcv_len > qp->r_len)) { + qp->r_reuse_sge = 1; + dev->n_pkt_drops++; + goto done; + } + ipath_copy_sge(&qp->r_sge, data, pmtu); + break; + + case OP(SEND_LAST_WITH_IMMEDIATE): + send_last_imm: + if (header_in_data) { + wc.imm_data = *(__be32 *) data; + data += sizeof(__be32); + } else { + /* Immediate data comes after BTH */ + wc.imm_data = ohdr->u.imm_data; + } + hdrsize += 4; + wc.wc_flags = IB_WC_WITH_IMM; + /* FALLTHROUGH */ + case OP(SEND_LAST): + send_last: + /* Get the number of bytes the message was padded by. */ + pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3; + /* Check for invalid length. */ + /* XXX LAST len should be >= 1 */ + if (unlikely(tlen < (hdrsize + pad + 4))) { + qp->r_reuse_sge = 1; + dev->n_pkt_drops++; + goto done; + } + /* Don't count the CRC. */ + tlen -= (hdrsize + pad + 4); + wc.byte_len = tlen + qp->r_rcv_len; + if (unlikely(wc.byte_len > qp->r_len)) { + qp->r_reuse_sge = 1; + dev->n_pkt_drops++; + goto done; + } + /* XXX Need to free SGEs */ + last_imm: + ipath_copy_sge(&qp->r_sge, data, tlen); + wc.wr_id = qp->r_wr_id; + wc.status = IB_WC_SUCCESS; + wc.opcode = IB_WC_RECV; + wc.vendor_err = 0; + wc.qp_num = qp->ibqp.qp_num; + wc.src_qp = qp->remote_qpn; + wc.pkey_index = 0; + wc.slid = qp->remote_ah_attr.dlid; + wc.sl = qp->remote_ah_attr.sl; + wc.dlid_path_bits = 0; + wc.port_num = 0; + /* Signal completion event if the solicited bit is set. */ + ipath_cq_enter(to_icq(qp->ibqp.recv_cq), &wc, + (ohdr->bth[0] & + __constant_cpu_to_be32(1 << 23)) != 0); + break; + + case OP(RDMA_WRITE_FIRST): + case OP(RDMA_WRITE_ONLY): + case OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE): /* consume RWQE */ + rdma_first: + /* RETH comes after BTH */ + if (!header_in_data) + reth = &ohdr->u.rc.reth; + else { + reth = (struct ib_reth *)data; + data += sizeof(*reth); + } + hdrsize += sizeof(*reth); + qp->r_len = be32_to_cpu(reth->length); + qp->r_rcv_len = 0; + if (qp->r_len != 0) { + u32 rkey = be32_to_cpu(reth->rkey); + u64 vaddr = be64_to_cpu(reth->vaddr); + + /* Check rkey */ + if (unlikely(!ipath_rkey_ok( + dev, &qp->r_sge, qp->r_len, + vaddr, rkey, + IB_ACCESS_REMOTE_WRITE))) { + dev->n_pkt_drops++; + goto done; + } + } else { + qp->r_sge.sg_list = NULL; + qp->r_sge.sge.mr = NULL; + qp->r_sge.sge.vaddr = NULL; + qp->r_sge.sge.length = 0; + qp->r_sge.sge.sge_length = 0; + } + if (unlikely(!(qp->qp_access_flags & + IB_ACCESS_REMOTE_WRITE))) { + dev->n_pkt_drops++; + goto done; + } + if (opcode == OP(RDMA_WRITE_ONLY)) + goto rdma_last; + else if (opcode == + OP(RDMA_WRITE_ONLY_WITH_IMMEDIATE)) + goto rdma_last_imm; + /* FALLTHROUGH */ + case OP(RDMA_WRITE_MIDDLE): + /* Check for invalid length PMTU or posted rwqe len. */ + if (unlikely(tlen != (hdrsize + pmtu + 4))) { + dev->n_pkt_drops++; + goto done; + } + qp->r_rcv_len += pmtu; + if (unlikely(qp->r_rcv_len > qp->r_len)) { + dev->n_pkt_drops++; + goto done; + } + ipath_copy_sge(&qp->r_sge, data, pmtu); + break; + + case OP(RDMA_WRITE_LAST_WITH_IMMEDIATE): + rdma_last_imm: + /* Get the number of bytes the message was padded by. */ + pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3; + /* Check for invalid length. */ + /* XXX LAST len should be >= 1 */ + if (unlikely(tlen < (hdrsize + pad + 4))) { + dev->n_pkt_drops++; + goto done; + } + /* Don't count the CRC. */ + tlen -= (hdrsize + pad + 4); + if (unlikely(tlen + qp->r_rcv_len != qp->r_len)) { + dev->n_pkt_drops++; + goto done; + } + if (qp->r_reuse_sge) { + qp->r_reuse_sge = 0; + } else if (!ipath_get_rwqe(qp, 1)) { + dev->n_pkt_drops++; + goto done; + } + if (header_in_data) { + wc.imm_data = *(__be32 *) data; + data += sizeof(__be32); + } else { + /* Immediate data comes after BTH */ + wc.imm_data = ohdr->u.imm_data; + } + hdrsize += 4; + wc.wc_flags = IB_WC_WITH_IMM; + wc.byte_len = 0; + goto last_imm; + + case OP(RDMA_WRITE_LAST): + rdma_last: + /* Get the number of bytes the message was padded by. */ + pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3; + /* Check for invalid length. */ + /* XXX LAST len should be >= 1 */ + if (unlikely(tlen < (hdrsize + pad + 4))) { + dev->n_pkt_drops++; + goto done; + } + /* Don't count the CRC. */ + tlen -= (hdrsize + pad + 4); + if (unlikely(tlen + qp->r_rcv_len != qp->r_len)) { + dev->n_pkt_drops++; + goto done; + } + ipath_copy_sge(&qp->r_sge, data, tlen); + break; + + default: + /* Drop packet for unknown opcodes. */ + spin_unlock_irqrestore(&qp->r_rq.lock, flags); + dev->n_pkt_drops++; + goto bail; + } + qp->r_psn++; + qp->r_state = opcode; +done: + spin_unlock_irqrestore(&qp->r_rq.lock, flags); + +bail: + return; +} diff --git a/drivers/infiniband/hw/ipath/ipath_ud.c b/drivers/infiniband/hw/ipath/ipath_ud.c new file mode 100644 index 000000000000..5ff3de6128b2 --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_ud.c @@ -0,0 +1,621 @@ +/* + * Copyright (c) 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include "ipath_verbs.h" +#include "ips_common.h" + +/** + * ipath_ud_loopback - handle send on loopback QPs + * @sqp: the QP + * @ss: the SGE state + * @length: the length of the data to send + * @wr: the work request + * @wc: the work completion entry + * + * This is called from ipath_post_ud_send() to forward a WQE addressed + * to the same HCA. + */ +void ipath_ud_loopback(struct ipath_qp *sqp, struct ipath_sge_state *ss, + u32 length, struct ib_send_wr *wr, struct ib_wc *wc) +{ + struct ipath_ibdev *dev = to_idev(sqp->ibqp.device); + struct ipath_qp *qp; + struct ib_ah_attr *ah_attr; + unsigned long flags; + struct ipath_rq *rq; + struct ipath_srq *srq; + struct ipath_sge_state rsge; + struct ipath_sge *sge; + struct ipath_rwqe *wqe; + + qp = ipath_lookup_qpn(&dev->qp_table, wr->wr.ud.remote_qpn); + if (!qp) + return; + + /* + * Check that the qkey matches (except for QP0, see 9.6.1.4.1). + * Qkeys with the high order bit set mean use the + * qkey from the QP context instead of the WR (see 10.2.5). + */ + if (unlikely(qp->ibqp.qp_num && + ((int) wr->wr.ud.remote_qkey < 0 + ? qp->qkey : wr->wr.ud.remote_qkey) != qp->qkey)) { + /* XXX OK to lose a count once in a while. */ + dev->qkey_violations++; + dev->n_pkt_drops++; + goto done; + } + + /* + * A GRH is expected to preceed the data even if not + * present on the wire. + */ + wc->byte_len = length + sizeof(struct ib_grh); + + if (wr->opcode == IB_WR_SEND_WITH_IMM) { + wc->wc_flags = IB_WC_WITH_IMM; + wc->imm_data = wr->imm_data; + } else { + wc->wc_flags = 0; + wc->imm_data = 0; + } + + /* + * Get the next work request entry to find where to put the data. + * Note that it is safe to drop the lock after changing rq->tail + * since ipath_post_receive() won't fill the empty slot. + */ + if (qp->ibqp.srq) { + srq = to_isrq(qp->ibqp.srq); + rq = &srq->rq; + } else { + srq = NULL; + rq = &qp->r_rq; + } + spin_lock_irqsave(&rq->lock, flags); + if (rq->tail == rq->head) { + spin_unlock_irqrestore(&rq->lock, flags); + dev->n_pkt_drops++; + goto done; + } + /* Silently drop packets which are too big. */ + wqe = get_rwqe_ptr(rq, rq->tail); + if (wc->byte_len > wqe->length) { + spin_unlock_irqrestore(&rq->lock, flags); + dev->n_pkt_drops++; + goto done; + } + wc->wr_id = wqe->wr_id; + rsge.sge = wqe->sg_list[0]; + rsge.sg_list = wqe->sg_list + 1; + rsge.num_sge = wqe->num_sge; + if (++rq->tail >= rq->size) + rq->tail = 0; + if (srq && srq->ibsrq.event_handler) { + u32 n; + + if (rq->head < rq->tail) + n = rq->size + rq->head - rq->tail; + else + n = rq->head - rq->tail; + if (n < srq->limit) { + struct ib_event ev; + + srq->limit = 0; + spin_unlock_irqrestore(&rq->lock, flags); + ev.device = qp->ibqp.device; + ev.element.srq = qp->ibqp.srq; + ev.event = IB_EVENT_SRQ_LIMIT_REACHED; + srq->ibsrq.event_handler(&ev, + srq->ibsrq.srq_context); + } else + spin_unlock_irqrestore(&rq->lock, flags); + } else + spin_unlock_irqrestore(&rq->lock, flags); + ah_attr = &to_iah(wr->wr.ud.ah)->attr; + if (ah_attr->ah_flags & IB_AH_GRH) { + ipath_copy_sge(&rsge, &ah_attr->grh, sizeof(struct ib_grh)); + wc->wc_flags |= IB_WC_GRH; + } else + ipath_skip_sge(&rsge, sizeof(struct ib_grh)); + sge = &ss->sge; + while (length) { + u32 len = sge->length; + + if (len > length) + len = length; + BUG_ON(len == 0); + ipath_copy_sge(&rsge, sge->vaddr, len); + sge->vaddr += len; + sge->length -= len; + sge->sge_length -= len; + if (sge->sge_length == 0) { + if (--ss->num_sge) + *sge = *ss->sg_list++; + } else if (sge->length == 0 && sge->mr != NULL) { + if (++sge->n >= IPATH_SEGSZ) { + if (++sge->m >= sge->mr->mapsz) + break; + sge->n = 0; + } + sge->vaddr = + sge->mr->map[sge->m]->segs[sge->n].vaddr; + sge->length = + sge->mr->map[sge->m]->segs[sge->n].length; + } + length -= len; + } + wc->status = IB_WC_SUCCESS; + wc->opcode = IB_WC_RECV; + wc->vendor_err = 0; + wc->qp_num = qp->ibqp.qp_num; + wc->src_qp = sqp->ibqp.qp_num; + /* XXX do we know which pkey matched? Only needed for GSI. */ + wc->pkey_index = 0; + wc->slid = ipath_layer_get_lid(dev->dd) | + (ah_attr->src_path_bits & + ((1 << (dev->mkeyprot_resv_lmc & 7)) - 1)); + wc->sl = ah_attr->sl; + wc->dlid_path_bits = + ah_attr->dlid & ((1 << (dev->mkeyprot_resv_lmc & 7)) - 1); + /* Signal completion event if the solicited bit is set. */ + ipath_cq_enter(to_icq(qp->ibqp.recv_cq), wc, + wr->send_flags & IB_SEND_SOLICITED); + +done: + if (atomic_dec_and_test(&qp->refcount)) + wake_up(&qp->wait); +} + +/** + * ipath_post_ud_send - post a UD send on QP + * @qp: the QP + * @wr: the work request + * + * Note that we actually send the data as it is posted instead of putting + * the request into a ring buffer. If we wanted to use a ring buffer, + * we would need to save a reference to the destination address in the SWQE. + */ +int ipath_post_ud_send(struct ipath_qp *qp, struct ib_send_wr *wr) +{ + struct ipath_ibdev *dev = to_idev(qp->ibqp.device); + struct ipath_other_headers *ohdr; + struct ib_ah_attr *ah_attr; + struct ipath_sge_state ss; + struct ipath_sge *sg_list; + struct ib_wc wc; + u32 hwords; + u32 nwords; + u32 len; + u32 extra_bytes; + u32 bth0; + u16 lrh0; + u16 lid; + int i; + int ret; + + if (!(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_SEND_OK)) { + ret = 0; + goto bail; + } + + /* IB spec says that num_sge == 0 is OK. */ + if (wr->num_sge > qp->s_max_sge) { + ret = -EINVAL; + goto bail; + } + + if (wr->num_sge > 1) { + sg_list = kmalloc((qp->s_max_sge - 1) * sizeof(*sg_list), + GFP_ATOMIC); + if (!sg_list) { + ret = -ENOMEM; + goto bail; + } + } else + sg_list = NULL; + + /* Check the buffer to send. */ + ss.sg_list = sg_list; + ss.sge.mr = NULL; + ss.sge.vaddr = NULL; + ss.sge.length = 0; + ss.sge.sge_length = 0; + ss.num_sge = 0; + len = 0; + for (i = 0; i < wr->num_sge; i++) { + /* Check LKEY */ + if (to_ipd(qp->ibqp.pd)->user && wr->sg_list[i].lkey == 0) { + ret = -EINVAL; + goto bail; + } + + if (wr->sg_list[i].length == 0) + continue; + if (!ipath_lkey_ok(&dev->lk_table, ss.num_sge ? + sg_list + ss.num_sge - 1 : &ss.sge, + &wr->sg_list[i], 0)) { + ret = -EINVAL; + goto bail; + } + len += wr->sg_list[i].length; + ss.num_sge++; + } + extra_bytes = (4 - len) & 3; + nwords = (len + extra_bytes) >> 2; + + /* Construct the header. */ + ah_attr = &to_iah(wr->wr.ud.ah)->attr; + if (ah_attr->dlid == 0) { + ret = -EINVAL; + goto bail; + } + if (ah_attr->dlid >= IPS_MULTICAST_LID_BASE) { + if (ah_attr->dlid != IPS_PERMISSIVE_LID) + dev->n_multicast_xmit++; + else + dev->n_unicast_xmit++; + } else { + dev->n_unicast_xmit++; + lid = ah_attr->dlid & + ~((1 << (dev->mkeyprot_resv_lmc & 7)) - 1); + if (unlikely(lid == ipath_layer_get_lid(dev->dd))) { + /* + * Pass in an uninitialized ib_wc to save stack + * space. + */ + ipath_ud_loopback(qp, &ss, len, wr, &wc); + goto done; + } + } + if (ah_attr->ah_flags & IB_AH_GRH) { + /* Header size in 32-bit words. */ + hwords = 17; + lrh0 = IPS_LRH_GRH; + ohdr = &qp->s_hdr.u.l.oth; + qp->s_hdr.u.l.grh.version_tclass_flow = + cpu_to_be32((6 << 28) | + (ah_attr->grh.traffic_class << 20) | + ah_attr->grh.flow_label); + qp->s_hdr.u.l.grh.paylen = + cpu_to_be16(((wr->opcode == + IB_WR_SEND_WITH_IMM ? 6 : 5) + + nwords + SIZE_OF_CRC) << 2); + /* next_hdr is defined by C8-7 in ch. 8.4.1 */ + qp->s_hdr.u.l.grh.next_hdr = 0x1B; + qp->s_hdr.u.l.grh.hop_limit = ah_attr->grh.hop_limit; + /* The SGID is 32-bit aligned. */ + qp->s_hdr.u.l.grh.sgid.global.subnet_prefix = + dev->gid_prefix; + qp->s_hdr.u.l.grh.sgid.global.interface_id = + ipath_layer_get_guid(dev->dd); + qp->s_hdr.u.l.grh.dgid = ah_attr->grh.dgid; + /* + * Don't worry about sending to locally attached multicast + * QPs. It is unspecified by the spec. what happens. + */ + } else { + /* Header size in 32-bit words. */ + hwords = 7; + lrh0 = IPS_LRH_BTH; + ohdr = &qp->s_hdr.u.oth; + } + if (wr->opcode == IB_WR_SEND_WITH_IMM) { + ohdr->u.ud.imm_data = wr->imm_data; + wc.imm_data = wr->imm_data; + hwords += 1; + bth0 = IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE << 24; + } else if (wr->opcode == IB_WR_SEND) { + wc.imm_data = 0; + bth0 = IB_OPCODE_UD_SEND_ONLY << 24; + } else { + ret = -EINVAL; + goto bail; + } + lrh0 |= ah_attr->sl << 4; + if (qp->ibqp.qp_type == IB_QPT_SMI) + lrh0 |= 0xF000; /* Set VL (see ch. 13.5.3.1) */ + qp->s_hdr.lrh[0] = cpu_to_be16(lrh0); + qp->s_hdr.lrh[1] = cpu_to_be16(ah_attr->dlid); /* DEST LID */ + qp->s_hdr.lrh[2] = cpu_to_be16(hwords + nwords + SIZE_OF_CRC); + lid = ipath_layer_get_lid(dev->dd); + if (lid) { + lid |= ah_attr->src_path_bits & + ((1 << (dev->mkeyprot_resv_lmc & 7)) - 1); + qp->s_hdr.lrh[3] = cpu_to_be16(lid); + } else + qp->s_hdr.lrh[3] = IB_LID_PERMISSIVE; + if (wr->send_flags & IB_SEND_SOLICITED) + bth0 |= 1 << 23; + bth0 |= extra_bytes << 20; + bth0 |= qp->ibqp.qp_type == IB_QPT_SMI ? IPS_DEFAULT_P_KEY : + ipath_layer_get_pkey(dev->dd, qp->s_pkey_index); + ohdr->bth[0] = cpu_to_be32(bth0); + /* + * Use the multicast QP if the destination LID is a multicast LID. + */ + ohdr->bth[1] = ah_attr->dlid >= IPS_MULTICAST_LID_BASE && + ah_attr->dlid != IPS_PERMISSIVE_LID ? + __constant_cpu_to_be32(IPS_MULTICAST_QPN) : + cpu_to_be32(wr->wr.ud.remote_qpn); + /* XXX Could lose a PSN count but not worth locking */ + ohdr->bth[2] = cpu_to_be32(qp->s_next_psn++ & IPS_PSN_MASK); + /* + * Qkeys with the high order bit set mean use the + * qkey from the QP context instead of the WR (see 10.2.5). + */ + ohdr->u.ud.deth[0] = cpu_to_be32((int)wr->wr.ud.remote_qkey < 0 ? + qp->qkey : wr->wr.ud.remote_qkey); + ohdr->u.ud.deth[1] = cpu_to_be32(qp->ibqp.qp_num); + if (ipath_verbs_send(dev->dd, hwords, (u32 *) &qp->s_hdr, + len, &ss)) + dev->n_no_piobuf++; + +done: + /* Queue the completion status entry. */ + if (!test_bit(IPATH_S_SIGNAL_REQ_WR, &qp->s_flags) || + (wr->send_flags & IB_SEND_SIGNALED)) { + wc.wr_id = wr->wr_id; + wc.status = IB_WC_SUCCESS; + wc.vendor_err = 0; + wc.opcode = IB_WC_SEND; + wc.byte_len = len; + wc.qp_num = qp->ibqp.qp_num; + wc.src_qp = 0; + wc.wc_flags = 0; + /* XXX initialize other fields? */ + ipath_cq_enter(to_icq(qp->ibqp.send_cq), &wc, 0); + } + kfree(sg_list); + + ret = 0; + +bail: + return ret; +} + +/** + * ipath_ud_rcv - receive an incoming UD packet + * @dev: the device the packet came in on + * @hdr: the packet header + * @has_grh: true if the packet has a GRH + * @data: the packet data + * @tlen: the packet length + * @qp: the QP the packet came on + * + * This is called from ipath_qp_rcv() to process an incoming UD packet + * for the given QP. + * Called at interrupt level. + */ +void ipath_ud_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, + int has_grh, void *data, u32 tlen, struct ipath_qp *qp) +{ + struct ipath_other_headers *ohdr; + int opcode; + u32 hdrsize; + u32 pad; + unsigned long flags; + struct ib_wc wc; + u32 qkey; + u32 src_qp; + struct ipath_rq *rq; + struct ipath_srq *srq; + struct ipath_rwqe *wqe; + u16 dlid; + int header_in_data; + + /* Check for GRH */ + if (!has_grh) { + ohdr = &hdr->u.oth; + hdrsize = 8 + 12 + 8; /* LRH + BTH + DETH */ + qkey = be32_to_cpu(ohdr->u.ud.deth[0]); + src_qp = be32_to_cpu(ohdr->u.ud.deth[1]); + header_in_data = 0; + } else { + ohdr = &hdr->u.l.oth; + hdrsize = 8 + 40 + 12 + 8; /* LRH + GRH + BTH + DETH */ + /* + * The header with GRH is 68 bytes and the core driver sets + * the eager header buffer size to 56 bytes so the last 12 + * bytes of the IB header is in the data buffer. + */ + header_in_data = + ipath_layer_get_rcvhdrentsize(dev->dd) == 16; + if (header_in_data) { + qkey = be32_to_cpu(((__be32 *) data)[1]); + src_qp = be32_to_cpu(((__be32 *) data)[2]); + data += 12; + } else { + qkey = be32_to_cpu(ohdr->u.ud.deth[0]); + src_qp = be32_to_cpu(ohdr->u.ud.deth[1]); + } + } + src_qp &= IPS_QPN_MASK; + + /* + * Check that the permissive LID is only used on QP0 + * and the QKEY matches (see 9.6.1.4.1 and 9.6.1.5.1). + */ + if (qp->ibqp.qp_num) { + if (unlikely(hdr->lrh[1] == IB_LID_PERMISSIVE || + hdr->lrh[3] == IB_LID_PERMISSIVE)) { + dev->n_pkt_drops++; + goto bail; + } + if (unlikely(qkey != qp->qkey)) { + /* XXX OK to lose a count once in a while. */ + dev->qkey_violations++; + dev->n_pkt_drops++; + goto bail; + } + } else if (hdr->lrh[1] == IB_LID_PERMISSIVE || + hdr->lrh[3] == IB_LID_PERMISSIVE) { + struct ib_smp *smp = (struct ib_smp *) data; + + if (smp->mgmt_class != IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) { + dev->n_pkt_drops++; + goto bail; + } + } + + /* Get the number of bytes the message was padded by. */ + pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3; + if (unlikely(tlen < (hdrsize + pad + 4))) { + /* Drop incomplete packets. */ + dev->n_pkt_drops++; + goto bail; + } + tlen -= hdrsize + pad + 4; + + /* Drop invalid MAD packets (see 13.5.3.1). */ + if (unlikely((qp->ibqp.qp_num == 0 && + (tlen != 256 || + (be16_to_cpu(hdr->lrh[0]) >> 12) != 15)) || + (qp->ibqp.qp_num == 1 && + (tlen != 256 || + (be16_to_cpu(hdr->lrh[0]) >> 12) == 15)))) { + dev->n_pkt_drops++; + goto bail; + } + + /* + * A GRH is expected to preceed the data even if not + * present on the wire. + */ + wc.byte_len = tlen + sizeof(struct ib_grh); + + /* + * The opcode is in the low byte when its in network order + * (top byte when in host order). + */ + opcode = be32_to_cpu(ohdr->bth[0]) >> 24; + if (qp->ibqp.qp_num > 1 && + opcode == IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE) { + if (header_in_data) { + wc.imm_data = *(__be32 *) data; + data += sizeof(__be32); + } else + wc.imm_data = ohdr->u.ud.imm_data; + wc.wc_flags = IB_WC_WITH_IMM; + hdrsize += sizeof(u32); + } else if (opcode == IB_OPCODE_UD_SEND_ONLY) { + wc.imm_data = 0; + wc.wc_flags = 0; + } else { + dev->n_pkt_drops++; + goto bail; + } + + /* + * Get the next work request entry to find where to put the data. + * Note that it is safe to drop the lock after changing rq->tail + * since ipath_post_receive() won't fill the empty slot. + */ + if (qp->ibqp.srq) { + srq = to_isrq(qp->ibqp.srq); + rq = &srq->rq; + } else { + srq = NULL; + rq = &qp->r_rq; + } + spin_lock_irqsave(&rq->lock, flags); + if (rq->tail == rq->head) { + spin_unlock_irqrestore(&rq->lock, flags); + dev->n_pkt_drops++; + goto bail; + } + /* Silently drop packets which are too big. */ + wqe = get_rwqe_ptr(rq, rq->tail); + if (wc.byte_len > wqe->length) { + spin_unlock_irqrestore(&rq->lock, flags); + dev->n_pkt_drops++; + goto bail; + } + wc.wr_id = wqe->wr_id; + qp->r_sge.sge = wqe->sg_list[0]; + qp->r_sge.sg_list = wqe->sg_list + 1; + qp->r_sge.num_sge = wqe->num_sge; + if (++rq->tail >= rq->size) + rq->tail = 0; + if (srq && srq->ibsrq.event_handler) { + u32 n; + + if (rq->head < rq->tail) + n = rq->size + rq->head - rq->tail; + else + n = rq->head - rq->tail; + if (n < srq->limit) { + struct ib_event ev; + + srq->limit = 0; + spin_unlock_irqrestore(&rq->lock, flags); + ev.device = qp->ibqp.device; + ev.element.srq = qp->ibqp.srq; + ev.event = IB_EVENT_SRQ_LIMIT_REACHED; + srq->ibsrq.event_handler(&ev, + srq->ibsrq.srq_context); + } else + spin_unlock_irqrestore(&rq->lock, flags); + } else + spin_unlock_irqrestore(&rq->lock, flags); + if (has_grh) { + ipath_copy_sge(&qp->r_sge, &hdr->u.l.grh, + sizeof(struct ib_grh)); + wc.wc_flags |= IB_WC_GRH; + } else + ipath_skip_sge(&qp->r_sge, sizeof(struct ib_grh)); + ipath_copy_sge(&qp->r_sge, data, + wc.byte_len - sizeof(struct ib_grh)); + wc.status = IB_WC_SUCCESS; + wc.opcode = IB_WC_RECV; + wc.vendor_err = 0; + wc.qp_num = qp->ibqp.qp_num; + wc.src_qp = src_qp; + /* XXX do we know which pkey matched? Only needed for GSI. */ + wc.pkey_index = 0; + wc.slid = be16_to_cpu(hdr->lrh[3]); + wc.sl = (be16_to_cpu(hdr->lrh[0]) >> 4) & 0xF; + dlid = be16_to_cpu(hdr->lrh[1]); + /* + * Save the LMC lower bits if the destination LID is a unicast LID. + */ + wc.dlid_path_bits = dlid >= IPS_MULTICAST_LID_BASE ? 0 : + dlid & ((1 << (dev->mkeyprot_resv_lmc & 7)) - 1); + /* Signal completion event if the solicited bit is set. */ + ipath_cq_enter(to_icq(qp->ibqp.recv_cq), &wc, + (ohdr->bth[0] & + __constant_cpu_to_be32(1 << 23)) != 0); + +bail:; +} diff --git a/drivers/infiniband/hw/ipath/ipath_user_pages.c b/drivers/infiniband/hw/ipath/ipath_user_pages.c new file mode 100644 index 000000000000..2bb08afc86d0 --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_user_pages.c @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include "ipath_kernel.h" + +static void __ipath_release_user_pages(struct page **p, size_t num_pages, + int dirty) +{ + size_t i; + + for (i = 0; i < num_pages; i++) { + ipath_cdbg(MM, "%lu/%lu put_page %p\n", (unsigned long) i, + (unsigned long) num_pages, p[i]); + if (dirty) + set_page_dirty_lock(p[i]); + put_page(p[i]); + } +} + +/* call with current->mm->mmap_sem held */ +static int __get_user_pages(unsigned long start_page, size_t num_pages, + struct page **p, struct vm_area_struct **vma) +{ + unsigned long lock_limit; + size_t got; + int ret; + +#if 0 + /* + * XXX - causes MPI programs to fail, haven't had time to check + * yet + */ + if (!capable(CAP_IPC_LOCK)) { + ret = -EPERM; + goto bail; + } +#endif + + lock_limit = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur >> + PAGE_SHIFT; + + if (num_pages > lock_limit) { + ret = -ENOMEM; + goto bail; + } + + ipath_cdbg(VERBOSE, "pin %lx pages from vaddr %lx\n", + (unsigned long) num_pages, start_page); + + for (got = 0; got < num_pages; got += ret) { + ret = get_user_pages(current, current->mm, + start_page + got * PAGE_SIZE, + num_pages - got, 1, 1, + p + got, vma); + if (ret < 0) + goto bail_release; + } + + current->mm->locked_vm += num_pages; + + ret = 0; + goto bail; + +bail_release: + __ipath_release_user_pages(p, got, 0); +bail: + return ret; +} + +/** + * ipath_get_user_pages - lock user pages into memory + * @start_page: the start page + * @num_pages: the number of pages + * @p: the output page structures + * + * This function takes a given start page (page aligned user virtual + * address) and pins it and the following specified number of pages. For + * now, num_pages is always 1, but that will probably change at some point + * (because caller is doing expected sends on a single virtually contiguous + * buffer, so we can do all pages at once). + */ +int ipath_get_user_pages(unsigned long start_page, size_t num_pages, + struct page **p) +{ + int ret; + + down_write(¤t->mm->mmap_sem); + + ret = __get_user_pages(start_page, num_pages, p, NULL); + + up_write(¤t->mm->mmap_sem); + + return ret; +} + +/** + * ipath_get_user_pages_nocopy - lock a single page for I/O and mark shared + * @start_page: the page to lock + * @p: the output page structure + * + * This is similar to ipath_get_user_pages, but it's always one page, and we + * mark the page as locked for I/O, and shared. This is used for the user + * process page that contains the destination address for the rcvhdrq tail + * update, so we need to have the vma. If we don't do this, the page can be + * taken away from us on fork, even if the child never touches it, and then + * the user process never sees the tail register updates. + */ +int ipath_get_user_pages_nocopy(unsigned long page, struct page **p) +{ + struct vm_area_struct *vma; + int ret; + + down_write(¤t->mm->mmap_sem); + + ret = __get_user_pages(page, 1, p, &vma); + + up_write(¤t->mm->mmap_sem); + + return ret; +} + +void ipath_release_user_pages(struct page **p, size_t num_pages) +{ + down_write(¤t->mm->mmap_sem); + + __ipath_release_user_pages(p, num_pages, 1); + + current->mm->locked_vm -= num_pages; + + up_write(¤t->mm->mmap_sem); +} + +struct ipath_user_pages_work { + struct work_struct work; + struct mm_struct *mm; + unsigned long num_pages; +}; + +static void user_pages_account(void *ptr) +{ + struct ipath_user_pages_work *work = ptr; + + down_write(&work->mm->mmap_sem); + work->mm->locked_vm -= work->num_pages; + up_write(&work->mm->mmap_sem); + mmput(work->mm); + kfree(work); +} + +void ipath_release_user_pages_on_close(struct page **p, size_t num_pages) +{ + struct ipath_user_pages_work *work; + struct mm_struct *mm; + + __ipath_release_user_pages(p, num_pages, 1); + + mm = get_task_mm(current); + if (!mm) + goto bail; + + work = kmalloc(sizeof(*work), GFP_KERNEL); + if (!work) + goto bail_mm; + + goto bail; + + INIT_WORK(&work->work, user_pages_account, work); + work->mm = mm; + work->num_pages = num_pages; + +bail_mm: + mmput(mm); +bail: + return; +} diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.c b/drivers/infiniband/hw/ipath/ipath_verbs.c new file mode 100644 index 000000000000..9f27fd35cdbb --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_verbs.c @@ -0,0 +1,1222 @@ +/* + * Copyright (c) 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + +#include "ipath_kernel.h" +#include "ipath_verbs.h" +#include "ips_common.h" + +/* Not static, because we don't want the compiler removing it */ +const char ipath_verbs_version[] = "ipath_verbs " IPATH_IDSTR; + +unsigned int ib_ipath_qp_table_size = 251; +module_param_named(qp_table_size, ib_ipath_qp_table_size, uint, S_IRUGO); +MODULE_PARM_DESC(qp_table_size, "QP table size"); + +unsigned int ib_ipath_lkey_table_size = 12; +module_param_named(lkey_table_size, ib_ipath_lkey_table_size, uint, + S_IRUGO); +MODULE_PARM_DESC(lkey_table_size, + "LKEY table size in bits (2^n, 1 <= n <= 23)"); + +unsigned int ib_ipath_debug; /* debug mask */ +module_param_named(debug, ib_ipath_debug, uint, S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(debug, "Verbs debug mask"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("PathScale "); +MODULE_DESCRIPTION("Pathscale InfiniPath driver"); + +const int ib_ipath_state_ops[IB_QPS_ERR + 1] = { + [IB_QPS_RESET] = 0, + [IB_QPS_INIT] = IPATH_POST_RECV_OK, + [IB_QPS_RTR] = IPATH_POST_RECV_OK | IPATH_PROCESS_RECV_OK, + [IB_QPS_RTS] = IPATH_POST_RECV_OK | IPATH_PROCESS_RECV_OK | + IPATH_POST_SEND_OK | IPATH_PROCESS_SEND_OK, + [IB_QPS_SQD] = IPATH_POST_RECV_OK | IPATH_PROCESS_RECV_OK | + IPATH_POST_SEND_OK, + [IB_QPS_SQE] = IPATH_POST_RECV_OK | IPATH_PROCESS_RECV_OK, + [IB_QPS_ERR] = 0, +}; + +/* + * Translate ib_wr_opcode into ib_wc_opcode. + */ +const enum ib_wc_opcode ib_ipath_wc_opcode[] = { + [IB_WR_RDMA_WRITE] = IB_WC_RDMA_WRITE, + [IB_WR_RDMA_WRITE_WITH_IMM] = IB_WC_RDMA_WRITE, + [IB_WR_SEND] = IB_WC_SEND, + [IB_WR_SEND_WITH_IMM] = IB_WC_SEND, + [IB_WR_RDMA_READ] = IB_WC_RDMA_READ, + [IB_WR_ATOMIC_CMP_AND_SWP] = IB_WC_COMP_SWAP, + [IB_WR_ATOMIC_FETCH_AND_ADD] = IB_WC_FETCH_ADD +}; + +/* + * System image GUID. + */ +__be64 sys_image_guid; + +/** + * ipath_copy_sge - copy data to SGE memory + * @ss: the SGE state + * @data: the data to copy + * @length: the length of the data + */ +void ipath_copy_sge(struct ipath_sge_state *ss, void *data, u32 length) +{ + struct ipath_sge *sge = &ss->sge; + + while (length) { + u32 len = sge->length; + + BUG_ON(len == 0); + if (len > length) + len = length; + memcpy(sge->vaddr, data, len); + sge->vaddr += len; + sge->length -= len; + sge->sge_length -= len; + if (sge->sge_length == 0) { + if (--ss->num_sge) + *sge = *ss->sg_list++; + } else if (sge->length == 0 && sge->mr != NULL) { + if (++sge->n >= IPATH_SEGSZ) { + if (++sge->m >= sge->mr->mapsz) + break; + sge->n = 0; + } + sge->vaddr = + sge->mr->map[sge->m]->segs[sge->n].vaddr; + sge->length = + sge->mr->map[sge->m]->segs[sge->n].length; + } + data += len; + length -= len; + } +} + +/** + * ipath_skip_sge - skip over SGE memory - XXX almost dup of prev func + * @ss: the SGE state + * @length: the number of bytes to skip + */ +void ipath_skip_sge(struct ipath_sge_state *ss, u32 length) +{ + struct ipath_sge *sge = &ss->sge; + + while (length > sge->sge_length) { + length -= sge->sge_length; + ss->sge = *ss->sg_list++; + } + while (length) { + u32 len = sge->length; + + BUG_ON(len == 0); + if (len > length) + len = length; + sge->vaddr += len; + sge->length -= len; + sge->sge_length -= len; + if (sge->sge_length == 0) { + if (--ss->num_sge) + *sge = *ss->sg_list++; + } else if (sge->length == 0 && sge->mr != NULL) { + if (++sge->n >= IPATH_SEGSZ) { + if (++sge->m >= sge->mr->mapsz) + break; + sge->n = 0; + } + sge->vaddr = + sge->mr->map[sge->m]->segs[sge->n].vaddr; + sge->length = + sge->mr->map[sge->m]->segs[sge->n].length; + } + length -= len; + } +} + +/** + * ipath_post_send - post a send on a QP + * @ibqp: the QP to post the send on + * @wr: the list of work requests to post + * @bad_wr: the first bad WR is put here + * + * This may be called from interrupt context. + */ +static int ipath_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, + struct ib_send_wr **bad_wr) +{ + struct ipath_qp *qp = to_iqp(ibqp); + int err = 0; + + /* Check that state is OK to post send. */ + if (!(ib_ipath_state_ops[qp->state] & IPATH_POST_SEND_OK)) { + *bad_wr = wr; + err = -EINVAL; + goto bail; + } + + for (; wr; wr = wr->next) { + switch (qp->ibqp.qp_type) { + case IB_QPT_UC: + case IB_QPT_RC: + err = ipath_post_rc_send(qp, wr); + break; + + case IB_QPT_SMI: + case IB_QPT_GSI: + case IB_QPT_UD: + err = ipath_post_ud_send(qp, wr); + break; + + default: + err = -EINVAL; + } + if (err) { + *bad_wr = wr; + break; + } + } + +bail: + return err; +} + +/** + * ipath_post_receive - post a receive on a QP + * @ibqp: the QP to post the receive on + * @wr: the WR to post + * @bad_wr: the first bad WR is put here + * + * This may be called from interrupt context. + */ +static int ipath_post_receive(struct ib_qp *ibqp, struct ib_recv_wr *wr, + struct ib_recv_wr **bad_wr) +{ + struct ipath_qp *qp = to_iqp(ibqp); + unsigned long flags; + int ret; + + /* Check that state is OK to post receive. */ + if (!(ib_ipath_state_ops[qp->state] & IPATH_POST_RECV_OK)) { + *bad_wr = wr; + ret = -EINVAL; + goto bail; + } + + for (; wr; wr = wr->next) { + struct ipath_rwqe *wqe; + u32 next; + int i, j; + + if (wr->num_sge > qp->r_rq.max_sge) { + *bad_wr = wr; + ret = -ENOMEM; + goto bail; + } + + spin_lock_irqsave(&qp->r_rq.lock, flags); + next = qp->r_rq.head + 1; + if (next >= qp->r_rq.size) + next = 0; + if (next == qp->r_rq.tail) { + spin_unlock_irqrestore(&qp->r_rq.lock, flags); + *bad_wr = wr; + ret = -ENOMEM; + goto bail; + } + + wqe = get_rwqe_ptr(&qp->r_rq, qp->r_rq.head); + wqe->wr_id = wr->wr_id; + wqe->sg_list[0].mr = NULL; + wqe->sg_list[0].vaddr = NULL; + wqe->sg_list[0].length = 0; + wqe->sg_list[0].sge_length = 0; + wqe->length = 0; + for (i = 0, j = 0; i < wr->num_sge; i++) { + /* Check LKEY */ + if (to_ipd(qp->ibqp.pd)->user && + wr->sg_list[i].lkey == 0) { + spin_unlock_irqrestore(&qp->r_rq.lock, + flags); + *bad_wr = wr; + ret = -EINVAL; + goto bail; + } + if (wr->sg_list[i].length == 0) + continue; + if (!ipath_lkey_ok( + &to_idev(qp->ibqp.device)->lk_table, + &wqe->sg_list[j], &wr->sg_list[i], + IB_ACCESS_LOCAL_WRITE)) { + spin_unlock_irqrestore(&qp->r_rq.lock, + flags); + *bad_wr = wr; + ret = -EINVAL; + goto bail; + } + wqe->length += wr->sg_list[i].length; + j++; + } + wqe->num_sge = j; + qp->r_rq.head = next; + spin_unlock_irqrestore(&qp->r_rq.lock, flags); + } + ret = 0; + +bail: + return ret; +} + +/** + * ipath_qp_rcv - processing an incoming packet on a QP + * @dev: the device the packet came on + * @hdr: the packet header + * @has_grh: true if the packet has a GRH + * @data: the packet data + * @tlen: the packet length + * @qp: the QP the packet came on + * + * This is called from ipath_ib_rcv() to process an incoming packet + * for the given QP. + * Called at interrupt level. + */ +static void ipath_qp_rcv(struct ipath_ibdev *dev, + struct ipath_ib_header *hdr, int has_grh, + void *data, u32 tlen, struct ipath_qp *qp) +{ + /* Check for valid receive state. */ + if (!(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_RECV_OK)) { + dev->n_pkt_drops++; + return; + } + + switch (qp->ibqp.qp_type) { + case IB_QPT_SMI: + case IB_QPT_GSI: + case IB_QPT_UD: + ipath_ud_rcv(dev, hdr, has_grh, data, tlen, qp); + break; + + case IB_QPT_RC: + ipath_rc_rcv(dev, hdr, has_grh, data, tlen, qp); + break; + + case IB_QPT_UC: + ipath_uc_rcv(dev, hdr, has_grh, data, tlen, qp); + break; + + default: + break; + } +} + +/** + * ipath_ib_rcv - process and incoming packet + * @arg: the device pointer + * @rhdr: the header of the packet + * @data: the packet data + * @tlen: the packet length + * + * This is called from ipath_kreceive() to process an incoming packet at + * interrupt level. Tlen is the length of the header + data + CRC in bytes. + */ +static void ipath_ib_rcv(void *arg, void *rhdr, void *data, u32 tlen) +{ + struct ipath_ibdev *dev = (struct ipath_ibdev *) arg; + struct ipath_ib_header *hdr = rhdr; + struct ipath_other_headers *ohdr; + struct ipath_qp *qp; + u32 qp_num; + int lnh; + u8 opcode; + u16 lid; + + if (unlikely(dev == NULL)) + goto bail; + + if (unlikely(tlen < 24)) { /* LRH+BTH+CRC */ + dev->rcv_errors++; + goto bail; + } + + /* Check for a valid destination LID (see ch. 7.11.1). */ + lid = be16_to_cpu(hdr->lrh[1]); + if (lid < IPS_MULTICAST_LID_BASE) { + lid &= ~((1 << (dev->mkeyprot_resv_lmc & 7)) - 1); + if (unlikely(lid != ipath_layer_get_lid(dev->dd))) { + dev->rcv_errors++; + goto bail; + } + } + + /* Check for GRH */ + lnh = be16_to_cpu(hdr->lrh[0]) & 3; + if (lnh == IPS_LRH_BTH) + ohdr = &hdr->u.oth; + else if (lnh == IPS_LRH_GRH) + ohdr = &hdr->u.l.oth; + else { + dev->rcv_errors++; + goto bail; + } + + opcode = be32_to_cpu(ohdr->bth[0]) >> 24; + dev->opstats[opcode].n_bytes += tlen; + dev->opstats[opcode].n_packets++; + + /* Get the destination QP number. */ + qp_num = be32_to_cpu(ohdr->bth[1]) & IPS_QPN_MASK; + if (qp_num == IPS_MULTICAST_QPN) { + struct ipath_mcast *mcast; + struct ipath_mcast_qp *p; + + mcast = ipath_mcast_find(&hdr->u.l.grh.dgid); + if (mcast == NULL) { + dev->n_pkt_drops++; + goto bail; + } + dev->n_multicast_rcv++; + list_for_each_entry_rcu(p, &mcast->qp_list, list) + ipath_qp_rcv(dev, hdr, lnh == IPS_LRH_GRH, data, + tlen, p->qp); + /* + * Notify ipath_multicast_detach() if it is waiting for us + * to finish. + */ + if (atomic_dec_return(&mcast->refcount) <= 1) + wake_up(&mcast->wait); + } else { + qp = ipath_lookup_qpn(&dev->qp_table, qp_num); + if (qp) { + dev->n_unicast_rcv++; + ipath_qp_rcv(dev, hdr, lnh == IPS_LRH_GRH, data, + tlen, qp); + /* + * Notify ipath_destroy_qp() if it is waiting + * for us to finish. + */ + if (atomic_dec_and_test(&qp->refcount)) + wake_up(&qp->wait); + } else + dev->n_pkt_drops++; + } + +bail:; +} + +/** + * ipath_ib_timer - verbs timer + * @arg: the device pointer + * + * This is called from ipath_do_rcv_timer() at interrupt level to check for + * QPs which need retransmits and to collect performance numbers. + */ +static void ipath_ib_timer(void *arg) +{ + struct ipath_ibdev *dev = (struct ipath_ibdev *) arg; + struct ipath_qp *resend = NULL; + struct ipath_qp *rnr = NULL; + struct list_head *last; + struct ipath_qp *qp; + unsigned long flags; + + if (dev == NULL) + return; + + spin_lock_irqsave(&dev->pending_lock, flags); + /* Start filling the next pending queue. */ + if (++dev->pending_index >= ARRAY_SIZE(dev->pending)) + dev->pending_index = 0; + /* Save any requests still in the new queue, they have timed out. */ + last = &dev->pending[dev->pending_index]; + while (!list_empty(last)) { + qp = list_entry(last->next, struct ipath_qp, timerwait); + if (last->next == LIST_POISON1 || + last->next != &qp->timerwait || + qp->timerwait.prev != last) { + INIT_LIST_HEAD(last); + } else { + list_del(&qp->timerwait); + qp->timerwait.prev = (struct list_head *) resend; + resend = qp; + atomic_inc(&qp->refcount); + } + } + last = &dev->rnrwait; + if (!list_empty(last)) { + qp = list_entry(last->next, struct ipath_qp, timerwait); + if (--qp->s_rnr_timeout == 0) { + do { + if (last->next == LIST_POISON1 || + last->next != &qp->timerwait || + qp->timerwait.prev != last) { + INIT_LIST_HEAD(last); + break; + } + list_del(&qp->timerwait); + qp->timerwait.prev = + (struct list_head *) rnr; + rnr = qp; + if (list_empty(last)) + break; + qp = list_entry(last->next, struct ipath_qp, + timerwait); + } while (qp->s_rnr_timeout == 0); + } + } + /* + * We should only be in the started state if pma_sample_start != 0 + */ + if (dev->pma_sample_status == IB_PMA_SAMPLE_STATUS_STARTED && + --dev->pma_sample_start == 0) { + dev->pma_sample_status = IB_PMA_SAMPLE_STATUS_RUNNING; + ipath_layer_snapshot_counters(dev->dd, &dev->ipath_sword, + &dev->ipath_rword, + &dev->ipath_spkts, + &dev->ipath_rpkts, + &dev->ipath_xmit_wait); + } + if (dev->pma_sample_status == IB_PMA_SAMPLE_STATUS_RUNNING) { + if (dev->pma_sample_interval == 0) { + u64 ta, tb, tc, td, te; + + dev->pma_sample_status = IB_PMA_SAMPLE_STATUS_DONE; + ipath_layer_snapshot_counters(dev->dd, &ta, &tb, + &tc, &td, &te); + + dev->ipath_sword = ta - dev->ipath_sword; + dev->ipath_rword = tb - dev->ipath_rword; + dev->ipath_spkts = tc - dev->ipath_spkts; + dev->ipath_rpkts = td - dev->ipath_rpkts; + dev->ipath_xmit_wait = te - dev->ipath_xmit_wait; + } + else + dev->pma_sample_interval--; + } + spin_unlock_irqrestore(&dev->pending_lock, flags); + + /* XXX What if timer fires again while this is running? */ + for (qp = resend; qp != NULL; + qp = (struct ipath_qp *) qp->timerwait.prev) { + struct ib_wc wc; + + spin_lock_irqsave(&qp->s_lock, flags); + if (qp->s_last != qp->s_tail && qp->state == IB_QPS_RTS) { + dev->n_timeouts++; + ipath_restart_rc(qp, qp->s_last_psn + 1, &wc); + } + spin_unlock_irqrestore(&qp->s_lock, flags); + + /* Notify ipath_destroy_qp() if it is waiting. */ + if (atomic_dec_and_test(&qp->refcount)) + wake_up(&qp->wait); + } + for (qp = rnr; qp != NULL; + qp = (struct ipath_qp *) qp->timerwait.prev) + tasklet_hi_schedule(&qp->s_task); +} + +/** + * ipath_ib_piobufavail - callback when a PIO buffer is available + * @arg: the device pointer + * + * This is called from ipath_intr() at interrupt level when a PIO buffer is + * available after ipath_verbs_send() returned an error that no buffers were + * available. Return 0 if we consumed all the PIO buffers and we still have + * QPs waiting for buffers (for now, just do a tasklet_hi_schedule and + * return one). + */ +static int ipath_ib_piobufavail(void *arg) +{ + struct ipath_ibdev *dev = (struct ipath_ibdev *) arg; + struct ipath_qp *qp; + unsigned long flags; + + if (dev == NULL) + goto bail; + + spin_lock_irqsave(&dev->pending_lock, flags); + while (!list_empty(&dev->piowait)) { + qp = list_entry(dev->piowait.next, struct ipath_qp, + piowait); + list_del(&qp->piowait); + tasklet_hi_schedule(&qp->s_task); + } + spin_unlock_irqrestore(&dev->pending_lock, flags); + +bail: + return 1; +} + +static int ipath_query_device(struct ib_device *ibdev, + struct ib_device_attr *props) +{ + struct ipath_ibdev *dev = to_idev(ibdev); + u32 vendor, boardrev, majrev, minrev; + + memset(props, 0, sizeof(*props)); + + props->device_cap_flags = IB_DEVICE_BAD_PKEY_CNTR | + IB_DEVICE_BAD_QKEY_CNTR | IB_DEVICE_SHUTDOWN_PORT | + IB_DEVICE_SYS_IMAGE_GUID; + ipath_layer_query_device(dev->dd, &vendor, &boardrev, + &majrev, &minrev); + props->vendor_id = vendor; + props->vendor_part_id = boardrev; + props->hw_ver = boardrev << 16 | majrev << 8 | minrev; + + props->sys_image_guid = dev->sys_image_guid; + + props->max_mr_size = ~0ull; + props->max_qp = 0xffff; + props->max_qp_wr = 0xffff; + props->max_sge = 255; + props->max_cq = 0xffff; + props->max_cqe = 0xffff; + props->max_mr = 0xffff; + props->max_pd = 0xffff; + props->max_qp_rd_atom = 1; + props->max_qp_init_rd_atom = 1; + /* props->max_res_rd_atom */ + props->max_srq = 0xffff; + props->max_srq_wr = 0xffff; + props->max_srq_sge = 255; + /* props->local_ca_ack_delay */ + props->atomic_cap = IB_ATOMIC_HCA; + props->max_pkeys = ipath_layer_get_npkeys(dev->dd); + props->max_mcast_grp = 0xffff; + props->max_mcast_qp_attach = 0xffff; + props->max_total_mcast_qp_attach = props->max_mcast_qp_attach * + props->max_mcast_grp; + + return 0; +} + +const u8 ipath_cvt_physportstate[16] = { + [INFINIPATH_IBCS_LT_STATE_DISABLED] = 3, + [INFINIPATH_IBCS_LT_STATE_LINKUP] = 5, + [INFINIPATH_IBCS_LT_STATE_POLLACTIVE] = 2, + [INFINIPATH_IBCS_LT_STATE_POLLQUIET] = 2, + [INFINIPATH_IBCS_LT_STATE_SLEEPDELAY] = 1, + [INFINIPATH_IBCS_LT_STATE_SLEEPQUIET] = 1, + [INFINIPATH_IBCS_LT_STATE_CFGDEBOUNCE] = 4, + [INFINIPATH_IBCS_LT_STATE_CFGRCVFCFG] = 4, + [INFINIPATH_IBCS_LT_STATE_CFGWAITRMT] = 4, + [INFINIPATH_IBCS_LT_STATE_CFGIDLE] = 4, + [INFINIPATH_IBCS_LT_STATE_RECOVERRETRAIN] = 6, + [INFINIPATH_IBCS_LT_STATE_RECOVERWAITRMT] = 6, + [INFINIPATH_IBCS_LT_STATE_RECOVERIDLE] = 6, +}; + +static int ipath_query_port(struct ib_device *ibdev, + u8 port, struct ib_port_attr *props) +{ + struct ipath_ibdev *dev = to_idev(ibdev); + enum ib_mtu mtu; + u16 lid = ipath_layer_get_lid(dev->dd); + u64 ibcstat; + + memset(props, 0, sizeof(*props)); + props->lid = lid ? lid : __constant_be16_to_cpu(IB_LID_PERMISSIVE); + props->lmc = dev->mkeyprot_resv_lmc & 7; + props->sm_lid = dev->sm_lid; + props->sm_sl = dev->sm_sl; + ibcstat = ipath_layer_get_lastibcstat(dev->dd); + props->state = ((ibcstat >> 4) & 0x3) + 1; + /* See phys_state_show() */ + props->phys_state = ipath_cvt_physportstate[ + ipath_layer_get_lastibcstat(dev->dd) & 0xf]; + props->port_cap_flags = dev->port_cap_flags; + props->gid_tbl_len = 1; + props->max_msg_sz = 4096; + props->pkey_tbl_len = ipath_layer_get_npkeys(dev->dd); + props->bad_pkey_cntr = ipath_layer_get_cr_errpkey(dev->dd) - + dev->n_pkey_violations; + props->qkey_viol_cntr = dev->qkey_violations; + props->active_width = IB_WIDTH_4X; + /* See rate_show() */ + props->active_speed = 1; /* Regular 10Mbs speed. */ + props->max_vl_num = 1; /* VLCap = VL0 */ + props->init_type_reply = 0; + + props->max_mtu = IB_MTU_4096; + switch (ipath_layer_get_ibmtu(dev->dd)) { + case 4096: + mtu = IB_MTU_4096; + break; + case 2048: + mtu = IB_MTU_2048; + break; + case 1024: + mtu = IB_MTU_1024; + break; + case 512: + mtu = IB_MTU_512; + break; + case 256: + mtu = IB_MTU_256; + break; + default: + mtu = IB_MTU_2048; + } + props->active_mtu = mtu; + props->subnet_timeout = dev->subnet_timeout; + + return 0; +} + +static int ipath_modify_device(struct ib_device *device, + int device_modify_mask, + struct ib_device_modify *device_modify) +{ + int ret; + + if (device_modify_mask & ~(IB_DEVICE_MODIFY_SYS_IMAGE_GUID | + IB_DEVICE_MODIFY_NODE_DESC)) { + ret = -EOPNOTSUPP; + goto bail; + } + + if (device_modify_mask & IB_DEVICE_MODIFY_NODE_DESC) + memcpy(device->node_desc, device_modify->node_desc, 64); + + if (device_modify_mask & IB_DEVICE_MODIFY_SYS_IMAGE_GUID) + to_idev(device)->sys_image_guid = + cpu_to_be64(device_modify->sys_image_guid); + + ret = 0; + +bail: + return ret; +} + +static int ipath_modify_port(struct ib_device *ibdev, + u8 port, int port_modify_mask, + struct ib_port_modify *props) +{ + struct ipath_ibdev *dev = to_idev(ibdev); + + dev->port_cap_flags |= props->set_port_cap_mask; + dev->port_cap_flags &= ~props->clr_port_cap_mask; + if (port_modify_mask & IB_PORT_SHUTDOWN) + ipath_layer_set_linkstate(dev->dd, IPATH_IB_LINKDOWN); + if (port_modify_mask & IB_PORT_RESET_QKEY_CNTR) + dev->qkey_violations = 0; + return 0; +} + +static int ipath_query_gid(struct ib_device *ibdev, u8 port, + int index, union ib_gid *gid) +{ + struct ipath_ibdev *dev = to_idev(ibdev); + int ret; + + if (index >= 1) { + ret = -EINVAL; + goto bail; + } + gid->global.subnet_prefix = dev->gid_prefix; + gid->global.interface_id = ipath_layer_get_guid(dev->dd); + + ret = 0; + +bail: + return ret; +} + +static struct ib_pd *ipath_alloc_pd(struct ib_device *ibdev, + struct ib_ucontext *context, + struct ib_udata *udata) +{ + struct ipath_pd *pd; + struct ib_pd *ret; + + pd = kmalloc(sizeof *pd, GFP_KERNEL); + if (!pd) { + ret = ERR_PTR(-ENOMEM); + goto bail; + } + + /* ib_alloc_pd() will initialize pd->ibpd. */ + pd->user = udata != NULL; + + ret = &pd->ibpd; + +bail: + return ret; +} + +static int ipath_dealloc_pd(struct ib_pd *ibpd) +{ + struct ipath_pd *pd = to_ipd(ibpd); + + kfree(pd); + + return 0; +} + +/** + * ipath_create_ah - create an address handle + * @pd: the protection domain + * @ah_attr: the attributes of the AH + * + * This may be called from interrupt context. + */ +static struct ib_ah *ipath_create_ah(struct ib_pd *pd, + struct ib_ah_attr *ah_attr) +{ + struct ipath_ah *ah; + struct ib_ah *ret; + + /* A multicast address requires a GRH (see ch. 8.4.1). */ + if (ah_attr->dlid >= IPS_MULTICAST_LID_BASE && + ah_attr->dlid != IPS_PERMISSIVE_LID && + !(ah_attr->ah_flags & IB_AH_GRH)) { + ret = ERR_PTR(-EINVAL); + goto bail; + } + + ah = kmalloc(sizeof *ah, GFP_ATOMIC); + if (!ah) { + ret = ERR_PTR(-ENOMEM); + goto bail; + } + + /* ib_create_ah() will initialize ah->ibah. */ + ah->attr = *ah_attr; + + ret = &ah->ibah; + +bail: + return ret; +} + +/** + * ipath_destroy_ah - destroy an address handle + * @ibah: the AH to destroy + * + * This may be called from interrupt context. + */ +static int ipath_destroy_ah(struct ib_ah *ibah) +{ + struct ipath_ah *ah = to_iah(ibah); + + kfree(ah); + + return 0; +} + +static int ipath_query_ah(struct ib_ah *ibah, struct ib_ah_attr *ah_attr) +{ + struct ipath_ah *ah = to_iah(ibah); + + *ah_attr = ah->attr; + + return 0; +} + +static int ipath_query_pkey(struct ib_device *ibdev, u8 port, u16 index, + u16 *pkey) +{ + struct ipath_ibdev *dev = to_idev(ibdev); + int ret; + + if (index >= ipath_layer_get_npkeys(dev->dd)) { + ret = -EINVAL; + goto bail; + } + + *pkey = ipath_layer_get_pkey(dev->dd, index); + ret = 0; + +bail: + return ret; +} + + +/** + * ipath_alloc_ucontext - allocate a ucontest + * @ibdev: the infiniband device + * @udata: not used by the InfiniPath driver + */ + +static struct ib_ucontext *ipath_alloc_ucontext(struct ib_device *ibdev, + struct ib_udata *udata) +{ + struct ipath_ucontext *context; + struct ib_ucontext *ret; + + context = kmalloc(sizeof *context, GFP_KERNEL); + if (!context) { + ret = ERR_PTR(-ENOMEM); + goto bail; + } + + ret = &context->ibucontext; + +bail: + return ret; +} + +static int ipath_dealloc_ucontext(struct ib_ucontext *context) +{ + kfree(to_iucontext(context)); + return 0; +} + +static int ipath_verbs_register_sysfs(struct ib_device *dev); + +/** + * ipath_register_ib_device - register our device with the infiniband core + * @unit: the device number to register + * @dd: the device data structure + * Return the allocated ipath_ibdev pointer or NULL on error. + */ +static void *ipath_register_ib_device(int unit, struct ipath_devdata *dd) +{ + struct ipath_ibdev *idev; + struct ib_device *dev; + int ret; + + idev = (struct ipath_ibdev *)ib_alloc_device(sizeof *idev); + if (idev == NULL) + goto bail; + + dev = &idev->ibdev; + + /* Only need to initialize non-zero fields. */ + spin_lock_init(&idev->qp_table.lock); + spin_lock_init(&idev->lk_table.lock); + idev->sm_lid = __constant_be16_to_cpu(IB_LID_PERMISSIVE); + /* Set the prefix to the default value (see ch. 4.1.1) */ + idev->gid_prefix = __constant_cpu_to_be64(0xfe80000000000000ULL); + + ret = ipath_init_qp_table(idev, ib_ipath_qp_table_size); + if (ret) + goto err_qp; + + /* + * The top ib_ipath_lkey_table_size bits are used to index the + * table. The lower 8 bits can be owned by the user (copied from + * the LKEY). The remaining bits act as a generation number or tag. + */ + idev->lk_table.max = 1 << ib_ipath_lkey_table_size; + idev->lk_table.table = kzalloc(idev->lk_table.max * + sizeof(*idev->lk_table.table), + GFP_KERNEL); + if (idev->lk_table.table == NULL) { + ret = -ENOMEM; + goto err_lk; + } + spin_lock_init(&idev->pending_lock); + INIT_LIST_HEAD(&idev->pending[0]); + INIT_LIST_HEAD(&idev->pending[1]); + INIT_LIST_HEAD(&idev->pending[2]); + INIT_LIST_HEAD(&idev->piowait); + INIT_LIST_HEAD(&idev->rnrwait); + idev->pending_index = 0; + idev->port_cap_flags = + IB_PORT_SYS_IMAGE_GUID_SUP | IB_PORT_CLIENT_REG_SUP; + idev->pma_counter_select[0] = IB_PMA_PORT_XMIT_DATA; + idev->pma_counter_select[1] = IB_PMA_PORT_RCV_DATA; + idev->pma_counter_select[2] = IB_PMA_PORT_XMIT_PKTS; + idev->pma_counter_select[3] = IB_PMA_PORT_RCV_PKTS; + idev->pma_counter_select[5] = IB_PMA_PORT_XMIT_WAIT; + idev->link_width_enabled = 3; /* 1x or 4x */ + + /* + * The system image GUID is supposed to be the same for all + * IB HCAs in a single system but since there can be other + * device types in the system, we can't be sure this is unique. + */ + if (!sys_image_guid) + sys_image_guid = ipath_layer_get_guid(dd); + idev->sys_image_guid = sys_image_guid; + idev->ib_unit = unit; + idev->dd = dd; + + strlcpy(dev->name, "ipath%d", IB_DEVICE_NAME_MAX); + dev->node_guid = ipath_layer_get_guid(dd); + dev->uverbs_abi_ver = IPATH_UVERBS_ABI_VERSION; + dev->uverbs_cmd_mask = + (1ull << IB_USER_VERBS_CMD_GET_CONTEXT) | + (1ull << IB_USER_VERBS_CMD_QUERY_DEVICE) | + (1ull << IB_USER_VERBS_CMD_QUERY_PORT) | + (1ull << IB_USER_VERBS_CMD_ALLOC_PD) | + (1ull << IB_USER_VERBS_CMD_DEALLOC_PD) | + (1ull << IB_USER_VERBS_CMD_CREATE_AH) | + (1ull << IB_USER_VERBS_CMD_DESTROY_AH) | + (1ull << IB_USER_VERBS_CMD_QUERY_AH) | + (1ull << IB_USER_VERBS_CMD_REG_MR) | + (1ull << IB_USER_VERBS_CMD_DEREG_MR) | + (1ull << IB_USER_VERBS_CMD_CREATE_COMP_CHANNEL) | + (1ull << IB_USER_VERBS_CMD_CREATE_CQ) | + (1ull << IB_USER_VERBS_CMD_RESIZE_CQ) | + (1ull << IB_USER_VERBS_CMD_DESTROY_CQ) | + (1ull << IB_USER_VERBS_CMD_POLL_CQ) | + (1ull << IB_USER_VERBS_CMD_REQ_NOTIFY_CQ) | + (1ull << IB_USER_VERBS_CMD_CREATE_QP) | + (1ull << IB_USER_VERBS_CMD_QUERY_QP) | + (1ull << IB_USER_VERBS_CMD_MODIFY_QP) | + (1ull << IB_USER_VERBS_CMD_DESTROY_QP) | + (1ull << IB_USER_VERBS_CMD_POST_SEND) | + (1ull << IB_USER_VERBS_CMD_POST_RECV) | + (1ull << IB_USER_VERBS_CMD_ATTACH_MCAST) | + (1ull << IB_USER_VERBS_CMD_DETACH_MCAST) | + (1ull << IB_USER_VERBS_CMD_CREATE_SRQ) | + (1ull << IB_USER_VERBS_CMD_MODIFY_SRQ) | + (1ull << IB_USER_VERBS_CMD_QUERY_SRQ) | + (1ull << IB_USER_VERBS_CMD_DESTROY_SRQ) | + (1ull << IB_USER_VERBS_CMD_POST_SRQ_RECV); + dev->node_type = IB_NODE_CA; + dev->phys_port_cnt = 1; + dev->dma_device = ipath_layer_get_device(dd); + dev->class_dev.dev = dev->dma_device; + dev->query_device = ipath_query_device; + dev->modify_device = ipath_modify_device; + dev->query_port = ipath_query_port; + dev->modify_port = ipath_modify_port; + dev->query_pkey = ipath_query_pkey; + dev->query_gid = ipath_query_gid; + dev->alloc_ucontext = ipath_alloc_ucontext; + dev->dealloc_ucontext = ipath_dealloc_ucontext; + dev->alloc_pd = ipath_alloc_pd; + dev->dealloc_pd = ipath_dealloc_pd; + dev->create_ah = ipath_create_ah; + dev->destroy_ah = ipath_destroy_ah; + dev->query_ah = ipath_query_ah; + dev->create_srq = ipath_create_srq; + dev->modify_srq = ipath_modify_srq; + dev->query_srq = ipath_query_srq; + dev->destroy_srq = ipath_destroy_srq; + dev->create_qp = ipath_create_qp; + dev->modify_qp = ipath_modify_qp; + dev->query_qp = ipath_query_qp; + dev->destroy_qp = ipath_destroy_qp; + dev->post_send = ipath_post_send; + dev->post_recv = ipath_post_receive; + dev->post_srq_recv = ipath_post_srq_receive; + dev->create_cq = ipath_create_cq; + dev->destroy_cq = ipath_destroy_cq; + dev->resize_cq = ipath_resize_cq; + dev->poll_cq = ipath_poll_cq; + dev->req_notify_cq = ipath_req_notify_cq; + dev->get_dma_mr = ipath_get_dma_mr; + dev->reg_phys_mr = ipath_reg_phys_mr; + dev->reg_user_mr = ipath_reg_user_mr; + dev->dereg_mr = ipath_dereg_mr; + dev->alloc_fmr = ipath_alloc_fmr; + dev->map_phys_fmr = ipath_map_phys_fmr; + dev->unmap_fmr = ipath_unmap_fmr; + dev->dealloc_fmr = ipath_dealloc_fmr; + dev->attach_mcast = ipath_multicast_attach; + dev->detach_mcast = ipath_multicast_detach; + dev->process_mad = ipath_process_mad; + + snprintf(dev->node_desc, sizeof(dev->node_desc), + IPATH_IDSTR " %s kernel_SMA", system_utsname.nodename); + + ret = ib_register_device(dev); + if (ret) + goto err_reg; + + if (ipath_verbs_register_sysfs(dev)) + goto err_class; + + ipath_layer_enable_timer(dd); + + goto bail; + +err_class: + ib_unregister_device(dev); +err_reg: + kfree(idev->lk_table.table); +err_lk: + kfree(idev->qp_table.table); +err_qp: + ib_dealloc_device(dev); + _VERBS_ERROR("ib_ipath%d cannot register verbs (%d)!\n", + unit, -ret); + idev = NULL; + +bail: + return idev; +} + +static void ipath_unregister_ib_device(void *arg) +{ + struct ipath_ibdev *dev = (struct ipath_ibdev *) arg; + struct ib_device *ibdev = &dev->ibdev; + + ipath_layer_disable_timer(dev->dd); + + ib_unregister_device(ibdev); + + if (!list_empty(&dev->pending[0]) || + !list_empty(&dev->pending[1]) || + !list_empty(&dev->pending[2])) + _VERBS_ERROR("ipath%d pending list not empty!\n", + dev->ib_unit); + if (!list_empty(&dev->piowait)) + _VERBS_ERROR("ipath%d piowait list not empty!\n", + dev->ib_unit); + if (!list_empty(&dev->rnrwait)) + _VERBS_ERROR("ipath%d rnrwait list not empty!\n", + dev->ib_unit); + if (!ipath_mcast_tree_empty()) + _VERBS_ERROR("ipath%d multicast table memory leak!\n", + dev->ib_unit); + /* + * Note that ipath_unregister_ib_device() can be called before all + * the QPs are destroyed! + */ + ipath_free_all_qps(&dev->qp_table); + kfree(dev->qp_table.table); + kfree(dev->lk_table.table); + ib_dealloc_device(ibdev); +} + +int __init ipath_verbs_init(void) +{ + return ipath_verbs_register(ipath_register_ib_device, + ipath_unregister_ib_device, + ipath_ib_piobufavail, ipath_ib_rcv, + ipath_ib_timer); +} + +void __exit ipath_verbs_cleanup(void) +{ + ipath_verbs_unregister(); +} + +static ssize_t show_rev(struct class_device *cdev, char *buf) +{ + struct ipath_ibdev *dev = + container_of(cdev, struct ipath_ibdev, ibdev.class_dev); + int vendor, boardrev, majrev, minrev; + + ipath_layer_query_device(dev->dd, &vendor, &boardrev, + &majrev, &minrev); + return sprintf(buf, "%d.%d\n", majrev, minrev); +} + +static ssize_t show_hca(struct class_device *cdev, char *buf) +{ + struct ipath_ibdev *dev = + container_of(cdev, struct ipath_ibdev, ibdev.class_dev); + int ret; + + ret = ipath_layer_get_boardname(dev->dd, buf, 128); + if (ret < 0) + goto bail; + strcat(buf, "\n"); + ret = strlen(buf); + +bail: + return ret; +} + +static ssize_t show_stats(struct class_device *cdev, char *buf) +{ + struct ipath_ibdev *dev = + container_of(cdev, struct ipath_ibdev, ibdev.class_dev); + int i; + int len; + + len = sprintf(buf, + "RC resends %d\n" + "RC QACKs %d\n" + "RC ACKs %d\n" + "RC SEQ NAKs %d\n" + "RC RDMA seq %d\n" + "RC RNR NAKs %d\n" + "RC OTH NAKs %d\n" + "RC timeouts %d\n" + "RC RDMA dup %d\n" + "piobuf wait %d\n" + "no piobuf %d\n" + "PKT drops %d\n" + "WQE errs %d\n", + dev->n_rc_resends, dev->n_rc_qacks, dev->n_rc_acks, + dev->n_seq_naks, dev->n_rdma_seq, dev->n_rnr_naks, + dev->n_other_naks, dev->n_timeouts, + dev->n_rdma_dup_busy, dev->n_piowait, + dev->n_no_piobuf, dev->n_pkt_drops, dev->n_wqe_errs); + for (i = 0; i < ARRAY_SIZE(dev->opstats); i++) { + const struct ipath_opcode_stats *si = &dev->opstats[i]; + + if (!si->n_packets && !si->n_bytes) + continue; + len += sprintf(buf + len, "%02x %llu/%llu\n", i, + (unsigned long long) si->n_packets, + (unsigned long long) si->n_bytes); + } + return len; +} + +static CLASS_DEVICE_ATTR(hw_rev, S_IRUGO, show_rev, NULL); +static CLASS_DEVICE_ATTR(hca_type, S_IRUGO, show_hca, NULL); +static CLASS_DEVICE_ATTR(board_id, S_IRUGO, show_hca, NULL); +static CLASS_DEVICE_ATTR(stats, S_IRUGO, show_stats, NULL); + +static struct class_device_attribute *ipath_class_attributes[] = { + &class_device_attr_hw_rev, + &class_device_attr_hca_type, + &class_device_attr_board_id, + &class_device_attr_stats +}; + +static int ipath_verbs_register_sysfs(struct ib_device *dev) +{ + int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(ipath_class_attributes); ++i) + if (class_device_create_file(&dev->class_dev, + ipath_class_attributes[i])) { + ret = 1; + goto bail; + } + + ret = 0; + +bail: + return ret; +} + +module_init(ipath_verbs_init); +module_exit(ipath_verbs_cleanup); diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.h b/drivers/infiniband/hw/ipath/ipath_verbs.h new file mode 100644 index 000000000000..b824632b2a8c --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_verbs.h @@ -0,0 +1,697 @@ +/* + * Copyright (c) 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef IPATH_VERBS_H +#define IPATH_VERBS_H + +#include +#include +#include +#include +#include + +#include "ipath_layer.h" +#include "verbs_debug.h" + +#define QPN_MAX (1 << 24) +#define QPNMAP_ENTRIES (QPN_MAX / PAGE_SIZE / BITS_PER_BYTE) + +/* + * Increment this value if any changes that break userspace ABI + * compatibility are made. + */ +#define IPATH_UVERBS_ABI_VERSION 1 + +/* + * Define an ib_cq_notify value that is not valid so we know when CQ + * notifications are armed. + */ +#define IB_CQ_NONE (IB_CQ_NEXT_COMP + 1) + +#define IB_RNR_NAK 0x20 +#define IB_NAK_PSN_ERROR 0x60 +#define IB_NAK_INVALID_REQUEST 0x61 +#define IB_NAK_REMOTE_ACCESS_ERROR 0x62 +#define IB_NAK_REMOTE_OPERATIONAL_ERROR 0x63 +#define IB_NAK_INVALID_RD_REQUEST 0x64 + +#define IPATH_POST_SEND_OK 0x01 +#define IPATH_POST_RECV_OK 0x02 +#define IPATH_PROCESS_RECV_OK 0x04 +#define IPATH_PROCESS_SEND_OK 0x08 + +/* IB Performance Manager status values */ +#define IB_PMA_SAMPLE_STATUS_DONE 0x00 +#define IB_PMA_SAMPLE_STATUS_STARTED 0x01 +#define IB_PMA_SAMPLE_STATUS_RUNNING 0x02 + +/* Mandatory IB performance counter select values. */ +#define IB_PMA_PORT_XMIT_DATA __constant_htons(0x0001) +#define IB_PMA_PORT_RCV_DATA __constant_htons(0x0002) +#define IB_PMA_PORT_XMIT_PKTS __constant_htons(0x0003) +#define IB_PMA_PORT_RCV_PKTS __constant_htons(0x0004) +#define IB_PMA_PORT_XMIT_WAIT __constant_htons(0x0005) + +struct ib_reth { + __be64 vaddr; + __be32 rkey; + __be32 length; +} __attribute__ ((packed)); + +struct ib_atomic_eth { + __be64 vaddr; + __be32 rkey; + __be64 swap_data; + __be64 compare_data; +} __attribute__ ((packed)); + +struct ipath_other_headers { + __be32 bth[3]; + union { + struct { + __be32 deth[2]; + __be32 imm_data; + } ud; + struct { + struct ib_reth reth; + __be32 imm_data; + } rc; + struct { + __be32 aeth; + __be64 atomic_ack_eth; + } at; + __be32 imm_data; + __be32 aeth; + struct ib_atomic_eth atomic_eth; + } u; +} __attribute__ ((packed)); + +/* + * Note that UD packets with a GRH header are 8+40+12+8 = 68 bytes + * long (72 w/ imm_data). Only the first 56 bytes of the IB header + * will be in the eager header buffer. The remaining 12 or 16 bytes + * are in the data buffer. + */ +struct ipath_ib_header { + __be16 lrh[4]; + union { + struct { + struct ib_grh grh; + struct ipath_other_headers oth; + } l; + struct ipath_other_headers oth; + } u; +} __attribute__ ((packed)); + +/* + * There is one struct ipath_mcast for each multicast GID. + * All attached QPs are then stored as a list of + * struct ipath_mcast_qp. + */ +struct ipath_mcast_qp { + struct list_head list; + struct ipath_qp *qp; +}; + +struct ipath_mcast { + struct rb_node rb_node; + union ib_gid mgid; + struct list_head qp_list; + wait_queue_head_t wait; + atomic_t refcount; +}; + +/* Memory region */ +struct ipath_mr { + struct ib_mr ibmr; + struct ipath_mregion mr; /* must be last */ +}; + +/* Fast memory region */ +struct ipath_fmr { + struct ib_fmr ibfmr; + u8 page_shift; + struct ipath_mregion mr; /* must be last */ +}; + +/* Protection domain */ +struct ipath_pd { + struct ib_pd ibpd; + int user; /* non-zero if created from user space */ +}; + +/* Address Handle */ +struct ipath_ah { + struct ib_ah ibah; + struct ib_ah_attr attr; +}; + +/* + * Quick description of our CQ/QP locking scheme: + * + * We have one global lock that protects dev->cq/qp_table. Each + * struct ipath_cq/qp also has its own lock. An individual qp lock + * may be taken inside of an individual cq lock. Both cqs attached to + * a qp may be locked, with the send cq locked first. No other + * nesting should be done. + * + * Each struct ipath_cq/qp also has an atomic_t ref count. The + * pointer from the cq/qp_table to the struct counts as one reference. + * This reference also is good for access through the consumer API, so + * modifying the CQ/QP etc doesn't need to take another reference. + * Access because of a completion being polled does need a reference. + * + * Finally, each struct ipath_cq/qp has a wait_queue_head_t for the + * destroy function to sleep on. + * + * This means that access from the consumer API requires nothing but + * taking the struct's lock. + * + * Access because of a completion event should go as follows: + * - lock cq/qp_table and look up struct + * - increment ref count in struct + * - drop cq/qp_table lock + * - lock struct, do your thing, and unlock struct + * - decrement ref count; if zero, wake up waiters + * + * To destroy a CQ/QP, we can do the following: + * - lock cq/qp_table, remove pointer, unlock cq/qp_table lock + * - decrement ref count + * - wait_event until ref count is zero + * + * It is the consumer's responsibilty to make sure that no QP + * operations (WQE posting or state modification) are pending when the + * QP is destroyed. Also, the consumer must make sure that calls to + * qp_modify are serialized. + * + * Possible optimizations (wait for profile data to see if/where we + * have locks bouncing between CPUs): + * - split cq/qp table lock into n separate (cache-aligned) locks, + * indexed (say) by the page in the table + */ + +struct ipath_cq { + struct ib_cq ibcq; + struct tasklet_struct comptask; + spinlock_t lock; + u8 notify; + u8 triggered; + u32 head; /* new records added to the head */ + u32 tail; /* poll_cq() reads from here. */ + struct ib_wc *queue; /* this is actually ibcq.cqe + 1 */ +}; + +/* + * Send work request queue entry. + * The size of the sg_list is determined when the QP is created and stored + * in qp->s_max_sge. + */ +struct ipath_swqe { + struct ib_send_wr wr; /* don't use wr.sg_list */ + u32 psn; /* first packet sequence number */ + u32 lpsn; /* last packet sequence number */ + u32 ssn; /* send sequence number */ + u32 length; /* total length of data in sg_list */ + struct ipath_sge sg_list[0]; +}; + +/* + * Receive work request queue entry. + * The size of the sg_list is determined when the QP is created and stored + * in qp->r_max_sge. + */ +struct ipath_rwqe { + u64 wr_id; + u32 length; /* total length of data in sg_list */ + u8 num_sge; + struct ipath_sge sg_list[0]; +}; + +struct ipath_rq { + spinlock_t lock; + u32 head; /* new work requests posted to the head */ + u32 tail; /* receives pull requests from here. */ + u32 size; /* size of RWQE array */ + u8 max_sge; + struct ipath_rwqe *wq; /* RWQE array */ +}; + +struct ipath_srq { + struct ib_srq ibsrq; + struct ipath_rq rq; + /* send signal when number of RWQEs < limit */ + u32 limit; +}; + +/* + * Variables prefixed with s_ are for the requester (sender). + * Variables prefixed with r_ are for the responder (receiver). + * Variables prefixed with ack_ are for responder replies. + * + * Common variables are protected by both r_rq.lock and s_lock in that order + * which only happens in modify_qp() or changing the QP 'state'. + */ +struct ipath_qp { + struct ib_qp ibqp; + struct ipath_qp *next; /* link list for QPN hash table */ + struct list_head piowait; /* link for wait PIO buf */ + struct list_head timerwait; /* link for waiting for timeouts */ + struct ib_ah_attr remote_ah_attr; + struct ipath_ib_header s_hdr; /* next packet header to send */ + atomic_t refcount; + wait_queue_head_t wait; + struct tasklet_struct s_task; + struct ipath_sge_state *s_cur_sge; + struct ipath_sge_state s_sge; /* current send request data */ + /* current RDMA read send data */ + struct ipath_sge_state s_rdma_sge; + struct ipath_sge_state r_sge; /* current receive data */ + spinlock_t s_lock; + unsigned long s_flags; + u32 s_hdrwords; /* size of s_hdr in 32 bit words */ + u32 s_cur_size; /* size of send packet in bytes */ + u32 s_len; /* total length of s_sge */ + u32 s_rdma_len; /* total length of s_rdma_sge */ + u32 s_next_psn; /* PSN for next request */ + u32 s_last_psn; /* last response PSN processed */ + u32 s_psn; /* current packet sequence number */ + u32 s_rnr_timeout; /* number of milliseconds for RNR timeout */ + u32 s_ack_psn; /* PSN for next ACK or RDMA_READ */ + u64 s_ack_atomic; /* data for atomic ACK */ + u64 r_wr_id; /* ID for current receive WQE */ + u64 r_atomic_data; /* data for last atomic op */ + u32 r_atomic_psn; /* PSN of last atomic op */ + u32 r_len; /* total length of r_sge */ + u32 r_rcv_len; /* receive data len processed */ + u32 r_psn; /* expected rcv packet sequence number */ + u8 state; /* QP state */ + u8 s_state; /* opcode of last packet sent */ + u8 s_ack_state; /* opcode of packet to ACK */ + u8 s_nak_state; /* non-zero if NAK is pending */ + u8 r_state; /* opcode of last packet received */ + u8 r_reuse_sge; /* for UC receive errors */ + u8 r_sge_inx; /* current index into sg_list */ + u8 s_max_sge; /* size of s_wq->sg_list */ + u8 qp_access_flags; + u8 s_retry_cnt; /* number of times to retry */ + u8 s_rnr_retry_cnt; + u8 s_min_rnr_timer; + u8 s_retry; /* requester retry counter */ + u8 s_rnr_retry; /* requester RNR retry counter */ + u8 s_pkey_index; /* PKEY index to use */ + enum ib_mtu path_mtu; + atomic_t msn; /* message sequence number */ + u32 remote_qpn; + u32 qkey; /* QKEY for this QP (for UD or RD) */ + u32 s_size; /* send work queue size */ + u32 s_head; /* new entries added here */ + u32 s_tail; /* next entry to process */ + u32 s_cur; /* current work queue entry */ + u32 s_last; /* last un-ACK'ed entry */ + u32 s_ssn; /* SSN of tail entry */ + u32 s_lsn; /* limit sequence number (credit) */ + struct ipath_swqe *s_wq; /* send work queue */ + struct ipath_rq r_rq; /* receive work queue */ +}; + +/* + * Bit definitions for s_flags. + */ +#define IPATH_S_BUSY 0 +#define IPATH_S_SIGNAL_REQ_WR 1 + +/* + * Since struct ipath_swqe is not a fixed size, we can't simply index into + * struct ipath_qp.s_wq. This function does the array index computation. + */ +static inline struct ipath_swqe *get_swqe_ptr(struct ipath_qp *qp, + unsigned n) +{ + return (struct ipath_swqe *)((char *)qp->s_wq + + (sizeof(struct ipath_swqe) + + qp->s_max_sge * + sizeof(struct ipath_sge)) * n); +} + +/* + * Since struct ipath_rwqe is not a fixed size, we can't simply index into + * struct ipath_rq.wq. This function does the array index computation. + */ +static inline struct ipath_rwqe *get_rwqe_ptr(struct ipath_rq *rq, + unsigned n) +{ + return (struct ipath_rwqe *) + ((char *) rq->wq + + (sizeof(struct ipath_rwqe) + + rq->max_sge * sizeof(struct ipath_sge)) * n); +} + +/* + * QPN-map pages start out as NULL, they get allocated upon + * first use and are never deallocated. This way, + * large bitmaps are not allocated unless large numbers of QPs are used. + */ +struct qpn_map { + atomic_t n_free; + void *page; +}; + +struct ipath_qp_table { + spinlock_t lock; + u32 last; /* last QP number allocated */ + u32 max; /* size of the hash table */ + u32 nmaps; /* size of the map table */ + struct ipath_qp **table; + /* bit map of free numbers */ + struct qpn_map map[QPNMAP_ENTRIES]; +}; + +struct ipath_lkey_table { + spinlock_t lock; + u32 next; /* next unused index (speeds search) */ + u32 gen; /* generation count */ + u32 max; /* size of the table */ + struct ipath_mregion **table; +}; + +struct ipath_opcode_stats { + u64 n_packets; /* number of packets */ + u64 n_bytes; /* total number of bytes */ +}; + +struct ipath_ibdev { + struct ib_device ibdev; + struct list_head dev_list; + struct ipath_devdata *dd; + int ib_unit; /* This is the device number */ + u16 sm_lid; /* in host order */ + u8 sm_sl; + u8 mkeyprot_resv_lmc; + /* non-zero when timer is set */ + unsigned long mkey_lease_timeout; + + /* The following fields are really per port. */ + struct ipath_qp_table qp_table; + struct ipath_lkey_table lk_table; + struct list_head pending[3]; /* FIFO of QPs waiting for ACKs */ + struct list_head piowait; /* list for wait PIO buf */ + /* list of QPs waiting for RNR timer */ + struct list_head rnrwait; + spinlock_t pending_lock; + __be64 sys_image_guid; /* in network order */ + __be64 gid_prefix; /* in network order */ + __be64 mkey; + u64 ipath_sword; /* total dwords sent (sample result) */ + u64 ipath_rword; /* total dwords received (sample result) */ + u64 ipath_spkts; /* total packets sent (sample result) */ + u64 ipath_rpkts; /* total packets received (sample result) */ + /* # of ticks no data sent (sample result) */ + u64 ipath_xmit_wait; + u64 rcv_errors; /* # of packets with SW detected rcv errs */ + u64 n_unicast_xmit; /* total unicast packets sent */ + u64 n_unicast_rcv; /* total unicast packets received */ + u64 n_multicast_xmit; /* total multicast packets sent */ + u64 n_multicast_rcv; /* total multicast packets received */ + u64 n_symbol_error_counter; /* starting count for PMA */ + u64 n_link_error_recovery_counter; /* starting count for PMA */ + u64 n_link_downed_counter; /* starting count for PMA */ + u64 n_port_rcv_errors; /* starting count for PMA */ + u64 n_port_rcv_remphys_errors; /* starting count for PMA */ + u64 n_port_xmit_discards; /* starting count for PMA */ + u64 n_port_xmit_data; /* starting count for PMA */ + u64 n_port_rcv_data; /* starting count for PMA */ + u64 n_port_xmit_packets; /* starting count for PMA */ + u64 n_port_rcv_packets; /* starting count for PMA */ + u32 n_pkey_violations; /* starting count for PMA */ + u32 n_rc_resends; + u32 n_rc_acks; + u32 n_rc_qacks; + u32 n_seq_naks; + u32 n_rdma_seq; + u32 n_rnr_naks; + u32 n_other_naks; + u32 n_timeouts; + u32 n_pkt_drops; + u32 n_wqe_errs; + u32 n_rdma_dup_busy; + u32 n_piowait; + u32 n_no_piobuf; + u32 port_cap_flags; + u32 pma_sample_start; + u32 pma_sample_interval; + __be16 pma_counter_select[5]; + u16 pma_tag; + u16 qkey_violations; + u16 mkey_violations; + u16 mkey_lease_period; + u16 pending_index; /* which pending queue is active */ + u8 pma_sample_status; + u8 subnet_timeout; + u8 link_width_enabled; + u8 vl_high_limit; + struct ipath_opcode_stats opstats[128]; +}; + +struct ipath_ucontext { + struct ib_ucontext ibucontext; +}; + +static inline struct ipath_mr *to_imr(struct ib_mr *ibmr) +{ + return container_of(ibmr, struct ipath_mr, ibmr); +} + +static inline struct ipath_fmr *to_ifmr(struct ib_fmr *ibfmr) +{ + return container_of(ibfmr, struct ipath_fmr, ibfmr); +} + +static inline struct ipath_pd *to_ipd(struct ib_pd *ibpd) +{ + return container_of(ibpd, struct ipath_pd, ibpd); +} + +static inline struct ipath_ah *to_iah(struct ib_ah *ibah) +{ + return container_of(ibah, struct ipath_ah, ibah); +} + +static inline struct ipath_cq *to_icq(struct ib_cq *ibcq) +{ + return container_of(ibcq, struct ipath_cq, ibcq); +} + +static inline struct ipath_srq *to_isrq(struct ib_srq *ibsrq) +{ + return container_of(ibsrq, struct ipath_srq, ibsrq); +} + +static inline struct ipath_qp *to_iqp(struct ib_qp *ibqp) +{ + return container_of(ibqp, struct ipath_qp, ibqp); +} + +static inline struct ipath_ibdev *to_idev(struct ib_device *ibdev) +{ + return container_of(ibdev, struct ipath_ibdev, ibdev); +} + +int ipath_process_mad(struct ib_device *ibdev, + int mad_flags, + u8 port_num, + struct ib_wc *in_wc, + struct ib_grh *in_grh, + struct ib_mad *in_mad, struct ib_mad *out_mad); + +static inline struct ipath_ucontext *to_iucontext(struct ib_ucontext + *ibucontext) +{ + return container_of(ibucontext, struct ipath_ucontext, ibucontext); +} + +/* + * Compare the lower 24 bits of the two values. + * Returns an integer <, ==, or > than zero. + */ +static inline int ipath_cmp24(u32 a, u32 b) +{ + return (((int) a) - ((int) b)) << 8; +} + +struct ipath_mcast *ipath_mcast_find(union ib_gid *mgid); + +int ipath_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid); + +int ipath_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid); + +int ipath_mcast_tree_empty(void); + +__be32 ipath_compute_aeth(struct ipath_qp *qp); + +struct ipath_qp *ipath_lookup_qpn(struct ipath_qp_table *qpt, u32 qpn); + +struct ib_qp *ipath_create_qp(struct ib_pd *ibpd, + struct ib_qp_init_attr *init_attr, + struct ib_udata *udata); + +int ipath_destroy_qp(struct ib_qp *ibqp); + +int ipath_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, + int attr_mask); + +int ipath_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, + int attr_mask, struct ib_qp_init_attr *init_attr); + +void ipath_free_all_qps(struct ipath_qp_table *qpt); + +int ipath_init_qp_table(struct ipath_ibdev *idev, int size); + +void ipath_sqerror_qp(struct ipath_qp *qp, struct ib_wc *wc); + +void ipath_error_qp(struct ipath_qp *qp); + +void ipath_get_credit(struct ipath_qp *qp, u32 aeth); + +void ipath_do_rc_send(unsigned long data); + +void ipath_do_uc_send(unsigned long data); + +void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int sig); + +int ipath_rkey_ok(struct ipath_ibdev *dev, struct ipath_sge_state *ss, + u32 len, u64 vaddr, u32 rkey, int acc); + +int ipath_lkey_ok(struct ipath_lkey_table *rkt, struct ipath_sge *isge, + struct ib_sge *sge, int acc); + +void ipath_copy_sge(struct ipath_sge_state *ss, void *data, u32 length); + +void ipath_skip_sge(struct ipath_sge_state *ss, u32 length); + +int ipath_post_rc_send(struct ipath_qp *qp, struct ib_send_wr *wr); + +void ipath_uc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, + int has_grh, void *data, u32 tlen, struct ipath_qp *qp); + +void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, + int has_grh, void *data, u32 tlen, struct ipath_qp *qp); + +void ipath_restart_rc(struct ipath_qp *qp, u32 psn, struct ib_wc *wc); + +void ipath_ud_loopback(struct ipath_qp *sqp, struct ipath_sge_state *ss, + u32 length, struct ib_send_wr *wr, struct ib_wc *wc); + +int ipath_post_ud_send(struct ipath_qp *qp, struct ib_send_wr *wr); + +void ipath_ud_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, + int has_grh, void *data, u32 tlen, struct ipath_qp *qp); + +int ipath_alloc_lkey(struct ipath_lkey_table *rkt, + struct ipath_mregion *mr); + +void ipath_free_lkey(struct ipath_lkey_table *rkt, u32 lkey); + +int ipath_lkey_ok(struct ipath_lkey_table *rkt, struct ipath_sge *isge, + struct ib_sge *sge, int acc); + +int ipath_rkey_ok(struct ipath_ibdev *dev, struct ipath_sge_state *ss, + u32 len, u64 vaddr, u32 rkey, int acc); + +int ipath_post_srq_receive(struct ib_srq *ibsrq, struct ib_recv_wr *wr, + struct ib_recv_wr **bad_wr); + +struct ib_srq *ipath_create_srq(struct ib_pd *ibpd, + struct ib_srq_init_attr *srq_init_attr, + struct ib_udata *udata); + +int ipath_modify_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr, + enum ib_srq_attr_mask attr_mask); + +int ipath_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr); + +int ipath_destroy_srq(struct ib_srq *ibsrq); + +void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int sig); + +int ipath_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry); + +struct ib_cq *ipath_create_cq(struct ib_device *ibdev, int entries, + struct ib_ucontext *context, + struct ib_udata *udata); + +int ipath_destroy_cq(struct ib_cq *ibcq); + +int ipath_req_notify_cq(struct ib_cq *ibcq, enum ib_cq_notify notify); + +int ipath_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata); + +struct ib_mr *ipath_get_dma_mr(struct ib_pd *pd, int acc); + +struct ib_mr *ipath_reg_phys_mr(struct ib_pd *pd, + struct ib_phys_buf *buffer_list, + int num_phys_buf, int acc, u64 *iova_start); + +struct ib_mr *ipath_reg_user_mr(struct ib_pd *pd, struct ib_umem *region, + int mr_access_flags, + struct ib_udata *udata); + +int ipath_dereg_mr(struct ib_mr *ibmr); + +struct ib_fmr *ipath_alloc_fmr(struct ib_pd *pd, int mr_access_flags, + struct ib_fmr_attr *fmr_attr); + +int ipath_map_phys_fmr(struct ib_fmr *ibfmr, u64 * page_list, + int list_len, u64 iova); + +int ipath_unmap_fmr(struct list_head *fmr_list); + +int ipath_dealloc_fmr(struct ib_fmr *ibfmr); + +void ipath_no_bufs_available(struct ipath_qp *qp, struct ipath_ibdev *dev); + +void ipath_insert_rnr_queue(struct ipath_qp *qp); + +int ipath_get_rwqe(struct ipath_qp *qp, int wr_id_only); + +void ipath_ruc_loopback(struct ipath_qp *sqp, struct ib_wc *wc); + +extern const enum ib_wc_opcode ib_ipath_wc_opcode[]; + +extern const u8 ipath_cvt_physportstate[]; + +extern const int ib_ipath_state_ops[]; + +extern unsigned int ib_ipath_lkey_table_size; + +extern const u32 ib_ipath_rnr_table[]; + +#endif /* IPATH_VERBS_H */ diff --git a/drivers/infiniband/hw/ipath/ipath_verbs_mcast.c b/drivers/infiniband/hw/ipath/ipath_verbs_mcast.c new file mode 100644 index 000000000000..10b31d2c4f20 --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_verbs_mcast.c @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include + +#include "ipath_verbs.h" + +/* + * Global table of GID to attached QPs. + * The table is global to all ipath devices since a send from one QP/device + * needs to be locally routed to any locally attached QPs on the same + * or different device. + */ +static struct rb_root mcast_tree; +static DEFINE_SPINLOCK(mcast_lock); + +/** + * ipath_mcast_qp_alloc - alloc a struct to link a QP to mcast GID struct + * @qp: the QP to link + */ +static struct ipath_mcast_qp *ipath_mcast_qp_alloc(struct ipath_qp *qp) +{ + struct ipath_mcast_qp *mqp; + + mqp = kmalloc(sizeof *mqp, GFP_KERNEL); + if (!mqp) + goto bail; + + mqp->qp = qp; + atomic_inc(&qp->refcount); + +bail: + return mqp; +} + +static void ipath_mcast_qp_free(struct ipath_mcast_qp *mqp) +{ + struct ipath_qp *qp = mqp->qp; + + /* Notify ipath_destroy_qp() if it is waiting. */ + if (atomic_dec_and_test(&qp->refcount)) + wake_up(&qp->wait); + + kfree(mqp); +} + +/** + * ipath_mcast_alloc - allocate the multicast GID structure + * @mgid: the multicast GID + * + * A list of QPs will be attached to this structure. + */ +static struct ipath_mcast *ipath_mcast_alloc(union ib_gid *mgid) +{ + struct ipath_mcast *mcast; + + mcast = kmalloc(sizeof *mcast, GFP_KERNEL); + if (!mcast) + goto bail; + + mcast->mgid = *mgid; + INIT_LIST_HEAD(&mcast->qp_list); + init_waitqueue_head(&mcast->wait); + atomic_set(&mcast->refcount, 0); + +bail: + return mcast; +} + +static void ipath_mcast_free(struct ipath_mcast *mcast) +{ + struct ipath_mcast_qp *p, *tmp; + + list_for_each_entry_safe(p, tmp, &mcast->qp_list, list) + ipath_mcast_qp_free(p); + + kfree(mcast); +} + +/** + * ipath_mcast_find - search the global table for the given multicast GID + * @mgid: the multicast GID to search for + * + * Returns NULL if not found. + * + * The caller is responsible for decrementing the reference count if found. + */ +struct ipath_mcast *ipath_mcast_find(union ib_gid *mgid) +{ + struct rb_node *n; + unsigned long flags; + struct ipath_mcast *mcast; + + spin_lock_irqsave(&mcast_lock, flags); + n = mcast_tree.rb_node; + while (n) { + int ret; + + mcast = rb_entry(n, struct ipath_mcast, rb_node); + + ret = memcmp(mgid->raw, mcast->mgid.raw, + sizeof(union ib_gid)); + if (ret < 0) + n = n->rb_left; + else if (ret > 0) + n = n->rb_right; + else { + atomic_inc(&mcast->refcount); + spin_unlock_irqrestore(&mcast_lock, flags); + goto bail; + } + } + spin_unlock_irqrestore(&mcast_lock, flags); + + mcast = NULL; + +bail: + return mcast; +} + +/** + * ipath_mcast_add - insert mcast GID into table and attach QP struct + * @mcast: the mcast GID table + * @mqp: the QP to attach + * + * Return zero if both were added. Return EEXIST if the GID was already in + * the table but the QP was added. Return ESRCH if the QP was already + * attached and neither structure was added. + */ +static int ipath_mcast_add(struct ipath_mcast *mcast, + struct ipath_mcast_qp *mqp) +{ + struct rb_node **n = &mcast_tree.rb_node; + struct rb_node *pn = NULL; + unsigned long flags; + int ret; + + spin_lock_irqsave(&mcast_lock, flags); + + while (*n) { + struct ipath_mcast *tmcast; + struct ipath_mcast_qp *p; + + pn = *n; + tmcast = rb_entry(pn, struct ipath_mcast, rb_node); + + ret = memcmp(mcast->mgid.raw, tmcast->mgid.raw, + sizeof(union ib_gid)); + if (ret < 0) { + n = &pn->rb_left; + continue; + } + if (ret > 0) { + n = &pn->rb_right; + continue; + } + + /* Search the QP list to see if this is already there. */ + list_for_each_entry_rcu(p, &tmcast->qp_list, list) { + if (p->qp == mqp->qp) { + spin_unlock_irqrestore(&mcast_lock, flags); + ret = ESRCH; + goto bail; + } + } + list_add_tail_rcu(&mqp->list, &tmcast->qp_list); + spin_unlock_irqrestore(&mcast_lock, flags); + ret = EEXIST; + goto bail; + } + + list_add_tail_rcu(&mqp->list, &mcast->qp_list); + + atomic_inc(&mcast->refcount); + rb_link_node(&mcast->rb_node, pn, n); + rb_insert_color(&mcast->rb_node, &mcast_tree); + + spin_unlock_irqrestore(&mcast_lock, flags); + + ret = 0; + +bail: + return ret; +} + +int ipath_multicast_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) +{ + struct ipath_qp *qp = to_iqp(ibqp); + struct ipath_mcast *mcast; + struct ipath_mcast_qp *mqp; + int ret; + + /* + * Allocate data structures since its better to do this outside of + * spin locks and it will most likely be needed. + */ + mcast = ipath_mcast_alloc(gid); + if (mcast == NULL) { + ret = -ENOMEM; + goto bail; + } + mqp = ipath_mcast_qp_alloc(qp); + if (mqp == NULL) { + ipath_mcast_free(mcast); + ret = -ENOMEM; + goto bail; + } + switch (ipath_mcast_add(mcast, mqp)) { + case ESRCH: + /* Neither was used: can't attach the same QP twice. */ + ipath_mcast_qp_free(mqp); + ipath_mcast_free(mcast); + ret = -EINVAL; + goto bail; + case EEXIST: /* The mcast wasn't used */ + ipath_mcast_free(mcast); + break; + default: + break; + } + + ret = 0; + +bail: + return ret; +} + +int ipath_multicast_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) +{ + struct ipath_qp *qp = to_iqp(ibqp); + struct ipath_mcast *mcast = NULL; + struct ipath_mcast_qp *p, *tmp; + struct rb_node *n; + unsigned long flags; + int last = 0; + int ret; + + spin_lock_irqsave(&mcast_lock, flags); + + /* Find the GID in the mcast table. */ + n = mcast_tree.rb_node; + while (1) { + if (n == NULL) { + spin_unlock_irqrestore(&mcast_lock, flags); + ret = 0; + goto bail; + } + + mcast = rb_entry(n, struct ipath_mcast, rb_node); + ret = memcmp(gid->raw, mcast->mgid.raw, + sizeof(union ib_gid)); + if (ret < 0) + n = n->rb_left; + else if (ret > 0) + n = n->rb_right; + else + break; + } + + /* Search the QP list. */ + list_for_each_entry_safe(p, tmp, &mcast->qp_list, list) { + if (p->qp != qp) + continue; + /* + * We found it, so remove it, but don't poison the forward + * link until we are sure there are no list walkers. + */ + list_del_rcu(&p->list); + + /* If this was the last attached QP, remove the GID too. */ + if (list_empty(&mcast->qp_list)) { + rb_erase(&mcast->rb_node, &mcast_tree); + last = 1; + } + break; + } + + spin_unlock_irqrestore(&mcast_lock, flags); + + if (p) { + /* + * Wait for any list walkers to finish before freeing the + * list element. + */ + wait_event(mcast->wait, atomic_read(&mcast->refcount) <= 1); + ipath_mcast_qp_free(p); + } + if (last) { + atomic_dec(&mcast->refcount); + wait_event(mcast->wait, !atomic_read(&mcast->refcount)); + ipath_mcast_free(mcast); + } + + ret = 0; + +bail: + return ret; +} + +int ipath_mcast_tree_empty(void) +{ + return mcast_tree.rb_node == NULL; +} diff --git a/drivers/infiniband/hw/ipath/ipath_wc_x86_64.c b/drivers/infiniband/hw/ipath/ipath_wc_x86_64.c new file mode 100644 index 000000000000..adc5322f15c1 --- /dev/null +++ b/drivers/infiniband/hw/ipath/ipath_wc_x86_64.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * This file is conditionally built on x86_64 only. Otherwise weak symbol + * versions of the functions exported from here are used. + */ + +#include +#include +#include + +#include "ipath_kernel.h" + +/** + * ipath_enable_wc - enable write combining for MMIO writes to the device + * @dd: infinipath device + * + * This routine is x86_64-specific; it twiddles the CPU's MTRRs to enable + * write combining. + */ +int ipath_enable_wc(struct ipath_devdata *dd) +{ + int ret = 0; + u64 pioaddr, piolen; + unsigned bits; + const unsigned long addr = pci_resource_start(dd->pcidev, 0); + const size_t len = pci_resource_len(dd->pcidev, 0); + + /* + * Set the PIO buffers to be WCCOMB, so we get HT bursts to the + * chip. Linux (possibly the hardware) requires it to be on a power + * of 2 address matching the length (which has to be a power of 2). + * For rev1, that means the base address, for rev2, it will be just + * the PIO buffers themselves. + */ + pioaddr = addr + dd->ipath_piobufbase; + piolen = (dd->ipath_piobcnt2k + + dd->ipath_piobcnt4k) * + ALIGN(dd->ipath_piobcnt2k + + dd->ipath_piobcnt4k, dd->ipath_palign); + + for (bits = 0; !(piolen & (1ULL << bits)); bits++) + /* do nothing */ ; + + if (piolen != (1ULL << bits)) { + piolen >>= bits; + while (piolen >>= 1) + bits++; + piolen = 1ULL << (bits + 1); + } + if (pioaddr & (piolen - 1)) { + u64 atmp; + ipath_dbg("pioaddr %llx not on right boundary for size " + "%llx, fixing\n", + (unsigned long long) pioaddr, + (unsigned long long) piolen); + atmp = pioaddr & ~(piolen - 1); + if (atmp < addr || (atmp + piolen) > (addr + len)) { + ipath_dev_err(dd, "No way to align address/size " + "(%llx/%llx), no WC mtrr\n", + (unsigned long long) atmp, + (unsigned long long) piolen << 1); + ret = -ENODEV; + } else { + ipath_dbg("changing WC base from %llx to %llx, " + "len from %llx to %llx\n", + (unsigned long long) pioaddr, + (unsigned long long) atmp, + (unsigned long long) piolen, + (unsigned long long) piolen << 1); + pioaddr = atmp; + piolen <<= 1; + } + } + + if (!ret) { + int cookie; + ipath_cdbg(VERBOSE, "Setting mtrr for chip to WC " + "(addr %llx, len=0x%llx)\n", + (unsigned long long) pioaddr, + (unsigned long long) piolen); + cookie = mtrr_add(pioaddr, piolen, MTRR_TYPE_WRCOMB, 0); + if (cookie < 0) { + { + dev_info(&dd->pcidev->dev, + "mtrr_add() WC for PIO bufs " + "failed (%d)\n", + cookie); + ret = -EINVAL; + } + } else { + ipath_cdbg(VERBOSE, "Set mtrr for chip to WC, " + "cookie is %d\n", cookie); + dd->ipath_wc_cookie = cookie; + } + } + + return ret; +} + +/** + * ipath_disable_wc - disable write combining for MMIO writes to the device + * @dd: infinipath device + */ +void ipath_disable_wc(struct ipath_devdata *dd) +{ + if (dd->ipath_wc_cookie) { + ipath_cdbg(VERBOSE, "undoing WCCOMB on pio buffers\n"); + mtrr_del(dd->ipath_wc_cookie, 0, 0); + dd->ipath_wc_cookie = 0; + } +} + +/** + * ipath_unordered_wc - indicate whether write combining is ordered + * + * Because our performance depends on our ability to do write combining mmio + * writes in the most efficient way, we need to know if we are on an Intel + * or AMD x86_64 processor. AMD x86_64 processors flush WC buffers out in + * the order completed, and so no special flushing is required to get + * correct ordering. Intel processors, however, will flush write buffers + * out in "random" orders, and so explicit ordering is needed at times. + */ +int ipath_unordered_wc(void) +{ + return boot_cpu_data.x86_vendor != X86_VENDOR_AMD; +} diff --git a/drivers/infiniband/hw/ipath/ips_common.h b/drivers/infiniband/hw/ipath/ips_common.h new file mode 100644 index 000000000000..410a764dfcef --- /dev/null +++ b/drivers/infiniband/hw/ipath/ips_common.h @@ -0,0 +1,263 @@ +#ifndef IPS_COMMON_H +#define IPS_COMMON_H +/* + * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "ipath_common.h" + +struct ipath_header { + /* + * Version - 4 bits, Port - 4 bits, TID - 10 bits and Offset - + * 14 bits before ECO change ~28 Dec 03. After that, Vers 4, + * Port 3, TID 11, offset 14. + */ + __le32 ver_port_tid_offset; + __le16 chksum; + __le16 pkt_flags; +}; + +struct ips_message_header { + __be16 lrh[4]; + __be32 bth[3]; + /* fields below this point are in host byte order */ + struct ipath_header iph; + __u8 sub_opcode; + __u8 flags; + __u16 src_rank; + /* 24 bits. The upper 8 bit is available for other use */ + union { + struct { + unsigned ack_seq_num:24; + unsigned port:4; + unsigned unused:4; + }; + __u32 ack_seq_num_org; + }; + __u8 expected_tid_session_id; + __u8 tinylen; /* to aid MPI */ + union { + __u16 tag; /* to aid MPI */ + __u16 mqhdr; /* for PSM MQ */ + }; + union { + __u32 mpi[4]; /* to aid MPI */ + __u32 data[4]; + __u64 mq[2]; /* for PSM MQ */ + struct { + __u16 mtu; + __u8 major_ver; + __u8 minor_ver; + __u32 not_used; //free + __u32 run_id; + __u32 client_ver; + }; + }; +}; + +struct ether_header { + __be16 lrh[4]; + __be32 bth[3]; + struct ipath_header iph; + __u8 sub_opcode; + __u8 cmd; + __be16 lid; + __u16 mac[3]; + __u8 frag_num; + __u8 seq_num; + __le32 len; + /* MUST be of word size due to PIO write requirements */ + __u32 csum; + __le16 csum_offset; + __le16 flags; + __u16 first_2_bytes; + __u8 unused[2]; /* currently unused */ +}; + +/* + * The PIO buffer used for sending infinipath messages must only be written + * in 32-bit words, all the data must be written, and no writes can occur + * after the last word is written (which transfers "ownership" of the buffer + * to the chip and triggers the message to be sent). + * Since the Linux sk_buff structure can be recursive, non-aligned, and + * any number of bytes in each segment, we use the following structure + * to keep information about the overall state of the copy operation. + * This is used to save the information needed to store the checksum + * in the right place before sending the last word to the hardware and + * to buffer the last 0-3 bytes of non-word sized segments. + */ +struct copy_data_s { + struct ether_header *hdr; + /* addr of PIO buf to write csum to */ + __u32 __iomem *csum_pio; + __u32 __iomem *to; /* addr of PIO buf to write data to */ + __u32 device; /* which device to allocate PIO bufs from */ + __s32 error; /* set if there is an error. */ + __s32 extra; /* amount of data saved in u.buf below */ + __u32 len; /* total length to send in bytes */ + __u32 flen; /* frament length in words */ + __u32 csum; /* partial IP checksum */ + __u32 pos; /* position for partial checksum */ + __u32 offset; /* offset to where data currently starts */ + __s32 checksum_calc; /* set to 1 when csum has been calculated */ + struct sk_buff *skb; + union { + __u32 w; + __u8 buf[4]; + } u; +}; + +/* IB - LRH header consts */ +#define IPS_LRH_GRH 0x0003 /* 1. word of IB LRH - next header: GRH */ +#define IPS_LRH_BTH 0x0002 /* 1. word of IB LRH - next header: BTH */ + +#define IPS_OFFSET 0 + +/* + * defines the cut-off point between the header queue and eager/expected + * TID queue + */ +#define NUM_OF_EXTRA_WORDS_IN_HEADER_QUEUE \ + ((sizeof(struct ips_message_header) - \ + offsetof(struct ips_message_header, iph)) >> 2) + +/* OpCodes */ +#define OPCODE_IPS 0xC0 +#define OPCODE_ITH4X 0xC1 + +/* OpCode 30 is use by stand-alone test programs */ +#define OPCODE_RAW_DATA 0xDE +/* last OpCode (31) is reserved for test */ +#define OPCODE_TEST 0xDF + +/* sub OpCodes - ips */ +#define OPCODE_SEQ_DATA 0x01 +#define OPCODE_SEQ_CTRL 0x02 + +#define OPCODE_SEQ_MQ_DATA 0x03 +#define OPCODE_SEQ_MQ_CTRL 0x04 + +#define OPCODE_ACK 0x10 +#define OPCODE_NAK 0x11 + +#define OPCODE_ERR_CHK 0x20 +#define OPCODE_ERR_CHK_PLS 0x21 + +#define OPCODE_STARTUP 0x30 +#define OPCODE_STARTUP_ACK 0x31 +#define OPCODE_STARTUP_NAK 0x32 + +#define OPCODE_STARTUP_EXT 0x34 +#define OPCODE_STARTUP_ACK_EXT 0x35 +#define OPCODE_STARTUP_NAK_EXT 0x36 + +#define OPCODE_TIDS_RELEASE 0x40 +#define OPCODE_TIDS_RELEASE_CONFIRM 0x41 + +#define OPCODE_CLOSE 0x50 +#define OPCODE_CLOSE_ACK 0x51 +/* + * like OPCODE_CLOSE, but no complaint if other side has already closed. + * Used when doing abort(), MPI_Abort(), etc. + */ +#define OPCODE_ABORT 0x52 + +/* sub OpCodes - ith4x */ +#define OPCODE_ENCAP 0x81 +#define OPCODE_LID_ARP 0x82 + +/* Receive Header Queue: receive type (from infinipath) */ +#define RCVHQ_RCV_TYPE_EXPECTED 0 +#define RCVHQ_RCV_TYPE_EAGER 1 +#define RCVHQ_RCV_TYPE_NON_KD 2 +#define RCVHQ_RCV_TYPE_ERROR 3 + +/* misc. */ +#define SIZE_OF_CRC 1 + +#define EAGER_TID_ID INFINIPATH_I_TID_MASK + +#define IPS_DEFAULT_P_KEY 0xFFFF + +#define IPS_PERMISSIVE_LID 0xFFFF +#define IPS_MULTICAST_LID_BASE 0xC000 + +#define IPS_AETH_CREDIT_SHIFT 24 +#define IPS_AETH_CREDIT_MASK 0x1F +#define IPS_AETH_CREDIT_INVAL 0x1F + +#define IPS_PSN_MASK 0xFFFFFF +#define IPS_MSN_MASK 0xFFFFFF +#define IPS_QPN_MASK 0xFFFFFF +#define IPS_MULTICAST_QPN 0xFFFFFF + +/* functions for extracting fields from rcvhdrq entries */ +static inline __u32 ips_get_hdr_err_flags(const __le32 * rbuf) +{ + return __le32_to_cpu(rbuf[1]); +} + +static inline __u32 ips_get_index(const __le32 * rbuf) +{ + return (__le32_to_cpu(rbuf[0]) >> INFINIPATH_RHF_EGRINDEX_SHIFT) + & INFINIPATH_RHF_EGRINDEX_MASK; +} + +static inline __u32 ips_get_rcv_type(const __le32 * rbuf) +{ + return (__le32_to_cpu(rbuf[0]) >> INFINIPATH_RHF_RCVTYPE_SHIFT) + & INFINIPATH_RHF_RCVTYPE_MASK; +} + +static inline __u32 ips_get_length_in_bytes(const __le32 * rbuf) +{ + return ((__le32_to_cpu(rbuf[0]) >> INFINIPATH_RHF_LENGTH_SHIFT) + & INFINIPATH_RHF_LENGTH_MASK) << 2; +} + +static inline void *ips_get_first_protocol_header(const __u32 * rbuf) +{ + return (void *)&rbuf[2]; +} + +static inline struct ips_message_header *ips_get_ips_header(const __u32 * + rbuf) +{ + return (struct ips_message_header *)&rbuf[2]; +} + +static inline __u32 ips_get_ipath_ver(__le32 hdrword) +{ + return (__le32_to_cpu(hdrword) >> INFINIPATH_I_VERS_SHIFT) + & INFINIPATH_I_VERS_MASK; +} + +#endif /* IPS_COMMON_H */ diff --git a/drivers/infiniband/hw/ipath/verbs_debug.h b/drivers/infiniband/hw/ipath/verbs_debug.h new file mode 100644 index 000000000000..40d693cf3f94 --- /dev/null +++ b/drivers/infiniband/hw/ipath/verbs_debug.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _VERBS_DEBUG_H +#define _VERBS_DEBUG_H + +/* + * This file contains tracing code for the ib_ipath kernel module. + */ +#ifndef _VERBS_DEBUGGING /* tracing enabled or not */ +#define _VERBS_DEBUGGING 1 +#endif + +extern unsigned ib_ipath_debug; + +#define _VERBS_ERROR(fmt,...) \ + do { \ + printk(KERN_ERR "%s: " fmt, "ib_ipath", ##__VA_ARGS__); \ + } while(0) + +#define _VERBS_UNIT_ERROR(unit,fmt,...) \ + do { \ + printk(KERN_ERR "%s: " fmt, "ib_ipath", ##__VA_ARGS__); \ + } while(0) + +#if _VERBS_DEBUGGING + +/* + * Mask values for debugging. The scheme allows us to compile out any + * of the debug tracing stuff, and if compiled in, to enable or + * disable dynamically. + * This can be set at modprobe time also: + * modprobe ib_path ib_ipath_debug=3 + */ + +#define __VERBS_INFO 0x1 /* generic low verbosity stuff */ +#define __VERBS_DBG 0x2 /* generic debug */ +#define __VERBS_VDBG 0x4 /* verbose debug */ +#define __VERBS_SMADBG 0x8000 /* sma packet debug */ + +#define _VERBS_INFO(fmt,...) \ + do { \ + if (unlikely(ib_ipath_debug&__VERBS_INFO)) \ + printk(KERN_INFO "%s: " fmt,"ib_ipath", \ + ##__VA_ARGS__); \ + } while(0) + +#define _VERBS_DBG(fmt,...) \ + do { \ + if (unlikely(ib_ipath_debug&__VERBS_DBG)) \ + printk(KERN_DEBUG "%s: " fmt, __func__, \ + ##__VA_ARGS__); \ + } while(0) + +#define _VERBS_VDBG(fmt,...) \ + do { \ + if (unlikely(ib_ipath_debug&__VERBS_VDBG)) \ + printk(KERN_DEBUG "%s: " fmt, __func__, \ + ##__VA_ARGS__); \ + } while(0) + +#define _VERBS_SMADBG(fmt,...) \ + do { \ + if (unlikely(ib_ipath_debug&__VERBS_SMADBG)) \ + printk(KERN_DEBUG "%s: " fmt, __func__, \ + ##__VA_ARGS__); \ + } while(0) + +#else /* ! _VERBS_DEBUGGING */ + +#define _VERBS_INFO(fmt,...) +#define _VERBS_DBG(fmt,...) +#define _VERBS_VDBG(fmt,...) +#define _VERBS_SMADBG(fmt,...) + +#endif /* _VERBS_DEBUGGING */ + +#endif /* _VERBS_DEBUG_H */ diff --git a/drivers/infiniband/hw/mthca/mthca_av.c b/drivers/infiniband/hw/mthca/mthca_av.c index f023d3936518..bc5bdcbe51b5 100644 --- a/drivers/infiniband/hw/mthca/mthca_av.c +++ b/drivers/infiniband/hw/mthca/mthca_av.c @@ -265,7 +265,7 @@ int __devinit mthca_init_av_table(struct mthca_dev *dev) return -ENOMEM; } -void __devexit mthca_cleanup_av_table(struct mthca_dev *dev) +void mthca_cleanup_av_table(struct mthca_dev *dev) { if (mthca_is_memfree(dev)) return; diff --git a/drivers/infiniband/hw/mthca/mthca_cq.c b/drivers/infiniband/hw/mthca/mthca_cq.c index 76aabc5bf371..312cf90731ea 100644 --- a/drivers/infiniband/hw/mthca/mthca_cq.c +++ b/drivers/infiniband/hw/mthca/mthca_cq.c @@ -973,7 +973,7 @@ int __devinit mthca_init_cq_table(struct mthca_dev *dev) return err; } -void __devexit mthca_cleanup_cq_table(struct mthca_dev *dev) +void mthca_cleanup_cq_table(struct mthca_dev *dev) { mthca_array_cleanup(&dev->cq_table.cq, dev->limits.num_cqs); mthca_alloc_cleanup(&dev->cq_table.alloc); diff --git a/drivers/infiniband/hw/mthca/mthca_eq.c b/drivers/infiniband/hw/mthca/mthca_eq.c index cbdc348fb689..99f109c3815d 100644 --- a/drivers/infiniband/hw/mthca/mthca_eq.c +++ b/drivers/infiniband/hw/mthca/mthca_eq.c @@ -765,7 +765,7 @@ static int __devinit mthca_map_eq_regs(struct mthca_dev *dev) } -static void __devexit mthca_unmap_eq_regs(struct mthca_dev *dev) +static void mthca_unmap_eq_regs(struct mthca_dev *dev) { if (mthca_is_memfree(dev)) { mthca_unmap_reg(dev, (pci_resource_len(dev->pdev, 0) - 1) & @@ -821,7 +821,7 @@ int __devinit mthca_map_eq_icm(struct mthca_dev *dev, u64 icm_virt) return ret; } -void __devexit mthca_unmap_eq_icm(struct mthca_dev *dev) +void mthca_unmap_eq_icm(struct mthca_dev *dev) { u8 status; @@ -954,7 +954,7 @@ err_out_free: return err; } -void __devexit mthca_cleanup_eq_table(struct mthca_dev *dev) +void mthca_cleanup_eq_table(struct mthca_dev *dev) { u8 status; int i; diff --git a/drivers/infiniband/hw/mthca/mthca_mad.c b/drivers/infiniband/hw/mthca/mthca_mad.c index 4ace6a392f41..dfb482eac9a2 100644 --- a/drivers/infiniband/hw/mthca/mthca_mad.c +++ b/drivers/infiniband/hw/mthca/mthca_mad.c @@ -271,7 +271,7 @@ err: return PTR_ERR(agent); } -void mthca_free_agents(struct mthca_dev *dev) +void __devexit mthca_free_agents(struct mthca_dev *dev) { struct ib_mad_agent *agent; int p, q; diff --git a/drivers/infiniband/hw/mthca/mthca_mcg.c b/drivers/infiniband/hw/mthca/mthca_mcg.c index 9965bda9afed..47ca8a9b7247 100644 --- a/drivers/infiniband/hw/mthca/mthca_mcg.c +++ b/drivers/infiniband/hw/mthca/mthca_mcg.c @@ -388,7 +388,7 @@ int __devinit mthca_init_mcg_table(struct mthca_dev *dev) return 0; } -void __devexit mthca_cleanup_mcg_table(struct mthca_dev *dev) +void mthca_cleanup_mcg_table(struct mthca_dev *dev) { mthca_alloc_cleanup(&dev->mcg_table.alloc); } diff --git a/drivers/infiniband/hw/mthca/mthca_mr.c b/drivers/infiniband/hw/mthca/mthca_mr.c index 698b62125765..25e1c1db9a40 100644 --- a/drivers/infiniband/hw/mthca/mthca_mr.c +++ b/drivers/infiniband/hw/mthca/mthca_mr.c @@ -170,7 +170,7 @@ err_out: return -ENOMEM; } -static void __devexit mthca_buddy_cleanup(struct mthca_buddy *buddy) +static void mthca_buddy_cleanup(struct mthca_buddy *buddy) { int i; @@ -866,7 +866,7 @@ err_mtt_buddy: return err; } -void __devexit mthca_cleanup_mr_table(struct mthca_dev *dev) +void mthca_cleanup_mr_table(struct mthca_dev *dev) { /* XXX check if any MRs are still allocated? */ if (dev->limits.fmr_reserved_mtts) diff --git a/drivers/infiniband/hw/mthca/mthca_pd.c b/drivers/infiniband/hw/mthca/mthca_pd.c index 105fc5faaddf..59df51614c85 100644 --- a/drivers/infiniband/hw/mthca/mthca_pd.c +++ b/drivers/infiniband/hw/mthca/mthca_pd.c @@ -77,7 +77,7 @@ int __devinit mthca_init_pd_table(struct mthca_dev *dev) dev->limits.reserved_pds); } -void __devexit mthca_cleanup_pd_table(struct mthca_dev *dev) +void mthca_cleanup_pd_table(struct mthca_dev *dev) { /* XXX check if any PDs are still allocated? */ mthca_alloc_cleanup(&dev->pd_table.alloc); diff --git a/drivers/infiniband/hw/mthca/mthca_qp.c b/drivers/infiniband/hw/mthca/mthca_qp.c index 1bc2678c2fae..057c8e6af87b 100644 --- a/drivers/infiniband/hw/mthca/mthca_qp.c +++ b/drivers/infiniband/hw/mthca/mthca_qp.c @@ -2204,7 +2204,7 @@ int __devinit mthca_init_qp_table(struct mthca_dev *dev) return err; } -void __devexit mthca_cleanup_qp_table(struct mthca_dev *dev) +void mthca_cleanup_qp_table(struct mthca_dev *dev) { int i; u8 status; diff --git a/drivers/infiniband/hw/mthca/mthca_srq.c b/drivers/infiniband/hw/mthca/mthca_srq.c index 0cfd15802217..2dd3aea05341 100644 --- a/drivers/infiniband/hw/mthca/mthca_srq.c +++ b/drivers/infiniband/hw/mthca/mthca_srq.c @@ -206,7 +206,7 @@ int mthca_alloc_srq(struct mthca_dev *dev, struct mthca_pd *pd, roundup_pow_of_two(sizeof (struct mthca_next_seg) + srq->max_gs * sizeof (struct mthca_data_seg))); - if (ds > dev->limits.max_desc_sz) + if (!mthca_is_memfree(dev) && (ds > dev->limits.max_desc_sz)) return -EINVAL; srq->wqe_shift = long_log2(ds); @@ -684,7 +684,7 @@ int __devinit mthca_init_srq_table(struct mthca_dev *dev) return err; } -void __devexit mthca_cleanup_srq_table(struct mthca_dev *dev) +void mthca_cleanup_srq_table(struct mthca_dev *dev) { if (!(dev->mthca_flags & MTHCA_FLAG_SRQ)) return; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 53a32f65788d..9b0bd7c746ca 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -723,7 +723,7 @@ static int ipoib_hard_header(struct sk_buff *skb, * destination address onto the front of the skb so we can * figure out where to send the packet later. */ - if (!skb->dst || !skb->dst->neighbour) { + if ((!skb->dst || !skb->dst->neighbour) && daddr) { struct ipoib_pseudoheader *phdr = (struct ipoib_pseudoheader *) skb_push(skb, sizeof *phdr); memcpy(phdr->hwaddr, daddr, INFINIBAND_ALEN); diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index 61924cc30e55..fd8a95a9c5d3 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -607,10 +607,10 @@ static void srp_unmap_data(struct scsi_cmnd *scmnd, */ if (likely(scmnd->use_sg)) { nents = scmnd->use_sg; - scat = (struct scatterlist *) scmnd->request_buffer; + scat = scmnd->request_buffer; } else { nents = 1; - scat = (struct scatterlist *) scmnd->request_buffer; + scat = &req->fake_sg; } dma_unmap_sg(target->srp_host->dev->dma_device, scat, nents, diff --git a/drivers/input/evbug.c b/drivers/input/evbug.c index d7828936fd8f..07358fb51b82 100644 --- a/drivers/input/evbug.c +++ b/drivers/input/evbug.c @@ -49,9 +49,8 @@ static struct input_handle *evbug_connect(struct input_handler *handler, struct { struct input_handle *handle; - if (!(handle = kmalloc(sizeof(struct input_handle), GFP_KERNEL))) + if (!(handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL))) return NULL; - memset(handle, 0, sizeof(struct input_handle)); handle->dev = dev; handle->handler = handler; diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 745979f33dc2..a34e3d91d9ed 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -130,9 +130,8 @@ static int evdev_open(struct inode * inode, struct file * file) if ((accept_err = input_accept_process(&(evdev_table[i]->handle), file))) return accept_err; - if (!(list = kmalloc(sizeof(struct evdev_list), GFP_KERNEL))) + if (!(list = kzalloc(sizeof(struct evdev_list), GFP_KERNEL))) return -ENOMEM; - memset(list, 0, sizeof(struct evdev_list)); list->evdev = evdev_table[i]; list_add_tail(&list->node, &evdev_table[i]->list); @@ -609,9 +608,8 @@ static struct input_handle *evdev_connect(struct input_handler *handler, struct return NULL; } - if (!(evdev = kmalloc(sizeof(struct evdev), GFP_KERNEL))) + if (!(evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL))) return NULL; - memset(evdev, 0, sizeof(struct evdev)); INIT_LIST_HEAD(&evdev->list); init_waitqueue_head(&evdev->wait); diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index b765a155c008..36644bff379d 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c @@ -22,6 +22,7 @@ #include #include #include /* HZ */ +#include /*#include */ @@ -43,10 +44,10 @@ EXPORT_SYMBOL(gameport_start_polling); EXPORT_SYMBOL(gameport_stop_polling); /* - * gameport_sem protects entire gameport subsystem and is taken + * gameport_mutex protects entire gameport subsystem and is taken * every time gameport port or driver registrered or unregistered. */ -static DECLARE_MUTEX(gameport_sem); +static DEFINE_MUTEX(gameport_mutex); static LIST_HEAD(gameport_list); @@ -265,6 +266,7 @@ static void gameport_queue_event(void *object, struct module *owner, if ((event = kmalloc(sizeof(struct gameport_event), GFP_ATOMIC))) { if (!try_module_get(owner)) { printk(KERN_WARNING "gameport: Can't get module reference, dropping event %d\n", event_type); + kfree(event); goto out; } @@ -342,7 +344,7 @@ static void gameport_handle_event(void) struct gameport_event *event; struct gameport_driver *gameport_drv; - down(&gameport_sem); + mutex_lock(&gameport_mutex); /* * Note that we handle only one event here to give swsusp @@ -379,7 +381,7 @@ static void gameport_handle_event(void) gameport_free_event(event); } - up(&gameport_sem); + mutex_unlock(&gameport_mutex); } /* @@ -464,7 +466,7 @@ static ssize_t gameport_rebind_driver(struct device *dev, struct device_attribut struct device_driver *drv; int retval; - retval = down_interruptible(&gameport_sem); + retval = mutex_lock_interruptible(&gameport_mutex); if (retval) return retval; @@ -484,7 +486,7 @@ static ssize_t gameport_rebind_driver(struct device *dev, struct device_attribut retval = -EINVAL; } - up(&gameport_sem); + mutex_unlock(&gameport_mutex); return retval; } @@ -521,7 +523,7 @@ static void gameport_init_port(struct gameport *gameport) __module_get(THIS_MODULE); - init_MUTEX(&gameport->drv_sem); + mutex_init(&gameport->drv_mutex); device_initialize(&gameport->dev); snprintf(gameport->dev.bus_id, sizeof(gameport->dev.bus_id), "gameport%lu", (unsigned long)atomic_inc_return(&gameport_no) - 1); @@ -661,10 +663,10 @@ void __gameport_register_port(struct gameport *gameport, struct module *owner) */ void gameport_unregister_port(struct gameport *gameport) { - down(&gameport_sem); + mutex_lock(&gameport_mutex); gameport_disconnect_port(gameport); gameport_destroy_port(gameport); - up(&gameport_sem); + mutex_unlock(&gameport_mutex); } @@ -717,7 +719,7 @@ void gameport_unregister_driver(struct gameport_driver *drv) { struct gameport *gameport; - down(&gameport_sem); + mutex_lock(&gameport_mutex); drv->ignore = 1; /* so gameport_find_driver ignores it */ start_over: @@ -731,7 +733,7 @@ start_over: } driver_unregister(&drv->driver); - up(&gameport_sem); + mutex_unlock(&gameport_mutex); } static int gameport_bus_match(struct device *dev, struct device_driver *drv) @@ -743,9 +745,9 @@ static int gameport_bus_match(struct device *dev, struct device_driver *drv) static void gameport_set_drv(struct gameport *gameport, struct gameport_driver *drv) { - down(&gameport->drv_sem); + mutex_lock(&gameport->drv_mutex); gameport->drv = drv; - up(&gameport->drv_sem); + mutex_unlock(&gameport->drv_mutex); } int gameport_open(struct gameport *gameport, struct gameport_driver *drv, int mode) @@ -796,5 +798,5 @@ static void __exit gameport_exit(void) kthread_stop(gameport_task); } -module_init(gameport_init); +subsys_initcall(gameport_init); module_exit(gameport_exit); diff --git a/drivers/input/gameport/ns558.c b/drivers/input/gameport/ns558.c index d2e55dc956ba..3e2d28f263e9 100644 --- a/drivers/input/gameport/ns558.c +++ b/drivers/input/gameport/ns558.c @@ -252,14 +252,14 @@ static struct pnp_driver ns558_pnp_driver; #endif -static int pnp_registered = 0; - static int __init ns558_init(void) { int i = 0; + int error; - if (pnp_register_driver(&ns558_pnp_driver) >= 0) - pnp_registered = 1; + error = pnp_register_driver(&ns558_pnp_driver); + if (error && error != -ENODEV) /* should be ENOSYS really */ + return error; /* * Probe ISA ports after PnP, so that PnP ports that are already @@ -270,7 +270,7 @@ static int __init ns558_init(void) while (ns558_isa_portlist[i]) ns558_isa_probe(ns558_isa_portlist[i++]); - return (list_empty(&ns558_list) && !pnp_registered) ? -ENODEV : 0; + return list_empty(&ns558_list) && error ? -ENODEV : 0; } static void __exit ns558_exit(void) @@ -283,8 +283,7 @@ static void __exit ns558_exit(void) kfree(ns558); } - if (pnp_registered) - pnp_unregister_driver(&ns558_pnp_driver); + pnp_unregister_driver(&ns558_pnp_driver); } module_init(ns558_init); diff --git a/drivers/input/input.c b/drivers/input/input.c index f8af0945964e..a935abeffffc 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -18,9 +18,11 @@ #include #include #include +#include #include #include #include +#include MODULE_AUTHOR("Vojtech Pavlik "); MODULE_DESCRIPTION("Input core"); @@ -224,7 +226,7 @@ int input_open_device(struct input_handle *handle) struct input_dev *dev = handle->dev; int err; - err = down_interruptible(&dev->sem); + err = mutex_lock_interruptible(&dev->mutex); if (err) return err; @@ -236,7 +238,7 @@ int input_open_device(struct input_handle *handle) if (err) handle->open--; - up(&dev->sem); + mutex_unlock(&dev->mutex); return err; } @@ -255,13 +257,13 @@ void input_close_device(struct input_handle *handle) input_release_device(handle); - down(&dev->sem); + mutex_lock(&dev->mutex); if (!--dev->users && dev->close) dev->close(dev); handle->open--; - up(&dev->sem); + mutex_unlock(&dev->mutex); } static void input_link_handle(struct input_handle *handle) @@ -315,21 +317,6 @@ static struct input_device_id *input_match_device(struct input_device_id *id, st return NULL; } -static int input_print_bitmap(char *buf, int buf_size, unsigned long *bitmap, int max) -{ - int i; - int len = 0; - - for (i = NBITS(max) - 1; i > 0; i--) - if (bitmap[i]) - break; - - for (; i >= 0; i--) - len += snprintf(buf + len, max(buf_size - len, 0), - "%lx%s", bitmap[i], i > 0 ? " " : ""); - return len; -} - #ifdef CONFIG_PROC_FS static struct proc_dir_entry *proc_bus_input_dir; @@ -342,7 +329,7 @@ static inline void input_wakeup_procfs_readers(void) wake_up(&input_devices_poll_wait); } -static unsigned int input_devices_poll(struct file *file, poll_table *wait) +static unsigned int input_proc_devices_poll(struct file *file, poll_table *wait) { int state = input_devices_state; poll_wait(file, &input_devices_poll_wait, wait); @@ -351,115 +338,171 @@ static unsigned int input_devices_poll(struct file *file, poll_table *wait) return 0; } -#define SPRINTF_BIT(ev, bm) \ - do { \ - len += sprintf(buf + len, "B: %s=", #ev); \ - len += input_print_bitmap(buf + len, INT_MAX, \ - dev->bm##bit, ev##_MAX); \ - len += sprintf(buf + len, "\n"); \ - } while (0) - -#define TEST_AND_SPRINTF_BIT(ev, bm) \ - do { \ - if (test_bit(EV_##ev, dev->evbit)) \ - SPRINTF_BIT(ev, bm); \ - } while (0) - -static int input_devices_read(char *buf, char **start, off_t pos, int count, int *eof, void *data) +static struct list_head *list_get_nth_element(struct list_head *list, loff_t *pos) { - struct input_dev *dev; + struct list_head *node; + loff_t i = 0; + + list_for_each(node, list) + if (i++ == *pos) + return node; + + return NULL; +} + +static struct list_head *list_get_next_element(struct list_head *list, struct list_head *element, loff_t *pos) +{ + if (element->next == list) + return NULL; + + ++(*pos); + return element->next; +} + +static void *input_devices_seq_start(struct seq_file *seq, loff_t *pos) +{ + /* acquire lock here ... Yes, we do need locking, I knowi, I know... */ + + return list_get_nth_element(&input_dev_list, pos); +} + +static void *input_devices_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + return list_get_next_element(&input_dev_list, v, pos); +} + +static void input_devices_seq_stop(struct seq_file *seq, void *v) +{ + /* release lock here */ +} + +static void input_seq_print_bitmap(struct seq_file *seq, const char *name, + unsigned long *bitmap, int max) +{ + int i; + + for (i = NBITS(max) - 1; i > 0; i--) + if (bitmap[i]) + break; + + seq_printf(seq, "B: %s=", name); + for (; i >= 0; i--) + seq_printf(seq, "%lx%s", bitmap[i], i > 0 ? " " : ""); + seq_putc(seq, '\n'); +} + +static int input_devices_seq_show(struct seq_file *seq, void *v) +{ + struct input_dev *dev = container_of(v, struct input_dev, node); + const char *path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL); struct input_handle *handle; - const char *path; - off_t at = 0; - int len, cnt = 0; + seq_printf(seq, "I: Bus=%04x Vendor=%04x Product=%04x Version=%04x\n", + dev->id.bustype, dev->id.vendor, dev->id.product, dev->id.version); - list_for_each_entry(dev, &input_dev_list, node) { + seq_printf(seq, "N: Name=\"%s\"\n", dev->name ? dev->name : ""); + seq_printf(seq, "P: Phys=%s\n", dev->phys ? dev->phys : ""); + seq_printf(seq, "S: Sysfs=%s\n", path ? path : ""); + seq_printf(seq, "H: Handlers="); - path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL); + list_for_each_entry(handle, &dev->h_list, d_node) + seq_printf(seq, "%s ", handle->name); + seq_putc(seq, '\n'); - len = sprintf(buf, "I: Bus=%04x Vendor=%04x Product=%04x Version=%04x\n", - dev->id.bustype, dev->id.vendor, dev->id.product, dev->id.version); + input_seq_print_bitmap(seq, "EV", dev->evbit, EV_MAX); + if (test_bit(EV_KEY, dev->evbit)) + input_seq_print_bitmap(seq, "KEY", dev->keybit, KEY_MAX); + if (test_bit(EV_REL, dev->evbit)) + input_seq_print_bitmap(seq, "REL", dev->relbit, REL_MAX); + if (test_bit(EV_ABS, dev->evbit)) + input_seq_print_bitmap(seq, "ABS", dev->absbit, ABS_MAX); + if (test_bit(EV_MSC, dev->evbit)) + input_seq_print_bitmap(seq, "MSC", dev->mscbit, MSC_MAX); + if (test_bit(EV_LED, dev->evbit)) + input_seq_print_bitmap(seq, "LED", dev->ledbit, LED_MAX); + if (test_bit(EV_SND, dev->evbit)) + input_seq_print_bitmap(seq, "SND", dev->sndbit, SND_MAX); + if (test_bit(EV_FF, dev->evbit)) + input_seq_print_bitmap(seq, "FF", dev->ffbit, FF_MAX); + if (test_bit(EV_SW, dev->evbit)) + input_seq_print_bitmap(seq, "SW", dev->swbit, SW_MAX); - len += sprintf(buf + len, "N: Name=\"%s\"\n", dev->name ? dev->name : ""); - len += sprintf(buf + len, "P: Phys=%s\n", dev->phys ? dev->phys : ""); - len += sprintf(buf + len, "S: Sysfs=%s\n", path ? path : ""); - len += sprintf(buf + len, "H: Handlers="); + seq_putc(seq, '\n'); - list_for_each_entry(handle, &dev->h_list, d_node) - len += sprintf(buf + len, "%s ", handle->name); - - len += sprintf(buf + len, "\n"); - - SPRINTF_BIT(EV, ev); - TEST_AND_SPRINTF_BIT(KEY, key); - TEST_AND_SPRINTF_BIT(REL, rel); - TEST_AND_SPRINTF_BIT(ABS, abs); - TEST_AND_SPRINTF_BIT(MSC, msc); - TEST_AND_SPRINTF_BIT(LED, led); - TEST_AND_SPRINTF_BIT(SND, snd); - TEST_AND_SPRINTF_BIT(FF, ff); - TEST_AND_SPRINTF_BIT(SW, sw); - - len += sprintf(buf + len, "\n"); - - at += len; - - if (at >= pos) { - if (!*start) { - *start = buf + (pos - (at - len)); - cnt = at - pos; - } else cnt += len; - buf += len; - if (cnt >= count) - break; - } - - kfree(path); - } - - if (&dev->node == &input_dev_list) - *eof = 1; - - return (count > cnt) ? cnt : count; + kfree(path); + return 0; } -static int input_handlers_read(char *buf, char **start, off_t pos, int count, int *eof, void *data) +static struct seq_operations input_devices_seq_ops = { + .start = input_devices_seq_start, + .next = input_devices_seq_next, + .stop = input_devices_seq_stop, + .show = input_devices_seq_show, +}; + +static int input_proc_devices_open(struct inode *inode, struct file *file) { - struct input_handler *handler; - - off_t at = 0; - int len = 0, cnt = 0; - int i = 0; - - list_for_each_entry(handler, &input_handler_list, node) { - - if (handler->fops) - len = sprintf(buf, "N: Number=%d Name=%s Minor=%d\n", - i++, handler->name, handler->minor); - else - len = sprintf(buf, "N: Number=%d Name=%s\n", - i++, handler->name); - - at += len; - - if (at >= pos) { - if (!*start) { - *start = buf + (pos - (at - len)); - cnt = at - pos; - } else cnt += len; - buf += len; - if (cnt >= count) - break; - } - } - if (&handler->node == &input_handler_list) - *eof = 1; - - return (count > cnt) ? cnt : count; + return seq_open(file, &input_devices_seq_ops); } -static struct file_operations input_fileops; +static struct file_operations input_devices_fileops = { + .owner = THIS_MODULE, + .open = input_proc_devices_open, + .poll = input_proc_devices_poll, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos) +{ + /* acquire lock here ... Yes, we do need locking, I knowi, I know... */ + seq->private = (void *)(unsigned long)*pos; + return list_get_nth_element(&input_handler_list, pos); +} + +static void *input_handlers_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + seq->private = (void *)(unsigned long)(*pos + 1); + return list_get_next_element(&input_handler_list, v, pos); +} + +static void input_handlers_seq_stop(struct seq_file *seq, void *v) +{ + /* release lock here */ +} + +static int input_handlers_seq_show(struct seq_file *seq, void *v) +{ + struct input_handler *handler = container_of(v, struct input_handler, node); + + seq_printf(seq, "N: Number=%ld Name=%s", + (unsigned long)seq->private, handler->name); + if (handler->fops) + seq_printf(seq, " Minor=%d", handler->minor); + seq_putc(seq, '\n'); + + return 0; +} +static struct seq_operations input_handlers_seq_ops = { + .start = input_handlers_seq_start, + .next = input_handlers_seq_next, + .stop = input_handlers_seq_stop, + .show = input_handlers_seq_show, +}; + +static int input_proc_handlers_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &input_handlers_seq_ops); +} + +static struct file_operations input_handlers_fileops = { + .owner = THIS_MODULE, + .open = input_proc_handlers_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; static int __init input_proc_init(void) { @@ -471,20 +514,19 @@ static int __init input_proc_init(void) proc_bus_input_dir->owner = THIS_MODULE; - entry = create_proc_read_entry("devices", 0, proc_bus_input_dir, input_devices_read, NULL); + entry = create_proc_entry("devices", 0, proc_bus_input_dir); if (!entry) goto fail1; entry->owner = THIS_MODULE; - input_fileops = *entry->proc_fops; - input_fileops.poll = input_devices_poll; - entry->proc_fops = &input_fileops; + entry->proc_fops = &input_devices_fileops; - entry = create_proc_read_entry("handlers", 0, proc_bus_input_dir, input_handlers_read, NULL); + entry = create_proc_entry("handlers", 0, proc_bus_input_dir); if (!entry) goto fail2; entry->owner = THIS_MODULE; + entry->proc_fops = &input_handlers_fileops; return 0; @@ -512,13 +554,14 @@ static ssize_t input_dev_show_##name(struct class_device *dev, char *buf) \ struct input_dev *input_dev = to_input_dev(dev); \ int retval; \ \ - retval = down_interruptible(&input_dev->sem); \ + retval = mutex_lock_interruptible(&input_dev->mutex); \ if (retval) \ return retval; \ \ - retval = sprintf(buf, "%s\n", input_dev->name ? input_dev->name : ""); \ + retval = scnprintf(buf, PAGE_SIZE, \ + "%s\n", input_dev->name ? input_dev->name : ""); \ \ - up(&input_dev->sem); \ + mutex_unlock(&input_dev->mutex); \ \ return retval; \ } \ @@ -528,46 +571,51 @@ INPUT_DEV_STRING_ATTR_SHOW(name); INPUT_DEV_STRING_ATTR_SHOW(phys); INPUT_DEV_STRING_ATTR_SHOW(uniq); -static int print_modalias_bits(char *buf, int size, char prefix, unsigned long *arr, - unsigned int min, unsigned int max) +static int input_print_modalias_bits(char *buf, int size, + char name, unsigned long *bm, + unsigned int min_bit, unsigned int max_bit) { - int len, i; + int len = 0, i; - len = snprintf(buf, size, "%c", prefix); - for (i = min; i < max; i++) - if (arr[LONG(i)] & BIT(i)) - len += snprintf(buf + len, size - len, "%X,", i); + len += snprintf(buf, max(size, 0), "%c", name); + for (i = min_bit; i < max_bit; i++) + if (bm[LONG(i)] & BIT(i)) + len += snprintf(buf + len, max(size - len, 0), "%X,", i); return len; } -static int print_modalias(char *buf, int size, struct input_dev *id) +static int input_print_modalias(char *buf, int size, struct input_dev *id, + int add_cr) { int len; - len = snprintf(buf, size, "input:b%04Xv%04Xp%04Xe%04X-", - id->id.bustype, - id->id.vendor, - id->id.product, - id->id.version); + len = snprintf(buf, max(size, 0), + "input:b%04Xv%04Xp%04Xe%04X-", + id->id.bustype, id->id.vendor, + id->id.product, id->id.version); + + len += input_print_modalias_bits(buf + len, size - len, + 'e', id->evbit, 0, EV_MAX); + len += input_print_modalias_bits(buf + len, size - len, + 'k', id->keybit, KEY_MIN_INTERESTING, KEY_MAX); + len += input_print_modalias_bits(buf + len, size - len, + 'r', id->relbit, 0, REL_MAX); + len += input_print_modalias_bits(buf + len, size - len, + 'a', id->absbit, 0, ABS_MAX); + len += input_print_modalias_bits(buf + len, size - len, + 'm', id->mscbit, 0, MSC_MAX); + len += input_print_modalias_bits(buf + len, size - len, + 'l', id->ledbit, 0, LED_MAX); + len += input_print_modalias_bits(buf + len, size - len, + 's', id->sndbit, 0, SND_MAX); + len += input_print_modalias_bits(buf + len, size - len, + 'f', id->ffbit, 0, FF_MAX); + len += input_print_modalias_bits(buf + len, size - len, + 'w', id->swbit, 0, SW_MAX); + + if (add_cr) + len += snprintf(buf + len, max(size - len, 0), "\n"); - len += print_modalias_bits(buf + len, size - len, 'e', id->evbit, - 0, EV_MAX); - len += print_modalias_bits(buf + len, size - len, 'k', id->keybit, - KEY_MIN_INTERESTING, KEY_MAX); - len += print_modalias_bits(buf + len, size - len, 'r', id->relbit, - 0, REL_MAX); - len += print_modalias_bits(buf + len, size - len, 'a', id->absbit, - 0, ABS_MAX); - len += print_modalias_bits(buf + len, size - len, 'm', id->mscbit, - 0, MSC_MAX); - len += print_modalias_bits(buf + len, size - len, 'l', id->ledbit, - 0, LED_MAX); - len += print_modalias_bits(buf + len, size - len, 's', id->sndbit, - 0, SND_MAX); - len += print_modalias_bits(buf + len, size - len, 'f', id->ffbit, - 0, FF_MAX); - len += print_modalias_bits(buf + len, size - len, 'w', id->swbit, - 0, SW_MAX); return len; } @@ -576,9 +624,9 @@ static ssize_t input_dev_show_modalias(struct class_device *dev, char *buf) struct input_dev *id = to_input_dev(dev); ssize_t len; - len = print_modalias(buf, PAGE_SIZE, id); - len += snprintf(buf + len, PAGE_SIZE-len, "\n"); - return len; + len = input_print_modalias(buf, PAGE_SIZE, id, 1); + + return max_t(int, len, PAGE_SIZE); } static CLASS_DEVICE_ATTR(modalias, S_IRUGO, input_dev_show_modalias, NULL); @@ -598,7 +646,7 @@ static struct attribute_group input_dev_attr_group = { static ssize_t input_dev_show_id_##name(struct class_device *dev, char *buf) \ { \ struct input_dev *input_dev = to_input_dev(dev); \ - return sprintf(buf, "%04x\n", input_dev->id.name); \ + return scnprintf(buf, PAGE_SIZE, "%04x\n", input_dev->id.name); \ } \ static CLASS_DEVICE_ATTR(name, S_IRUGO, input_dev_show_id_##name, NULL); @@ -620,11 +668,33 @@ static struct attribute_group input_dev_id_attr_group = { .attrs = input_dev_id_attrs, }; +static int input_print_bitmap(char *buf, int buf_size, unsigned long *bitmap, + int max, int add_cr) +{ + int i; + int len = 0; + + for (i = NBITS(max) - 1; i > 0; i--) + if (bitmap[i]) + break; + + for (; i >= 0; i--) + len += snprintf(buf + len, max(buf_size - len, 0), + "%lx%s", bitmap[i], i > 0 ? " " : ""); + + if (add_cr) + len += snprintf(buf + len, max(buf_size - len, 0), "\n"); + + return len; +} + #define INPUT_DEV_CAP_ATTR(ev, bm) \ static ssize_t input_dev_show_cap_##bm(struct class_device *dev, char *buf) \ { \ struct input_dev *input_dev = to_input_dev(dev); \ - return input_print_bitmap(buf, PAGE_SIZE, input_dev->bm##bit, ev##_MAX);\ + int len = input_print_bitmap(buf, PAGE_SIZE, \ + input_dev->bm##bit, ev##_MAX, 1); \ + return min_t(int, len, PAGE_SIZE); \ } \ static CLASS_DEVICE_ATTR(bm, S_IRUGO, input_dev_show_cap_##bm, NULL); @@ -669,8 +739,8 @@ static void input_dev_release(struct class_device *class_dev) * device bitfields. */ static int input_add_uevent_bm_var(char **envp, int num_envp, int *cur_index, - char *buffer, int buffer_size, int *cur_len, - const char *name, unsigned long *bitmap, int max) + char *buffer, int buffer_size, int *cur_len, + const char *name, unsigned long *bitmap, int max) { if (*cur_index >= num_envp - 1) return -ENOMEM; @@ -678,12 +748,36 @@ static int input_add_uevent_bm_var(char **envp, int num_envp, int *cur_index, envp[*cur_index] = buffer + *cur_len; *cur_len += snprintf(buffer + *cur_len, max(buffer_size - *cur_len, 0), name); - if (*cur_len > buffer_size) + if (*cur_len >= buffer_size) return -ENOMEM; *cur_len += input_print_bitmap(buffer + *cur_len, max(buffer_size - *cur_len, 0), - bitmap, max) + 1; + bitmap, max, 0) + 1; + if (*cur_len > buffer_size) + return -ENOMEM; + + (*cur_index)++; + return 0; +} + +static int input_add_uevent_modalias_var(char **envp, int num_envp, int *cur_index, + char *buffer, int buffer_size, int *cur_len, + struct input_dev *dev) +{ + if (*cur_index >= num_envp - 1) + return -ENOMEM; + + envp[*cur_index] = buffer + *cur_len; + + *cur_len += snprintf(buffer + *cur_len, max(buffer_size - *cur_len, 0), + "MODALIAS="); + if (*cur_len >= buffer_size) + return -ENOMEM; + + *cur_len += input_print_modalias(buffer + *cur_len, + max(buffer_size - *cur_len, 0), + dev, 0) + 1; if (*cur_len > buffer_size) return -ENOMEM; @@ -693,7 +787,7 @@ static int input_add_uevent_bm_var(char **envp, int num_envp, int *cur_index, #define INPUT_ADD_HOTPLUG_VAR(fmt, val...) \ do { \ - int err = add_uevent_var(envp, num_envp, &i, \ + int err = add_uevent_var(envp, num_envp, &i, \ buffer, buffer_size, &len, \ fmt, val); \ if (err) \ @@ -709,6 +803,16 @@ static int input_add_uevent_bm_var(char **envp, int num_envp, int *cur_index, return err; \ } while (0) +#define INPUT_ADD_HOTPLUG_MODALIAS_VAR(dev) \ + do { \ + int err = input_add_uevent_modalias_var(envp, \ + num_envp, &i, \ + buffer, buffer_size, &len, \ + dev); \ + if (err) \ + return err; \ + } while (0) + static int input_dev_uevent(struct class_device *cdev, char **envp, int num_envp, char *buffer, int buffer_size) { @@ -744,9 +848,7 @@ static int input_dev_uevent(struct class_device *cdev, char **envp, if (test_bit(EV_SW, dev->evbit)) INPUT_ADD_HOTPLUG_BM_VAR("SW=", dev->swbit, SW_MAX); - envp[i++] = buffer + len; - len += snprintf(buffer + len, buffer_size - len, "MODALIAS="); - len += print_modalias(buffer + len, buffer_size - len, dev) + 1; + INPUT_ADD_HOTPLUG_MODALIAS_VAR(dev); envp[i] = NULL; return 0; @@ -790,7 +892,7 @@ int input_register_device(struct input_dev *dev) return -EINVAL; } - init_MUTEX(&dev->sem); + mutex_init(&dev->mutex); set_bit(EV_SYN, dev->evbit); /* diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index 20e2972b9204..949bdcef8c2b 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -171,9 +171,8 @@ static int joydev_open(struct inode *inode, struct file *file) if (i >= JOYDEV_MINORS || !joydev_table[i]) return -ENODEV; - if (!(list = kmalloc(sizeof(struct joydev_list), GFP_KERNEL))) + if (!(list = kzalloc(sizeof(struct joydev_list), GFP_KERNEL))) return -ENOMEM; - memset(list, 0, sizeof(struct joydev_list)); list->joydev = joydev_table[i]; list_add_tail(&list->node, &joydev_table[i]->list); @@ -457,9 +456,8 @@ static struct input_handle *joydev_connect(struct input_handler *handler, struct return NULL; } - if (!(joydev = kmalloc(sizeof(struct joydev), GFP_KERNEL))) + if (!(joydev = kzalloc(sizeof(struct joydev), GFP_KERNEL))) return NULL; - memset(joydev, 0, sizeof(struct joydev)); INIT_LIST_HEAD(&joydev->list); init_waitqueue_head(&joydev->wait); diff --git a/drivers/input/joystick/amijoy.c b/drivers/input/joystick/amijoy.c index ec55a29fc861..7249d324297b 100644 --- a/drivers/input/joystick/amijoy.c +++ b/drivers/input/joystick/amijoy.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -52,7 +53,7 @@ MODULE_PARM_DESC(map, "Map of attached joysticks in form of , (default is __obsolete_setup("amijoy="); static int amijoy_used; -static DECLARE_MUTEX(amijoy_sem); +static DEFINE_MUTEX(amijoy_mutex); static struct input_dev *amijoy_dev[2]; static char *amijoy_phys[2] = { "amijoy/input0", "amijoy/input1" }; @@ -85,7 +86,7 @@ static int amijoy_open(struct input_dev *dev) { int err; - err = down_interruptible(&amijoy_sem); + err = mutex_lock_interruptible(&amijoy_mutex); if (err) return err; @@ -97,16 +98,16 @@ static int amijoy_open(struct input_dev *dev) amijoy_used++; out: - up(&amijoy_sem); + mutex_unlock(&amijoy_mutex); return err; } static void amijoy_close(struct input_dev *dev) { - down(&amijoy_sem); + mutex_lock(&amijoy_mutex); if (!--amijoy_used) free_irq(IRQ_AMIGA_VERTB, amijoy_interrupt); - up(&amijoy_sem); + mutex_unlock(&amijoy_mutex); } static int __init amijoy_init(void) diff --git a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c index dcffc34f30c3..e61894685cb1 100644 --- a/drivers/input/joystick/db9.c +++ b/drivers/input/joystick/db9.c @@ -38,6 +38,7 @@ #include #include #include +#include MODULE_AUTHOR("Vojtech Pavlik "); MODULE_DESCRIPTION("Atari, Amstrad, Commodore, Amiga, Sega, etc. joystick driver"); @@ -111,7 +112,7 @@ struct db9 { struct pardevice *pd; int mode; int used; - struct semaphore sem; + struct mutex mutex; char phys[DB9_MAX_DEVICES][32]; }; @@ -525,7 +526,7 @@ static int db9_open(struct input_dev *dev) struct parport *port = db9->pd->port; int err; - err = down_interruptible(&db9->sem); + err = mutex_lock_interruptible(&db9->mutex); if (err) return err; @@ -539,7 +540,7 @@ static int db9_open(struct input_dev *dev) mod_timer(&db9->timer, jiffies + DB9_REFRESH_TIME); } - up(&db9->sem); + mutex_unlock(&db9->mutex); return 0; } @@ -548,14 +549,14 @@ static void db9_close(struct input_dev *dev) struct db9 *db9 = dev->private; struct parport *port = db9->pd->port; - down(&db9->sem); + mutex_lock(&db9->mutex); if (!--db9->used) { del_timer_sync(&db9->timer); parport_write_control(port, 0x00); parport_data_forward(port); parport_release(db9->pd); } - up(&db9->sem); + mutex_unlock(&db9->mutex); } static struct db9 __init *db9_probe(int parport, int mode) @@ -603,7 +604,7 @@ static struct db9 __init *db9_probe(int parport, int mode) goto err_unreg_pardev; } - init_MUTEX(&db9->sem); + mutex_init(&db9->mutex); db9->pd = pd; db9->mode = mode; init_timer(&db9->timer); diff --git a/drivers/input/joystick/gamecon.c b/drivers/input/joystick/gamecon.c index 900587acdb47..ecbdb6b9bbd6 100644 --- a/drivers/input/joystick/gamecon.c +++ b/drivers/input/joystick/gamecon.c @@ -7,6 +7,7 @@ * Based on the work of: * Andree Borrmann John Dahlstrom * David Kuder Nathan Hand + * Raphael Assenat */ /* @@ -36,6 +37,7 @@ #include #include #include +#include MODULE_AUTHOR("Vojtech Pavlik "); MODULE_DESCRIPTION("NES, SNES, N64, MultiSystem, PSX gamepad driver"); @@ -72,8 +74,9 @@ __obsolete_setup("gc_3="); #define GC_N64 6 #define GC_PSX 7 #define GC_DDR 8 +#define GC_SNESMOUSE 9 -#define GC_MAX 8 +#define GC_MAX 9 #define GC_REFRESH_TIME HZ/100 @@ -83,7 +86,7 @@ struct gc { struct timer_list timer; unsigned char pads[GC_MAX + 1]; int used; - struct semaphore sem; + struct mutex mutex; char phys[GC_MAX_DEVICES][32]; }; @@ -93,7 +96,7 @@ static int gc_status_bit[] = { 0x40, 0x80, 0x20, 0x10, 0x08 }; static char *gc_names[] = { NULL, "SNES pad", "NES pad", "NES FourPort", "Multisystem joystick", "Multisystem 2-button joystick", "N64 controller", "PSX controller", - "PSX DDR controller" }; + "PSX DDR controller", "SNES mouse" }; /* * N64 support. */ @@ -205,9 +208,12 @@ static void gc_n64_process_packet(struct gc *gc) * NES/SNES support. */ -#define GC_NES_DELAY 6 /* Delay between bits - 6us */ -#define GC_NES_LENGTH 8 /* The NES pads use 8 bits of data */ -#define GC_SNES_LENGTH 12 /* The SNES true length is 16, but the last 4 bits are unused */ +#define GC_NES_DELAY 6 /* Delay between bits - 6us */ +#define GC_NES_LENGTH 8 /* The NES pads use 8 bits of data */ +#define GC_SNES_LENGTH 12 /* The SNES true length is 16, but the + last 4 bits are unused */ +#define GC_SNESMOUSE_LENGTH 32 /* The SNES mouse uses 32 bits, the first + 16 bits are equivalent to a gamepad */ #define GC_NES_POWER 0xfc #define GC_NES_CLOCK 0x01 @@ -242,11 +248,15 @@ static void gc_nes_read_packet(struct gc *gc, int length, unsigned char *data) static void gc_nes_process_packet(struct gc *gc) { - unsigned char data[GC_SNES_LENGTH]; + unsigned char data[GC_SNESMOUSE_LENGTH]; struct input_dev *dev; - int i, j, s; + int i, j, s, len; + char x_rel, y_rel; - gc_nes_read_packet(gc, gc->pads[GC_SNES] ? GC_SNES_LENGTH : GC_NES_LENGTH, data); + len = gc->pads[GC_SNESMOUSE] ? GC_SNESMOUSE_LENGTH : + (gc->pads[GC_SNES] ? GC_SNES_LENGTH : GC_NES_LENGTH); + + gc_nes_read_packet(gc, len, data); for (i = 0; i < GC_MAX_DEVICES; i++) { @@ -269,6 +279,44 @@ static void gc_nes_process_packet(struct gc *gc) for (j = 0; j < 8; j++) input_report_key(dev, gc_snes_btn[j], s & data[gc_snes_bytes[j]]); + if (s & gc->pads[GC_SNESMOUSE]) { + /* + * The 4 unused bits from SNES controllers appear to be ID bits + * so use them to make sure iwe are dealing with a mouse. + * gamepad is connected. This is important since + * my SNES gamepad sends 1's for bits 16-31, which + * cause the mouse pointer to quickly move to the + * upper left corner of the screen. + */ + if (!(s & data[12]) && !(s & data[13]) && + !(s & data[14]) && (s & data[15])) { + input_report_key(dev, BTN_LEFT, s & data[9]); + input_report_key(dev, BTN_RIGHT, s & data[8]); + + x_rel = y_rel = 0; + for (j = 0; j < 7; j++) { + x_rel <<= 1; + if (data[25 + j] & s) + x_rel |= 1; + + y_rel <<= 1; + if (data[17 + j] & s) + y_rel |= 1; + } + + if (x_rel) { + if (data[24] & s) + x_rel = -x_rel; + input_report_rel(dev, REL_X, x_rel); + } + + if (y_rel) { + if (data[16] & s) + y_rel = -y_rel; + input_report_rel(dev, REL_Y, y_rel); + } + } + } input_sync(dev); } } @@ -524,10 +572,10 @@ static void gc_timer(unsigned long private) gc_n64_process_packet(gc); /* - * NES and SNES pads + * NES and SNES pads or mouse */ - if (gc->pads[GC_NES] || gc->pads[GC_SNES]) + if (gc->pads[GC_NES] || gc->pads[GC_SNES] || gc->pads[GC_SNESMOUSE]) gc_nes_process_packet(gc); /* @@ -552,7 +600,7 @@ static int gc_open(struct input_dev *dev) struct gc *gc = dev->private; int err; - err = down_interruptible(&gc->sem); + err = mutex_lock_interruptible(&gc->mutex); if (err) return err; @@ -562,7 +610,7 @@ static int gc_open(struct input_dev *dev) mod_timer(&gc->timer, jiffies + GC_REFRESH_TIME); } - up(&gc->sem); + mutex_unlock(&gc->mutex); return 0; } @@ -570,13 +618,13 @@ static void gc_close(struct input_dev *dev) { struct gc *gc = dev->private; - down(&gc->sem); + mutex_lock(&gc->mutex); if (!--gc->used) { del_timer_sync(&gc->timer); parport_write_control(gc->pd->port, 0x00); parport_release(gc->pd); } - up(&gc->sem); + mutex_unlock(&gc->mutex); } static int __init gc_setup_pad(struct gc *gc, int idx, int pad_type) @@ -609,10 +657,13 @@ static int __init gc_setup_pad(struct gc *gc, int idx, int pad_type) input_dev->open = gc_open; input_dev->close = gc_close; - input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + if (pad_type != GC_SNESMOUSE) { + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); - for (i = 0; i < 2; i++) - input_set_abs_params(input_dev, ABS_X + i, -1, 1, 0, 0); + for (i = 0; i < 2; i++) + input_set_abs_params(input_dev, ABS_X + i, -1, 1, 0, 0); + } else + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); gc->pads[0] |= gc_status_bit[idx]; gc->pads[pad_type] |= gc_status_bit[idx]; @@ -630,6 +681,13 @@ static int __init gc_setup_pad(struct gc *gc, int idx, int pad_type) break; + case GC_SNESMOUSE: + set_bit(BTN_LEFT, input_dev->keybit); + set_bit(BTN_RIGHT, input_dev->keybit); + set_bit(REL_X, input_dev->relbit); + set_bit(REL_Y, input_dev->relbit); + break; + case GC_SNES: for (i = 4; i < 8; i++) set_bit(gc_snes_btn[i], input_dev->keybit); @@ -693,7 +751,7 @@ static struct gc __init *gc_probe(int parport, int *pads, int n_pads) goto err_unreg_pardev; } - init_MUTEX(&gc->sem); + mutex_init(&gc->mutex); gc->pd = pd; init_timer(&gc->timer); gc->timer.data = (long) gc; diff --git a/drivers/input/joystick/iforce/iforce-ff.c b/drivers/input/joystick/iforce/iforce-ff.c index 4678b6dab43b..2b8e8456c9fa 100644 --- a/drivers/input/joystick/iforce/iforce-ff.c +++ b/drivers/input/joystick/iforce/iforce-ff.c @@ -42,14 +42,14 @@ static int make_magnitude_modifier(struct iforce* iforce, unsigned char data[3]; if (!no_alloc) { - down(&iforce->mem_mutex); + mutex_lock(&iforce->mem_mutex); if (allocate_resource(&(iforce->device_memory), mod_chunk, 2, iforce->device_memory.start, iforce->device_memory.end, 2L, NULL, NULL)) { - up(&iforce->mem_mutex); + mutex_unlock(&iforce->mem_mutex); return -ENOMEM; } - up(&iforce->mem_mutex); + mutex_unlock(&iforce->mem_mutex); } data[0] = LO(mod_chunk->start); @@ -75,14 +75,14 @@ static int make_period_modifier(struct iforce* iforce, period = TIME_SCALE(period); if (!no_alloc) { - down(&iforce->mem_mutex); + mutex_lock(&iforce->mem_mutex); if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0c, iforce->device_memory.start, iforce->device_memory.end, 2L, NULL, NULL)) { - up(&iforce->mem_mutex); + mutex_unlock(&iforce->mem_mutex); return -ENOMEM; } - up(&iforce->mem_mutex); + mutex_unlock(&iforce->mem_mutex); } data[0] = LO(mod_chunk->start); @@ -115,14 +115,14 @@ static int make_envelope_modifier(struct iforce* iforce, fade_duration = TIME_SCALE(fade_duration); if (!no_alloc) { - down(&iforce->mem_mutex); + mutex_lock(&iforce->mem_mutex); if (allocate_resource(&(iforce->device_memory), mod_chunk, 0x0e, iforce->device_memory.start, iforce->device_memory.end, 2L, NULL, NULL)) { - up(&iforce->mem_mutex); + mutex_unlock(&iforce->mem_mutex); return -ENOMEM; } - up(&iforce->mem_mutex); + mutex_unlock(&iforce->mem_mutex); } data[0] = LO(mod_chunk->start); @@ -152,14 +152,14 @@ static int make_condition_modifier(struct iforce* iforce, unsigned char data[10]; if (!no_alloc) { - down(&iforce->mem_mutex); + mutex_lock(&iforce->mem_mutex); if (allocate_resource(&(iforce->device_memory), mod_chunk, 8, iforce->device_memory.start, iforce->device_memory.end, 2L, NULL, NULL)) { - up(&iforce->mem_mutex); + mutex_unlock(&iforce->mem_mutex); return -ENOMEM; } - up(&iforce->mem_mutex); + mutex_unlock(&iforce->mem_mutex); } data[0] = LO(mod_chunk->start); diff --git a/drivers/input/joystick/iforce/iforce-main.c b/drivers/input/joystick/iforce/iforce-main.c index b6bc04998047..ab0a26b924ca 100644 --- a/drivers/input/joystick/iforce/iforce-main.c +++ b/drivers/input/joystick/iforce/iforce-main.c @@ -350,7 +350,7 @@ int iforce_init_device(struct iforce *iforce) init_waitqueue_head(&iforce->wait); spin_lock_init(&iforce->xmit_lock); - init_MUTEX(&iforce->mem_mutex); + mutex_init(&iforce->mem_mutex); iforce->xmit.buf = iforce->xmit_data; iforce->dev = input_dev; diff --git a/drivers/input/joystick/iforce/iforce.h b/drivers/input/joystick/iforce/iforce.h index 146f406b8f8a..668f24535ba0 100644 --- a/drivers/input/joystick/iforce/iforce.h +++ b/drivers/input/joystick/iforce/iforce.h @@ -37,7 +37,7 @@ #include #include #include -#include +#include /* This module provides arbitrary resource management routines. * I use it to manage the device's memory. @@ -45,6 +45,7 @@ */ #include + #define IFORCE_MAX_LENGTH 16 /* iforce::bus */ @@ -146,7 +147,7 @@ struct iforce { wait_queue_head_t wait; struct resource device_memory; struct iforce_core_effect core_effects[FF_EFFECTS_MAX]; - struct semaphore mem_mutex; + struct mutex mem_mutex; }; /* Get hi and low bytes of a 16-bits int */ diff --git a/drivers/input/joystick/turbografx.c b/drivers/input/joystick/turbografx.c index b154938e88a4..5570fd5487c7 100644 --- a/drivers/input/joystick/turbografx.c +++ b/drivers/input/joystick/turbografx.c @@ -37,6 +37,7 @@ #include #include #include +#include MODULE_AUTHOR("Vojtech Pavlik "); MODULE_DESCRIPTION("TurboGraFX parallel port interface driver"); @@ -86,7 +87,7 @@ static struct tgfx { char phys[TGFX_MAX_DEVICES][32]; int sticks; int used; - struct semaphore sem; + struct mutex sem; } *tgfx_base[TGFX_MAX_PORTS]; /* @@ -128,7 +129,7 @@ static int tgfx_open(struct input_dev *dev) struct tgfx *tgfx = dev->private; int err; - err = down_interruptible(&tgfx->sem); + err = mutex_lock_interruptible(&tgfx->sem); if (err) return err; @@ -138,7 +139,7 @@ static int tgfx_open(struct input_dev *dev) mod_timer(&tgfx->timer, jiffies + TGFX_REFRESH_TIME); } - up(&tgfx->sem); + mutex_unlock(&tgfx->sem); return 0; } @@ -146,13 +147,13 @@ static void tgfx_close(struct input_dev *dev) { struct tgfx *tgfx = dev->private; - down(&tgfx->sem); + mutex_lock(&tgfx->sem); if (!--tgfx->used) { del_timer_sync(&tgfx->timer); parport_write_control(tgfx->pd->port, 0x00); parport_release(tgfx->pd); } - up(&tgfx->sem); + mutex_unlock(&tgfx->sem); } @@ -191,7 +192,7 @@ static struct tgfx __init *tgfx_probe(int parport, int *n_buttons, int n_devs) goto err_unreg_pardev; } - init_MUTEX(&tgfx->sem); + mutex_init(&tgfx->sem); tgfx->pd = pd; init_timer(&tgfx->timer); tgfx->timer.data = (long) tgfx; diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 3b0ac3b43c54..a9dda56f62c4 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -13,7 +13,7 @@ menuconfig INPUT_KEYBOARD if INPUT_KEYBOARD config KEYBOARD_ATKBD - tristate "AT keyboard" if !X86_PC + tristate "AT keyboard" if EMBEDDED || !X86_PC default y select SERIO select SERIO_LIBPS2 diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index ffacf6eca5f5..fad04b66d268 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -27,6 +27,7 @@ #include #include #include +#include #define DRIVER_DESC "AT and PS/2 keyboard driver" @@ -216,7 +217,7 @@ struct atkbd { unsigned long time; struct work_struct event_work; - struct semaphore event_sem; + struct mutex event_mutex; unsigned long event_mask; }; @@ -302,19 +303,19 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data, if (atkbd->translated) { if (atkbd->emul || - !(code == ATKBD_RET_EMUL0 || code == ATKBD_RET_EMUL1 || - code == ATKBD_RET_HANGUEL || code == ATKBD_RET_HANJA || - (code == ATKBD_RET_ERR && !atkbd->err_xl) || - (code == ATKBD_RET_BAT && !atkbd->bat_xl))) { + (code != ATKBD_RET_EMUL0 && code != ATKBD_RET_EMUL1 && + code != ATKBD_RET_HANGUEL && code != ATKBD_RET_HANJA && + (code != ATKBD_RET_ERR || atkbd->err_xl) && + (code != ATKBD_RET_BAT || atkbd->bat_xl))) { atkbd->release = code >> 7; code &= 0x7f; } if (!atkbd->emul) { if ((code & 0x7f) == (ATKBD_RET_BAT & 0x7f)) - atkbd->bat_xl = !atkbd->release; + atkbd->bat_xl = !(data >> 7); if ((code & 0x7f) == (ATKBD_RET_ERR & 0x7f)) - atkbd->err_xl = !atkbd->release; + atkbd->err_xl = !(data >> 7); } } @@ -449,7 +450,7 @@ static void atkbd_event_work(void *data) unsigned char param[2]; int i, j; - down(&atkbd->event_sem); + mutex_lock(&atkbd->event_mutex); if (test_and_clear_bit(ATKBD_LED_EVENT_BIT, &atkbd->event_mask)) { param[0] = (test_bit(LED_SCROLLL, dev->led) ? 1 : 0) @@ -480,7 +481,7 @@ static void atkbd_event_work(void *data) ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_SETREP); } - up(&atkbd->event_sem); + mutex_unlock(&atkbd->event_mutex); } /* @@ -846,7 +847,7 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv) atkbd->dev = dev; ps2_init(&atkbd->ps2dev, serio); INIT_WORK(&atkbd->event_work, atkbd_event_work, atkbd); - init_MUTEX(&atkbd->event_sem); + mutex_init(&atkbd->event_mutex); switch (serio->id.type) { @@ -862,9 +863,6 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv) atkbd->softrepeat = atkbd_softrepeat; atkbd->scroll = atkbd_scroll; - if (!atkbd->write) - atkbd->softrepeat = 1; - if (atkbd->softrepeat) atkbd->softraw = 1; diff --git a/drivers/input/keyboard/corgikbd.c b/drivers/input/keyboard/corgikbd.c index e301ee4ca264..96c6bf77248a 100644 --- a/drivers/input/keyboard/corgikbd.c +++ b/drivers/input/keyboard/corgikbd.c @@ -29,11 +29,11 @@ #define KB_COLS 12 #define KB_ROWMASK(r) (1 << (r)) #define SCANCODE(r,c) ( ((r)<<4) + (c) + 1 ) -/* zero code, 124 scancodes + 3 hinge combinations */ -#define NR_SCANCODES ( SCANCODE(KB_ROWS-1,KB_COLS-1) +1 +1 +3 ) -#define SCAN_INTERVAL (HZ/10) +/* zero code, 124 scancodes */ +#define NR_SCANCODES ( SCANCODE(KB_ROWS-1,KB_COLS-1) +1 +1 ) -#define HINGE_SCAN_INTERVAL (HZ/4) +#define SCAN_INTERVAL (50) /* ms */ +#define HINGE_SCAN_INTERVAL (250) /* ms */ #define CORGI_KEY_CALENDER KEY_F1 #define CORGI_KEY_ADDRESS KEY_F2 @@ -49,9 +49,6 @@ #define CORGI_KEY_MAIL KEY_F10 #define CORGI_KEY_OK KEY_F11 #define CORGI_KEY_MENU KEY_F12 -#define CORGI_HINGE_0 KEY_KP0 -#define CORGI_HINGE_1 KEY_KP1 -#define CORGI_HINGE_2 KEY_KP2 static unsigned char corgikbd_keycode[NR_SCANCODES] = { 0, /* 0 */ @@ -63,7 +60,6 @@ static unsigned char corgikbd_keycode[NR_SCANCODES] = { CORGI_KEY_MAIL, KEY_Z, KEY_X, KEY_MINUS, KEY_SPACE, KEY_COMMA, 0, KEY_UP, 0, 0, 0, CORGI_KEY_FN, 0, 0, 0, 0, /* 81-96 */ KEY_SYSRQ, CORGI_KEY_JAP1, CORGI_KEY_JAP2, CORGI_KEY_CANCEL, CORGI_KEY_OK, CORGI_KEY_MENU, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, 0, 0, /* 97-112 */ CORGI_KEY_OFF, CORGI_KEY_EXOK, CORGI_KEY_EXCANCEL, CORGI_KEY_EXJOGDOWN, CORGI_KEY_EXJOGUP, 0, 0, 0, 0, 0, 0, 0, /* 113-124 */ - CORGI_HINGE_0, CORGI_HINGE_1, CORGI_HINGE_2 /* 125-127 */ }; @@ -187,7 +183,7 @@ static void corgikbd_scankeyboard(struct corgikbd *corgikbd_data, struct pt_regs /* if any keys are pressed, enable the timer */ if (num_pressed) - mod_timer(&corgikbd_data->timer, jiffies + SCAN_INTERVAL); + mod_timer(&corgikbd_data->timer, jiffies + msecs_to_jiffies(SCAN_INTERVAL)); spin_unlock_irqrestore(&corgikbd_data->lock, flags); } @@ -228,6 +224,7 @@ static void corgikbd_timer_callback(unsigned long data) * 0x0c - Keyboard and Screen Closed */ +#define READ_GPIO_BIT(x) (GPLR(x) & GPIO_bit(x)) #define HINGE_STABLE_COUNT 2 static int sharpsl_hinge_state; static int hinge_count; @@ -239,6 +236,7 @@ static void corgikbd_hinge_timer(unsigned long data) unsigned long flags; gprr = read_scoop_reg(&corgiscoop_device.dev, SCOOP_GPRR) & (CORGI_SCP_SWA | CORGI_SCP_SWB); + gprr |= (READ_GPIO_BIT(CORGI_GPIO_AK_INT) != 0); if (gprr != sharpsl_hinge_state) { hinge_count = 0; sharpsl_hinge_state = gprr; @@ -249,27 +247,38 @@ static void corgikbd_hinge_timer(unsigned long data) input_report_switch(corgikbd_data->input, SW_0, ((sharpsl_hinge_state & CORGI_SCP_SWA) != 0)); input_report_switch(corgikbd_data->input, SW_1, ((sharpsl_hinge_state & CORGI_SCP_SWB) != 0)); + input_report_switch(corgikbd_data->input, SW_2, (READ_GPIO_BIT(CORGI_GPIO_AK_INT) != 0)); input_sync(corgikbd_data->input); spin_unlock_irqrestore(&corgikbd_data->lock, flags); } } - mod_timer(&corgikbd_data->htimer, jiffies + HINGE_SCAN_INTERVAL); + mod_timer(&corgikbd_data->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL)); } #ifdef CONFIG_PM static int corgikbd_suspend(struct platform_device *dev, pm_message_t state) { + int i; struct corgikbd *corgikbd = platform_get_drvdata(dev); + corgikbd->suspended = 1; + /* strobe 0 is the power key so this can't be made an input for + powersaving therefore i = 1 */ + for (i = 1; i < CORGI_KEY_STROBE_NUM; i++) + pxa_gpio_mode(CORGI_GPIO_KEY_STROBE(i) | GPIO_IN); return 0; } static int corgikbd_resume(struct platform_device *dev) { + int i; struct corgikbd *corgikbd = platform_get_drvdata(dev); + for (i = 1; i < CORGI_KEY_STROBE_NUM; i++) + pxa_gpio_mode(CORGI_GPIO_KEY_STROBE(i) | GPIO_OUT | GPIO_DFLT_HIGH); + /* Upon resume, ignore the suspend key for a short while */ corgikbd->suspend_jiffies=jiffies; corgikbd->suspended = 0; @@ -333,10 +342,11 @@ static int __init corgikbd_probe(struct platform_device *pdev) clear_bit(0, input_dev->keybit); set_bit(SW_0, input_dev->swbit); set_bit(SW_1, input_dev->swbit); + set_bit(SW_2, input_dev->swbit); input_register_device(corgikbd->input); - mod_timer(&corgikbd->htimer, jiffies + HINGE_SCAN_INTERVAL); + mod_timer(&corgikbd->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL)); /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */ for (i = 0; i < CORGI_KEY_SENSE_NUM; i++) { @@ -351,6 +361,9 @@ static int __init corgikbd_probe(struct platform_device *pdev) for (i = 0; i < CORGI_KEY_STROBE_NUM; i++) pxa_gpio_mode(CORGI_GPIO_KEY_STROBE(i) | GPIO_OUT | GPIO_DFLT_HIGH); + /* Setup the headphone jack as an input */ + pxa_gpio_mode(CORGI_GPIO_AK_INT | GPIO_IN); + return 0; } diff --git a/drivers/input/keyboard/hil_kbd.c b/drivers/input/keyboard/hil_kbd.c index 0a90962c38e7..1dca3cf42a54 100644 --- a/drivers/input/keyboard/hil_kbd.c +++ b/drivers/input/keyboard/hil_kbd.c @@ -66,7 +66,7 @@ static unsigned int hil_kbd_set3[HIL_KEYCODES_SET3_TBLSIZE] = static char hil_language[][16] = { HIL_LOCALE_MAP }; struct hil_kbd { - struct input_dev dev; + struct input_dev *dev; struct serio *serio; /* Input buffer and index for packets from HIL bus. */ @@ -86,7 +86,7 @@ struct hil_kbd { /* Process a complete packet after transfer from the HIL */ static void hil_kbd_process_record(struct hil_kbd *kbd) { - struct input_dev *dev = &kbd->dev; + struct input_dev *dev = kbd->dev; hil_packet *data = kbd->data; hil_packet p; int idx, i, cnt; @@ -240,8 +240,8 @@ static void hil_kbd_disconnect(struct serio *serio) return; } - input_unregister_device(&kbd->dev); serio_close(serio); + input_unregister_device(kbd->dev); kfree(kbd); } @@ -250,17 +250,22 @@ static int hil_kbd_connect(struct serio *serio, struct serio_driver *drv) struct hil_kbd *kbd; uint8_t did, *idd; int i; - - kbd = kmalloc(sizeof(*kbd), GFP_KERNEL); + + kbd = kzalloc(sizeof(*kbd), GFP_KERNEL); if (!kbd) return -ENOMEM; - memset(kbd, 0, sizeof(struct hil_kbd)); - if (serio_open(serio, drv)) goto bail0; + kbd->dev = input_allocate_device(); + if (!kbd->dev) + goto bail0; + + kbd->dev->private = kbd; + + if (serio_open(serio, drv)) + goto bail1; serio_set_drvdata(serio, kbd); kbd->serio = serio; - kbd->dev.private = kbd; init_MUTEX_LOCKED(&(kbd->sem)); @@ -302,38 +307,38 @@ static int hil_kbd_connect(struct serio *serio, struct serio_driver *drv) did, hil_language[did & HIL_IDD_DID_TYPE_KB_LANG_MASK]); break; default: - goto bail1; + goto bail2; } if(HIL_IDD_NUM_BUTTONS(idd) || HIL_IDD_NUM_AXES_PER_SET(*idd)) { printk(KERN_INFO PREFIX "keyboards only, no combo devices supported.\n"); - goto bail1; + goto bail2; } - kbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP); - kbd->dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL); - kbd->dev.keycodemax = HIL_KEYCODES_SET1_TBLSIZE; - kbd->dev.keycodesize = sizeof(hil_kbd_set1[0]); - kbd->dev.keycode = hil_kbd_set1; - kbd->dev.name = strlen(kbd->rnm) ? kbd->rnm : HIL_GENERIC_NAME; - kbd->dev.phys = "hpkbd/input0"; /* XXX */ + kbd->dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); + kbd->dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL); + kbd->dev->keycodemax = HIL_KEYCODES_SET1_TBLSIZE; + kbd->dev->keycodesize = sizeof(hil_kbd_set1[0]); + kbd->dev->keycode = hil_kbd_set1; + kbd->dev->name = strlen(kbd->rnm) ? kbd->rnm : HIL_GENERIC_NAME; + kbd->dev->phys = "hpkbd/input0"; /* XXX */ - kbd->dev.id.bustype = BUS_HIL; - kbd->dev.id.vendor = PCI_VENDOR_ID_HP; - kbd->dev.id.product = 0x0001; /* TODO: get from kbd->rsc */ - kbd->dev.id.version = 0x0100; /* TODO: get from kbd->rsc */ - kbd->dev.dev = &serio->dev; + kbd->dev->id.bustype = BUS_HIL; + kbd->dev->id.vendor = PCI_VENDOR_ID_HP; + kbd->dev->id.product = 0x0001; /* TODO: get from kbd->rsc */ + kbd->dev->id.version = 0x0100; /* TODO: get from kbd->rsc */ + kbd->dev->dev = &serio->dev; for (i = 0; i < 128; i++) { - set_bit(hil_kbd_set1[i], kbd->dev.keybit); - set_bit(hil_kbd_set3[i], kbd->dev.keybit); + set_bit(hil_kbd_set1[i], kbd->dev->keybit); + set_bit(hil_kbd_set3[i], kbd->dev->keybit); } - clear_bit(0, kbd->dev.keybit); + clear_bit(0, kbd->dev->keybit); - input_register_device(&kbd->dev); + input_register_device(kbd->dev); printk(KERN_INFO "input: %s, ID: %d\n", - kbd->dev.name, did); + kbd->dev->name, did); serio->write(serio, 0); serio->write(serio, 0); @@ -343,8 +348,10 @@ static int hil_kbd_connect(struct serio *serio, struct serio_driver *drv) up(&(kbd->sem)); return 0; - bail1: + bail2: serio_close(serio); + bail1: + input_free_device(kbd->dev); bail0: kfree(kbd); serio_set_drvdata(serio, NULL); diff --git a/drivers/input/keyboard/hilkbd.c b/drivers/input/keyboard/hilkbd.c index e95bc052e32a..33edd030aa75 100644 --- a/drivers/input/keyboard/hilkbd.c +++ b/drivers/input/keyboard/hilkbd.c @@ -3,7 +3,7 @@ * * Copyright (C) 1998 Philip Blundell * Copyright (C) 1999 Matthew Wilcox - * Copyright (C) 1999-2003 Helge Deller + * Copyright (C) 1999-2006 Helge Deller * * Very basic HP Human Interface Loop (HIL) driver. * This driver handles the keyboard on HP300 (m68k) and on some @@ -90,7 +90,7 @@ static unsigned int hphilkeyb_keycode[HIL_KEYCODES_SET1_TBLSIZE] = /* HIL structure */ static struct { - struct input_dev dev; + struct input_dev *dev; unsigned int curdev; @@ -117,7 +117,7 @@ static void poll_finished(void) down = (hil_dev.data[1] & 1) == 0; scode = hil_dev.data[1] >> 1; key = hphilkeyb_keycode[scode]; - input_report_key(&hil_dev.dev, key, down); + input_report_key(hil_dev.dev, key, down); break; } hil_dev.curdev = 0; @@ -207,9 +207,14 @@ hil_keyb_init(void) unsigned int i, kbid; wait_queue_head_t hil_wait; - if (hil_dev.dev.id.bustype) { + if (hil_dev.dev) { return -ENODEV; /* already initialized */ } + + hil_dev.dev = input_allocate_device(); + if (!hil_dev.dev) + return -ENOMEM; + hil_dev.dev->private = &hil_dev; #if defined(CONFIG_HP300) if (!hwreg_present((void *)(HILBASE + HIL_DATA))) @@ -247,28 +252,26 @@ hil_keyb_init(void) c = 0; hil_do(HIL_WRITEKBDSADR, &c, 1); - init_input_dev(&hil_dev.dev); - for (i = 0; i < HIL_KEYCODES_SET1_TBLSIZE; i++) if (hphilkeyb_keycode[i] != KEY_RESERVED) - set_bit(hphilkeyb_keycode[i], hil_dev.dev.keybit); + set_bit(hphilkeyb_keycode[i], hil_dev.dev->keybit); - hil_dev.dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP); - hil_dev.dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL); - hil_dev.dev.keycodemax = HIL_KEYCODES_SET1_TBLSIZE; - hil_dev.dev.keycodesize = sizeof(hphilkeyb_keycode[0]); - hil_dev.dev.keycode = hphilkeyb_keycode; - hil_dev.dev.name = "HIL keyboard"; - hil_dev.dev.phys = "hpkbd/input0"; + hil_dev.dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); + hil_dev.dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL); + hil_dev.dev->keycodemax = HIL_KEYCODES_SET1_TBLSIZE; + hil_dev.dev->keycodesize = sizeof(hphilkeyb_keycode[0]); + hil_dev.dev->keycode = hphilkeyb_keycode; + hil_dev.dev->name = "HIL keyboard"; + hil_dev.dev->phys = "hpkbd/input0"; - hil_dev.dev.id.bustype = BUS_HIL; - hil_dev.dev.id.vendor = PCI_VENDOR_ID_HP; - hil_dev.dev.id.product = 0x0001; - hil_dev.dev.id.version = 0x0010; + hil_dev.dev->id.bustype = BUS_HIL; + hil_dev.dev->id.vendor = PCI_VENDOR_ID_HP; + hil_dev.dev->id.product = 0x0001; + hil_dev.dev->id.version = 0x0010; - input_register_device(&hil_dev.dev); + input_register_device(hil_dev.dev); printk(KERN_INFO "input: %s, ID %d at 0x%08lx (irq %d) found and attached\n", - hil_dev.dev.name, kbid, HILBASE, HIL_IRQ); + hil_dev.dev->name, kbid, HILBASE, HIL_IRQ); return 0; } @@ -329,7 +332,9 @@ static void __exit hil_exit(void) /* Turn off interrupts */ hil_do(HIL_INTOFF, NULL, 0); - input_unregister_device(&hil_dev.dev); + input_unregister_device(hil_dev.dev); + + hil_dev.dev = NULL; #if defined(CONFIG_PARISC) unregister_parisc_driver(&hil_driver); diff --git a/drivers/input/keyboard/spitzkbd.c b/drivers/input/keyboard/spitzkbd.c index 83999d583122..bc61cf8cfc65 100644 --- a/drivers/input/keyboard/spitzkbd.c +++ b/drivers/input/keyboard/spitzkbd.c @@ -30,6 +30,7 @@ #define SCANCODE(r,c) (((r)<<4) + (c) + 1) #define NR_SCANCODES ((KB_ROWS<<4) + 1) +#define SCAN_INTERVAL (50) /* ms */ #define HINGE_SCAN_INTERVAL (150) /* ms */ #define SPITZ_KEY_CALENDER KEY_F1 @@ -230,7 +231,7 @@ static void spitzkbd_scankeyboard(struct spitzkbd *spitzkbd_data, struct pt_regs /* if any keys are pressed, enable the timer */ if (num_pressed) - mod_timer(&spitzkbd_data->timer, jiffies + msecs_to_jiffies(100)); + mod_timer(&spitzkbd_data->timer, jiffies + msecs_to_jiffies(SCAN_INTERVAL)); spin_unlock_irqrestore(&spitzkbd_data->lock, flags); } @@ -287,6 +288,7 @@ static void spitzkbd_hinge_timer(unsigned long data) unsigned long flags; state = GPLR(SPITZ_GPIO_SWA) & (GPIO_bit(SPITZ_GPIO_SWA)|GPIO_bit(SPITZ_GPIO_SWB)); + state |= (GPLR(SPITZ_GPIO_AK_INT) & GPIO_bit(SPITZ_GPIO_AK_INT)); if (state != sharpsl_hinge_state) { hinge_count = 0; sharpsl_hinge_state = state; @@ -299,6 +301,7 @@ static void spitzkbd_hinge_timer(unsigned long data) input_report_switch(spitzkbd_data->input, SW_0, ((GPLR(SPITZ_GPIO_SWA) & GPIO_bit(SPITZ_GPIO_SWA)) != 0)); input_report_switch(spitzkbd_data->input, SW_1, ((GPLR(SPITZ_GPIO_SWB) & GPIO_bit(SPITZ_GPIO_SWB)) != 0)); + input_report_switch(spitzkbd_data->input, SW_2, ((GPLR(SPITZ_GPIO_AK_INT) & GPIO_bit(SPITZ_GPIO_AK_INT)) != 0)); input_sync(spitzkbd_data->input); spin_unlock_irqrestore(&spitzkbd_data->lock, flags); @@ -397,6 +400,7 @@ static int __init spitzkbd_probe(struct platform_device *dev) clear_bit(0, input_dev->keybit); set_bit(SW_0, input_dev->swbit); set_bit(SW_1, input_dev->swbit); + set_bit(SW_2, input_dev->swbit); input_register_device(input_dev); @@ -432,6 +436,9 @@ static int __init spitzkbd_probe(struct platform_device *dev) request_irq(SPITZ_IRQ_GPIO_SWB, spitzkbd_hinge_isr, SA_INTERRUPT | SA_TRIGGER_RISING | SA_TRIGGER_FALLING, "Spitzkbd SWB", spitzkbd); + request_irq(SPITZ_IRQ_GPIO_AK_INT, spitzkbd_hinge_isr, + SA_INTERRUPT | SA_TRIGGER_RISING | SA_TRIGGER_FALLING, + "Spitzkbd HP", spitzkbd); printk(KERN_INFO "input: Spitz Keyboard Registered\n"); @@ -450,6 +457,7 @@ static int spitzkbd_remove(struct platform_device *dev) free_irq(SPITZ_IRQ_GPIO_ON_KEY, spitzkbd); free_irq(SPITZ_IRQ_GPIO_SWA, spitzkbd); free_irq(SPITZ_IRQ_GPIO_SWB, spitzkbd); + free_irq(SPITZ_IRQ_GPIO_AK_INT, spitzkbd); del_timer_sync(&spitzkbd->htimer); del_timer_sync(&spitzkbd->timer); diff --git a/drivers/input/misc/pcspkr.c b/drivers/input/misc/pcspkr.c index 1ef477f4469c..afd322185bbf 100644 --- a/drivers/input/misc/pcspkr.c +++ b/drivers/input/misc/pcspkr.c @@ -24,7 +24,6 @@ MODULE_AUTHOR("Vojtech Pavlik "); MODULE_DESCRIPTION("PC Speaker beeper driver"); MODULE_LICENSE("GPL"); -static struct platform_device *pcspkr_platform_device; static DEFINE_SPINLOCK(i8253_beep_lock); static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) @@ -135,35 +134,11 @@ static struct platform_driver pcspkr_platform_driver = { static int __init pcspkr_init(void) { - int err; - - err = platform_driver_register(&pcspkr_platform_driver); - if (err) - return err; - - pcspkr_platform_device = platform_device_alloc("pcspkr", -1); - if (!pcspkr_platform_device) { - err = -ENOMEM; - goto err_unregister_driver; - } - - err = platform_device_add(pcspkr_platform_device); - if (err) - goto err_free_device; - - return 0; - - err_free_device: - platform_device_put(pcspkr_platform_device); - err_unregister_driver: - platform_driver_unregister(&pcspkr_platform_driver); - - return err; + return platform_driver_register(&pcspkr_platform_driver); } static void __exit pcspkr_exit(void) { - platform_device_unregister(pcspkr_platform_device); platform_driver_unregister(&pcspkr_platform_driver); } diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 546ed9b4901d..d723e9ad7c41 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -194,7 +194,7 @@ static int uinput_open(struct inode *inode, struct file *file) if (!newdev) return -ENOMEM; - init_MUTEX(&newdev->sem); + mutex_init(&newdev->mutex); spin_lock_init(&newdev->requests_lock); init_waitqueue_head(&newdev->requests_waitq); init_waitqueue_head(&newdev->waitq); @@ -340,7 +340,7 @@ static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t struct uinput_device *udev = file->private_data; int retval; - retval = down_interruptible(&udev->sem); + retval = mutex_lock_interruptible(&udev->mutex); if (retval) return retval; @@ -348,7 +348,7 @@ static ssize_t uinput_write(struct file *file, const char __user *buffer, size_t uinput_inject_event(udev, buffer, count) : uinput_setup_device(udev, buffer, count); - up(&udev->sem); + mutex_unlock(&udev->mutex); return retval; } @@ -369,7 +369,7 @@ static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count, if (retval) return retval; - retval = down_interruptible(&udev->sem); + retval = mutex_lock_interruptible(&udev->mutex); if (retval) return retval; @@ -388,7 +388,7 @@ static ssize_t uinput_read(struct file *file, char __user *buffer, size_t count, } out: - up(&udev->sem); + mutex_unlock(&udev->mutex); return retval; } @@ -439,7 +439,7 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) udev = file->private_data; - retval = down_interruptible(&udev->sem); + retval = mutex_lock_interruptible(&udev->mutex); if (retval) return retval; @@ -589,7 +589,7 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } out: - up(&udev->sem); + mutex_unlock(&udev->mutex); return retval; } diff --git a/drivers/input/mouse/hil_ptr.c b/drivers/input/mouse/hil_ptr.c index c2bf2ed07dc6..69f02178c528 100644 --- a/drivers/input/mouse/hil_ptr.c +++ b/drivers/input/mouse/hil_ptr.c @@ -55,7 +55,7 @@ MODULE_LICENSE("Dual BSD/GPL"); #define HIL_PTR_MAX_LENGTH 16 struct hil_ptr { - struct input_dev dev; + struct input_dev *dev; struct serio *serio; /* Input buffer and index for packets from HIL bus. */ @@ -79,7 +79,7 @@ struct hil_ptr { /* Process a complete packet after transfer from the HIL */ static void hil_ptr_process_record(struct hil_ptr *ptr) { - struct input_dev *dev = &ptr->dev; + struct input_dev *dev = ptr->dev; hil_packet *data = ptr->data; hil_packet p; int idx, i, cnt, laxis; @@ -148,12 +148,12 @@ static void hil_ptr_process_record(struct hil_ptr *ptr) if (absdev) { val = lo + (hi<<8); #ifdef TABLET_AUTOADJUST - if (val < ptr->dev.absmin[ABS_X + i]) - ptr->dev.absmin[ABS_X + i] = val; - if (val > ptr->dev.absmax[ABS_X + i]) - ptr->dev.absmax[ABS_X + i] = val; + if (val < dev->absmin[ABS_X + i]) + dev->absmin[ABS_X + i] = val; + if (val > dev->absmax[ABS_X + i]) + dev->absmax[ABS_X + i] = val; #endif - if (i%3) val = ptr->dev.absmax[ABS_X + i] - val; + if (i%3) val = dev->absmax[ABS_X + i] - val; input_report_abs(dev, ABS_X + i, val); } else { val = (int) (((int8_t)lo) | ((int8_t)hi<<8)); @@ -233,26 +233,32 @@ static void hil_ptr_disconnect(struct serio *serio) return; } - input_unregister_device(&ptr->dev); serio_close(serio); + input_unregister_device(ptr->dev); kfree(ptr); } static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver) { - struct hil_ptr *ptr; - char *txt; - unsigned int i, naxsets, btntype; - uint8_t did, *idd; + struct hil_ptr *ptr; + char *txt; + unsigned int i, naxsets, btntype; + uint8_t did, *idd; - if (!(ptr = kmalloc(sizeof(struct hil_ptr), GFP_KERNEL))) return -ENOMEM; - memset(ptr, 0, sizeof(struct hil_ptr)); + if (!(ptr = kzalloc(sizeof(struct hil_ptr), GFP_KERNEL))) + return -ENOMEM; - if (serio_open(serio, driver)) goto bail0; + ptr->dev = input_allocate_device(); + if (!ptr->dev) + goto bail0; + + ptr->dev->private = ptr; + + if (serio_open(serio, driver)) + goto bail1; serio_set_drvdata(serio, ptr); ptr->serio = serio; - ptr->dev.private = ptr; init_MUTEX_LOCKED(&(ptr->sem)); @@ -283,25 +289,24 @@ static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver) up(&(ptr->sem)); - init_input_dev(&ptr->dev); did = ptr->idd[0]; idd = ptr->idd + 1; txt = "unknown"; if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_REL) { - ptr->dev.evbit[0] = BIT(EV_REL); + ptr->dev->evbit[0] = BIT(EV_REL); txt = "relative"; } if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_ABS) { - ptr->dev.evbit[0] = BIT(EV_ABS); + ptr->dev->evbit[0] = BIT(EV_ABS); txt = "absolute"; } - if (!ptr->dev.evbit[0]) { - goto bail1; + if (!ptr->dev->evbit[0]) { + goto bail2; } ptr->nbtn = HIL_IDD_NUM_BUTTONS(idd); - if (ptr->nbtn) ptr->dev.evbit[0] |= BIT(EV_KEY); + if (ptr->nbtn) ptr->dev->evbit[0] |= BIT(EV_KEY); naxsets = HIL_IDD_NUM_AXSETS(*idd); ptr->naxes = HIL_IDD_NUM_AXES_PER_SET(*idd); @@ -325,7 +330,7 @@ static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver) btntype = BTN_MOUSE; for (i = 0; i < ptr->nbtn; i++) { - set_bit(btntype | i, ptr->dev.keybit); + set_bit(btntype | i, ptr->dev->keybit); ptr->btnmap[i] = btntype | i; } @@ -337,50 +342,52 @@ static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver) if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_REL) { for (i = 0; i < ptr->naxes; i++) { - set_bit(REL_X + i, ptr->dev.relbit); + set_bit(REL_X + i, ptr->dev->relbit); } for (i = 3; (i < ptr->naxes + 3) && (naxsets > 1); i++) { - set_bit(REL_X + i, ptr->dev.relbit); + set_bit(REL_X + i, ptr->dev->relbit); } } else { for (i = 0; i < ptr->naxes; i++) { - set_bit(ABS_X + i, ptr->dev.absbit); - ptr->dev.absmin[ABS_X + i] = 0; - ptr->dev.absmax[ABS_X + i] = + set_bit(ABS_X + i, ptr->dev->absbit); + ptr->dev->absmin[ABS_X + i] = 0; + ptr->dev->absmax[ABS_X + i] = HIL_IDD_AXIS_MAX((ptr->idd + 1), i); } for (i = 3; (i < ptr->naxes + 3) && (naxsets > 1); i++) { - set_bit(ABS_X + i, ptr->dev.absbit); - ptr->dev.absmin[ABS_X + i] = 0; - ptr->dev.absmax[ABS_X + i] = + set_bit(ABS_X + i, ptr->dev->absbit); + ptr->dev->absmin[ABS_X + i] = 0; + ptr->dev->absmax[ABS_X + i] = HIL_IDD_AXIS_MAX((ptr->idd + 1), (i - 3)); } #ifdef TABLET_AUTOADJUST for (i = 0; i < ABS_MAX; i++) { - int diff = ptr->dev.absmax[ABS_X + i] / 10; - ptr->dev.absmin[ABS_X + i] += diff; - ptr->dev.absmax[ABS_X + i] -= diff; + int diff = ptr->dev->absmax[ABS_X + i] / 10; + ptr->dev->absmin[ABS_X + i] += diff; + ptr->dev->absmax[ABS_X + i] -= diff; } #endif } - ptr->dev.name = strlen(ptr->rnm) ? ptr->rnm : HIL_GENERIC_NAME; + ptr->dev->name = strlen(ptr->rnm) ? ptr->rnm : HIL_GENERIC_NAME; - ptr->dev.id.bustype = BUS_HIL; - ptr->dev.id.vendor = PCI_VENDOR_ID_HP; - ptr->dev.id.product = 0x0001; /* TODO: get from ptr->rsc */ - ptr->dev.id.version = 0x0100; /* TODO: get from ptr->rsc */ - ptr->dev.dev = &serio->dev; + ptr->dev->id.bustype = BUS_HIL; + ptr->dev->id.vendor = PCI_VENDOR_ID_HP; + ptr->dev->id.product = 0x0001; /* TODO: get from ptr->rsc */ + ptr->dev->id.version = 0x0100; /* TODO: get from ptr->rsc */ + ptr->dev->dev = &serio->dev; - input_register_device(&ptr->dev); + input_register_device(ptr->dev); printk(KERN_INFO "input: %s (%s), ID: %d\n", - ptr->dev.name, + ptr->dev->name, (btntype == BTN_MOUSE) ? "HIL mouse":"HIL tablet or touchpad", did); return 0; - bail1: + bail2: serio_close(serio); + bail1: + input_free_device(ptr->dev); bail0: kfree(ptr); serio_set_drvdata(serio, NULL); diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index ad6217467676..32d70ed8f41d 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -20,6 +20,8 @@ #include #include #include +#include + #include "psmouse.h" #include "synaptics.h" #include "logips2pp.h" @@ -98,13 +100,13 @@ __obsolete_setup("psmouse_resetafter="); __obsolete_setup("psmouse_rate="); /* - * psmouse_sem protects all operations changing state of mouse + * psmouse_mutex protects all operations changing state of mouse * (connecting, disconnecting, changing rate or resolution via * sysfs). We could use a per-device semaphore but since there * rarely more than one PS/2 mouse connected and since semaphore * is taken in "slow" paths it is not worth it. */ -static DECLARE_MUTEX(psmouse_sem); +static DEFINE_MUTEX(psmouse_mutex); static struct workqueue_struct *kpsmoused_wq; @@ -868,7 +870,7 @@ static void psmouse_resync(void *p) int failed = 0, enabled = 0; int i; - down(&psmouse_sem); + mutex_lock(&psmouse_mutex); if (psmouse->state != PSMOUSE_RESYNCING) goto out; @@ -948,7 +950,7 @@ static void psmouse_resync(void *p) if (parent) psmouse_activate(parent); out: - up(&psmouse_sem); + mutex_unlock(&psmouse_mutex); } /* @@ -974,14 +976,14 @@ static void psmouse_disconnect(struct serio *serio) sysfs_remove_group(&serio->dev.kobj, &psmouse_attribute_group); - down(&psmouse_sem); + mutex_lock(&psmouse_mutex); psmouse_set_state(psmouse, PSMOUSE_CMD_MODE); /* make sure we don't have a resync in progress */ - up(&psmouse_sem); + mutex_unlock(&psmouse_mutex); flush_workqueue(kpsmoused_wq); - down(&psmouse_sem); + mutex_lock(&psmouse_mutex); if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { parent = serio_get_drvdata(serio->parent); @@ -1004,7 +1006,7 @@ static void psmouse_disconnect(struct serio *serio) if (parent) psmouse_activate(parent); - up(&psmouse_sem); + mutex_unlock(&psmouse_mutex); } static int psmouse_switch_protocol(struct psmouse *psmouse, struct psmouse_protocol *proto) @@ -1076,7 +1078,7 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv) struct input_dev *input_dev; int retval = -ENOMEM; - down(&psmouse_sem); + mutex_lock(&psmouse_mutex); /* * If this is a pass-through port deactivate parent so the device @@ -1144,7 +1146,7 @@ out: if (parent) psmouse_activate(parent); - up(&psmouse_sem); + mutex_unlock(&psmouse_mutex); return retval; } @@ -1161,7 +1163,7 @@ static int psmouse_reconnect(struct serio *serio) return -1; } - down(&psmouse_sem); + mutex_lock(&psmouse_mutex); if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { parent = serio_get_drvdata(serio->parent); @@ -1195,7 +1197,7 @@ out: if (parent) psmouse_activate(parent); - up(&psmouse_sem); + mutex_unlock(&psmouse_mutex); return rc; } @@ -1273,7 +1275,7 @@ ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *dev goto out_unpin; } - retval = down_interruptible(&psmouse_sem); + retval = mutex_lock_interruptible(&psmouse_mutex); if (retval) goto out_unpin; @@ -1281,7 +1283,7 @@ ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *dev if (psmouse->state == PSMOUSE_IGNORE) { retval = -ENODEV; - goto out_up; + goto out_unlock; } if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) { @@ -1299,8 +1301,8 @@ ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *dev if (parent) psmouse_activate(parent); - out_up: - up(&psmouse_sem); + out_unlock: + mutex_unlock(&psmouse_mutex); out_unpin: serio_unpin_driver(serio); return retval; @@ -1357,11 +1359,11 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co return -EIO; } - up(&psmouse_sem); + mutex_unlock(&psmouse_mutex); serio_unpin_driver(serio); serio_unregister_child_port(serio); serio_pin_driver_uninterruptible(serio); - down(&psmouse_sem); + mutex_lock(&psmouse_mutex); if (serio->drv != &psmouse_drv) { input_free_device(new_dev); diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 2051bec2c394..ad5d0a85e960 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -247,14 +247,12 @@ static void synaptics_pt_create(struct psmouse *psmouse) { struct serio *serio; - serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); if (!serio) { printk(KERN_ERR "synaptics: not enough memory to allocate pass-through port\n"); return; } - memset(serio, 0, sizeof(struct serio)); - serio->id.type = SERIO_PS_PSTHRU; strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name)); strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name)); @@ -605,14 +603,21 @@ static struct dmi_system_id toshiba_dmi_table[] = { .ident = "Toshiba Satellite", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), - DMI_MATCH(DMI_PRODUCT_NAME , "Satellite"), + DMI_MATCH(DMI_PRODUCT_NAME, "Satellite"), }, }, { .ident = "Toshiba Dynabook", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), - DMI_MATCH(DMI_PRODUCT_NAME , "dynabook"), + DMI_MATCH(DMI_PRODUCT_NAME, "dynabook"), + }, + }, + { + .ident = "Toshiba Portege M300", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE M300"), }, }, { } @@ -623,10 +628,9 @@ int synaptics_init(struct psmouse *psmouse) { struct synaptics_data *priv; - psmouse->private = priv = kmalloc(sizeof(struct synaptics_data), GFP_KERNEL); + psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL); if (!priv) return -1; - memset(priv, 0, sizeof(struct synaptics_data)); if (synaptics_query_hardware(psmouse)) { printk(KERN_ERR "Unable to query Synaptics hardware.\n"); diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c index 9abed18d2ecf..b685a507955d 100644 --- a/drivers/input/mousedev.c +++ b/drivers/input/mousedev.c @@ -412,9 +412,8 @@ static int mousedev_open(struct inode * inode, struct file * file) if (i >= MOUSEDEV_MINORS || !mousedev_table[i]) return -ENODEV; - if (!(list = kmalloc(sizeof(struct mousedev_list), GFP_KERNEL))) + if (!(list = kzalloc(sizeof(struct mousedev_list), GFP_KERNEL))) return -ENOMEM; - memset(list, 0, sizeof(struct mousedev_list)); spin_lock_init(&list->packet_lock); list->pos_x = xres / 2; @@ -626,9 +625,8 @@ static struct input_handle *mousedev_connect(struct input_handler *handler, stru return NULL; } - if (!(mousedev = kmalloc(sizeof(struct mousedev), GFP_KERNEL))) + if (!(mousedev = kzalloc(sizeof(struct mousedev), GFP_KERNEL))) return NULL; - memset(mousedev, 0, sizeof(struct mousedev)); INIT_LIST_HEAD(&mousedev->list); init_waitqueue_head(&mousedev->wait); diff --git a/drivers/input/power.c b/drivers/input/power.c index bfc5c63ebffe..526e6070600c 100644 --- a/drivers/input/power.c +++ b/drivers/input/power.c @@ -103,9 +103,8 @@ static struct input_handle *power_connect(struct input_handler *handler, { struct input_handle *handle; - if (!(handle = kmalloc(sizeof(struct input_handle), GFP_KERNEL))) + if (!(handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL))) return NULL; - memset(handle, 0, sizeof(struct input_handle)); handle->dev = dev; handle->handler = handler; diff --git a/drivers/input/serio/gscps2.c b/drivers/input/serio/gscps2.c index a7b0de0f92b2..c0b1e4becad3 100644 --- a/drivers/input/serio/gscps2.c +++ b/drivers/input/serio/gscps2.c @@ -1,7 +1,7 @@ /* * drivers/input/serio/gscps2.c * - * Copyright (c) 2004 Helge Deller + * Copyright (c) 2004-2006 Helge Deller * Copyright (c) 2002 Laurent Canet * Copyright (c) 2002 Thibaut Varene * @@ -354,7 +354,7 @@ static int __init gscps2_probe(struct parisc_device *dev) memset(serio, 0, sizeof(struct serio)); ps2port->port = serio; ps2port->padev = dev; - ps2port->addr = ioremap(hpa, GSC_STATUS + 4); + ps2port->addr = ioremap_nocache(hpa, GSC_STATUS + 4); spin_lock_init(&ps2port->lock); gscps2_reset(ps2port); diff --git a/drivers/input/serio/hil_mlc.c b/drivers/input/serio/hil_mlc.c index ea499783fb12..bbbe15e21904 100644 --- a/drivers/input/serio/hil_mlc.c +++ b/drivers/input/serio/hil_mlc.c @@ -872,9 +872,8 @@ int hil_mlc_register(hil_mlc *mlc) { for (i = 0; i < HIL_MLC_DEVMEM; i++) { struct serio *mlc_serio; hil_mlc_copy_di_scratch(mlc, i); - mlc_serio = kmalloc(sizeof(*mlc_serio), GFP_KERNEL); + mlc_serio = kzalloc(sizeof(*mlc_serio), GFP_KERNEL); mlc->serio[i] = mlc_serio; - memset(mlc_serio, 0, sizeof(*mlc_serio)); mlc_serio->id = hil_mlc_serio_id; mlc_serio->write = hil_mlc_serio_write; mlc_serio->open = hil_mlc_serio_open; diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index a4c6f3522723..f606e96bc2f4 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -192,7 +192,9 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = { #include static int i8042_pnp_kbd_registered; +static unsigned int i8042_pnp_kbd_devices; static int i8042_pnp_aux_registered; +static unsigned int i8042_pnp_aux_devices; static int i8042_pnp_command_reg; static int i8042_pnp_data_reg; @@ -219,6 +221,7 @@ static int i8042_pnp_kbd_probe(struct pnp_dev *dev, const struct pnp_device_id * strncat(i8042_pnp_kbd_name, pnp_dev_name(dev), sizeof(i8042_pnp_kbd_name)); } + i8042_pnp_kbd_devices++; return 0; } @@ -239,6 +242,7 @@ static int i8042_pnp_aux_probe(struct pnp_dev *dev, const struct pnp_device_id * strncat(i8042_pnp_aux_name, pnp_dev_name(dev), sizeof(i8042_pnp_aux_name)); } + i8042_pnp_aux_devices++; return 0; } @@ -287,21 +291,23 @@ static void i8042_pnp_exit(void) static int __init i8042_pnp_init(void) { - int result_kbd = 0, result_aux = 0; char kbd_irq_str[4] = { 0 }, aux_irq_str[4] = { 0 }; + int err; if (i8042_nopnp) { printk(KERN_INFO "i8042: PNP detection disabled\n"); return 0; } - if ((result_kbd = pnp_register_driver(&i8042_pnp_kbd_driver)) >= 0) + err = pnp_register_driver(&i8042_pnp_kbd_driver); + if (!err) i8042_pnp_kbd_registered = 1; - if ((result_aux = pnp_register_driver(&i8042_pnp_aux_driver)) >= 0) + err = pnp_register_driver(&i8042_pnp_aux_driver); + if (!err) i8042_pnp_aux_registered = 1; - if (result_kbd <= 0 && result_aux <= 0) { + if (!i8042_pnp_kbd_devices && !i8042_pnp_aux_devices) { i8042_pnp_exit(); #if defined(__ia64__) return -ENODEV; @@ -311,24 +317,24 @@ static int __init i8042_pnp_init(void) #endif } - if (result_kbd > 0) + if (i8042_pnp_kbd_devices) snprintf(kbd_irq_str, sizeof(kbd_irq_str), "%d", i8042_pnp_kbd_irq); - if (result_aux > 0) + if (i8042_pnp_aux_devices) snprintf(aux_irq_str, sizeof(aux_irq_str), "%d", i8042_pnp_aux_irq); printk(KERN_INFO "PNP: PS/2 Controller [%s%s%s] at %#x,%#x irq %s%s%s\n", - i8042_pnp_kbd_name, (result_kbd > 0 && result_aux > 0) ? "," : "", + i8042_pnp_kbd_name, (i8042_pnp_kbd_devices && i8042_pnp_aux_devices) ? "," : "", i8042_pnp_aux_name, i8042_pnp_data_reg, i8042_pnp_command_reg, - kbd_irq_str, (result_kbd > 0 && result_aux > 0) ? "," : "", + kbd_irq_str, (i8042_pnp_kbd_devices && i8042_pnp_aux_devices) ? "," : "", aux_irq_str); #if defined(__ia64__) - if (result_kbd <= 0) + if (!i8042_pnp_kbd_devices) i8042_nokbd = 1; - if (result_aux <= 0) + if (!i8042_pnp_aux_devices) i8042_noaux = 1; #endif diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index d4c990f7c85e..79c97f94bcbd 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -84,7 +84,7 @@ void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout) maxbytes = sizeof(ps2dev->cmdbuf); } - down(&ps2dev->cmd_sem); + mutex_lock(&ps2dev->cmd_mutex); serio_pause_rx(ps2dev->serio); ps2dev->flags = PS2_FLAG_CMD; @@ -94,7 +94,7 @@ void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout) wait_event_timeout(ps2dev->wait, !(ps2dev->flags & PS2_FLAG_CMD), msecs_to_jiffies(timeout)); - up(&ps2dev->cmd_sem); + mutex_unlock(&ps2dev->cmd_mutex); } /* @@ -177,7 +177,7 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) return -1; } - down(&ps2dev->cmd_sem); + mutex_lock(&ps2dev->cmd_mutex); serio_pause_rx(ps2dev->serio); ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0; @@ -229,7 +229,7 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command) ps2dev->flags = 0; serio_continue_rx(ps2dev->serio); - up(&ps2dev->cmd_sem); + mutex_unlock(&ps2dev->cmd_mutex); return rc; } @@ -281,7 +281,7 @@ int ps2_schedule_command(struct ps2dev *ps2dev, unsigned char *param, int comman void ps2_init(struct ps2dev *ps2dev, struct serio *serio) { - init_MUTEX(&ps2dev->cmd_sem); + mutex_init(&ps2dev->cmd_mutex); init_waitqueue_head(&ps2dev->wait); ps2dev->serio = serio; } diff --git a/drivers/input/serio/parkbd.c b/drivers/input/serio/parkbd.c index 1d15c2819818..a5c1fb3a4a51 100644 --- a/drivers/input/serio/parkbd.c +++ b/drivers/input/serio/parkbd.c @@ -171,9 +171,8 @@ static struct serio * __init parkbd_allocate_serio(void) { struct serio *serio; - serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); if (serio) { - memset(serio, 0, sizeof(struct serio)); serio->id.type = parkbd_mode; serio->write = parkbd_write, strlcpy(serio->name, "PARKBD AT/XT keyboard adapter", sizeof(serio->name)); diff --git a/drivers/input/serio/rpckbd.c b/drivers/input/serio/rpckbd.c index a3bd11589bc3..513d37fc1acf 100644 --- a/drivers/input/serio/rpckbd.c +++ b/drivers/input/serio/rpckbd.c @@ -111,11 +111,10 @@ static int __devinit rpckbd_probe(struct platform_device *dev) { struct serio *serio; - serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); if (!serio) return -ENOMEM; - memset(serio, 0, sizeof(struct serio)); serio->id.type = SERIO_8042; serio->write = rpckbd_write; serio->open = rpckbd_open; diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index 2f76813c3a64..6521034bc933 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -34,6 +34,7 @@ #include #include #include +#include MODULE_AUTHOR("Vojtech Pavlik "); MODULE_DESCRIPTION("Serio abstraction core"); @@ -52,10 +53,10 @@ EXPORT_SYMBOL(serio_rescan); EXPORT_SYMBOL(serio_reconnect); /* - * serio_sem protects entire serio subsystem and is taken every time + * serio_mutex protects entire serio subsystem and is taken every time * serio port or driver registrered or unregistered. */ -static DECLARE_MUTEX(serio_sem); +static DEFINE_MUTEX(serio_mutex); static LIST_HEAD(serio_list); @@ -70,9 +71,9 @@ static int serio_connect_driver(struct serio *serio, struct serio_driver *drv) { int retval; - down(&serio->drv_sem); + mutex_lock(&serio->drv_mutex); retval = drv->connect(serio, drv); - up(&serio->drv_sem); + mutex_unlock(&serio->drv_mutex); return retval; } @@ -81,20 +82,20 @@ static int serio_reconnect_driver(struct serio *serio) { int retval = -1; - down(&serio->drv_sem); + mutex_lock(&serio->drv_mutex); if (serio->drv && serio->drv->reconnect) retval = serio->drv->reconnect(serio); - up(&serio->drv_sem); + mutex_unlock(&serio->drv_mutex); return retval; } static void serio_disconnect_driver(struct serio *serio) { - down(&serio->drv_sem); + mutex_lock(&serio->drv_mutex); if (serio->drv) serio->drv->disconnect(serio); - up(&serio->drv_sem); + mutex_unlock(&serio->drv_mutex); } static int serio_match_port(const struct serio_device_id *ids, struct serio *serio) @@ -195,6 +196,7 @@ static void serio_queue_event(void *object, struct module *owner, if ((event = kmalloc(sizeof(struct serio_event), GFP_ATOMIC))) { if (!try_module_get(owner)) { printk(KERN_WARNING "serio: Can't get module reference, dropping event %d\n", event_type); + kfree(event); goto out; } @@ -272,7 +274,7 @@ static void serio_handle_event(void) struct serio_event *event; struct serio_driver *serio_drv; - down(&serio_sem); + mutex_lock(&serio_mutex); /* * Note that we handle only one event here to give swsusp @@ -314,7 +316,7 @@ static void serio_handle_event(void) serio_free_event(event); } - up(&serio_sem); + mutex_unlock(&serio_mutex); } /* @@ -449,7 +451,7 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute * struct device_driver *drv; int retval; - retval = down_interruptible(&serio_sem); + retval = mutex_lock_interruptible(&serio_mutex); if (retval) return retval; @@ -469,7 +471,7 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute * retval = -EINVAL; } - up(&serio_sem); + mutex_unlock(&serio_mutex); return retval; } @@ -524,7 +526,7 @@ static void serio_init_port(struct serio *serio) __module_get(THIS_MODULE); spin_lock_init(&serio->lock); - init_MUTEX(&serio->drv_sem); + mutex_init(&serio->drv_mutex); device_initialize(&serio->dev); snprintf(serio->dev.bus_id, sizeof(serio->dev.bus_id), "serio%ld", (long)atomic_inc_return(&serio_no) - 1); @@ -661,10 +663,10 @@ void __serio_register_port(struct serio *serio, struct module *owner) */ void serio_unregister_port(struct serio *serio) { - down(&serio_sem); + mutex_lock(&serio_mutex); serio_disconnect_port(serio); serio_destroy_port(serio); - up(&serio_sem); + mutex_unlock(&serio_mutex); } /* @@ -672,17 +674,17 @@ void serio_unregister_port(struct serio *serio) */ void serio_unregister_child_port(struct serio *serio) { - down(&serio_sem); + mutex_lock(&serio_mutex); if (serio->child) { serio_disconnect_port(serio->child); serio_destroy_port(serio->child); } - up(&serio_sem); + mutex_unlock(&serio_mutex); } /* * Submits register request to kseriod for subsequent execution. - * Can be used when it is not obvious whether the serio_sem is + * Can be used when it is not obvious whether the serio_mutex is * taken or not and when delayed execution is feasible. */ void __serio_unregister_port_delayed(struct serio *serio, struct module *owner) @@ -765,7 +767,7 @@ void serio_unregister_driver(struct serio_driver *drv) { struct serio *serio; - down(&serio_sem); + mutex_lock(&serio_mutex); drv->manual_bind = 1; /* so serio_find_driver ignores it */ start_over: @@ -779,7 +781,7 @@ start_over: } driver_unregister(&drv->driver); - up(&serio_sem); + mutex_unlock(&serio_mutex); } static void serio_set_drv(struct serio *serio, struct serio_driver *drv) @@ -858,7 +860,7 @@ static int serio_resume(struct device *dev) return 0; } -/* called from serio_driver->connect/disconnect methods under serio_sem */ +/* called from serio_driver->connect/disconnect methods under serio_mutex */ int serio_open(struct serio *serio, struct serio_driver *drv) { serio_set_drv(serio, drv); @@ -870,7 +872,7 @@ int serio_open(struct serio *serio, struct serio_driver *drv) return 0; } -/* called from serio_driver->connect/disconnect methods under serio_sem */ +/* called from serio_driver->connect/disconnect methods under serio_mutex */ void serio_close(struct serio *serio) { if (serio->close) @@ -923,5 +925,5 @@ static void __exit serio_exit(void) kthread_stop(serio_task); } -module_init(serio_init); +subsys_initcall(serio_init); module_exit(serio_exit); diff --git a/drivers/input/serio/serio_raw.c b/drivers/input/serio/serio_raw.c index 47e08de18d07..5a2703b536dc 100644 --- a/drivers/input/serio/serio_raw.c +++ b/drivers/input/serio/serio_raw.c @@ -19,6 +19,7 @@ #include #include #include +#include #define DRIVER_DESC "Raw serio driver" @@ -46,7 +47,7 @@ struct serio_raw_list { struct list_head node; }; -static DECLARE_MUTEX(serio_raw_sem); +static DEFINE_MUTEX(serio_raw_mutex); static LIST_HEAD(serio_raw_list); static unsigned int serio_raw_no; @@ -81,7 +82,7 @@ static int serio_raw_open(struct inode *inode, struct file *file) struct serio_raw_list *list; int retval = 0; - retval = down_interruptible(&serio_raw_sem); + retval = mutex_lock_interruptible(&serio_raw_mutex); if (retval) return retval; @@ -95,12 +96,11 @@ static int serio_raw_open(struct inode *inode, struct file *file) goto out; } - if (!(list = kmalloc(sizeof(struct serio_raw_list), GFP_KERNEL))) { + if (!(list = kzalloc(sizeof(struct serio_raw_list), GFP_KERNEL))) { retval = -ENOMEM; goto out; } - memset(list, 0, sizeof(struct serio_raw_list)); list->serio_raw = serio_raw; file->private_data = list; @@ -108,7 +108,7 @@ static int serio_raw_open(struct inode *inode, struct file *file) list_add_tail(&list->node, &serio_raw->list); out: - up(&serio_raw_sem); + mutex_unlock(&serio_raw_mutex); return retval; } @@ -130,12 +130,12 @@ static int serio_raw_release(struct inode *inode, struct file *file) struct serio_raw_list *list = file->private_data; struct serio_raw *serio_raw = list->serio_raw; - down(&serio_raw_sem); + mutex_lock(&serio_raw_mutex); serio_raw_fasync(-1, file, 0); serio_raw_cleanup(serio_raw); - up(&serio_raw_sem); + mutex_unlock(&serio_raw_mutex); return 0; } @@ -194,7 +194,7 @@ static ssize_t serio_raw_write(struct file *file, const char __user *buffer, siz int retval; unsigned char c; - retval = down_interruptible(&serio_raw_sem); + retval = mutex_lock_interruptible(&serio_raw_mutex); if (retval) return retval; @@ -219,7 +219,7 @@ static ssize_t serio_raw_write(struct file *file, const char __user *buffer, siz }; out: - up(&serio_raw_sem); + mutex_unlock(&serio_raw_mutex); return written; } @@ -275,14 +275,13 @@ static int serio_raw_connect(struct serio *serio, struct serio_driver *drv) struct serio_raw *serio_raw; int err; - if (!(serio_raw = kmalloc(sizeof(struct serio_raw), GFP_KERNEL))) { + if (!(serio_raw = kzalloc(sizeof(struct serio_raw), GFP_KERNEL))) { printk(KERN_ERR "serio_raw.c: can't allocate memory for a device\n"); return -ENOMEM; } - down(&serio_raw_sem); + mutex_lock(&serio_raw_mutex); - memset(serio_raw, 0, sizeof(struct serio_raw)); snprintf(serio_raw->name, sizeof(serio_raw->name), "serio_raw%d", serio_raw_no++); serio_raw->refcnt = 1; serio_raw->serio = serio; @@ -325,7 +324,7 @@ out_free: serio_set_drvdata(serio, NULL); kfree(serio_raw); out: - up(&serio_raw_sem); + mutex_unlock(&serio_raw_mutex); return err; } @@ -350,7 +349,7 @@ static void serio_raw_disconnect(struct serio *serio) { struct serio_raw *serio_raw; - down(&serio_raw_sem); + mutex_lock(&serio_raw_mutex); serio_raw = serio_get_drvdata(serio); @@ -361,7 +360,7 @@ static void serio_raw_disconnect(struct serio *serio) if (!serio_raw_cleanup(serio_raw)) wake_up_interruptible(&serio_raw->wait); - up(&serio_raw_sem); + mutex_unlock(&serio_raw_mutex); } static struct serio_device_id serio_raw_serio_ids[] = { diff --git a/drivers/input/tsdev.c b/drivers/input/tsdev.c index ca1547929d62..d678d144bbf8 100644 --- a/drivers/input/tsdev.c +++ b/drivers/input/tsdev.c @@ -157,9 +157,8 @@ static int tsdev_open(struct inode *inode, struct file *file) if (i >= TSDEV_MINORS || !tsdev_table[i & TSDEV_MINOR_MASK]) return -ENODEV; - if (!(list = kmalloc(sizeof(struct tsdev_list), GFP_KERNEL))) + if (!(list = kzalloc(sizeof(struct tsdev_list), GFP_KERNEL))) return -ENOMEM; - memset(list, 0, sizeof(struct tsdev_list)); list->raw = (i >= TSDEV_MINORS/2) ? 1 : 0; @@ -379,9 +378,8 @@ static struct input_handle *tsdev_connect(struct input_handler *handler, return NULL; } - if (!(tsdev = kmalloc(sizeof(struct tsdev), GFP_KERNEL))) + if (!(tsdev = kzalloc(sizeof(struct tsdev), GFP_KERNEL))) return NULL; - memset(tsdev, 0, sizeof(struct tsdev)); INIT_LIST_HEAD(&tsdev->list); init_waitqueue_head(&tsdev->wait); diff --git a/drivers/isdn/gigaset/Kconfig b/drivers/isdn/gigaset/Kconfig index 53c4fb62ed85..5b203fe21dcd 100644 --- a/drivers/isdn/gigaset/Kconfig +++ b/drivers/isdn/gigaset/Kconfig @@ -3,8 +3,8 @@ menu "Siemens Gigaset" config ISDN_DRV_GIGASET tristate "Siemens Gigaset support (isdn)" - depends on ISDN_I4L && m -# depends on ISDN_I4L && MODULES + depends on ISDN_I4L + select CRC_CCITT help Say m here if you have a Gigaset or Sinus isdn device. diff --git a/drivers/isdn/gigaset/asyncdata.c b/drivers/isdn/gigaset/asyncdata.c index 171f8b703d61..ce3cd77094b3 100644 --- a/drivers/isdn/gigaset/asyncdata.c +++ b/drivers/isdn/gigaset/asyncdata.c @@ -3,7 +3,7 @@ * * Copyright (c) 2005 by Tilman Schmidt , * Hansjoerg Lipp , - * Stefan Eilers . + * Stefan Eilers. * * ===================================================================== * This program is free software; you can redistribute it and/or @@ -11,10 +11,6 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * ===================================================================== - * ToDo: ... - * ===================================================================== - * Version: $Id: asyncdata.c,v 1.2.2.7 2005/11/13 23:05:18 hjlipp Exp $ - * ===================================================================== */ #include "gigaset.h" @@ -45,7 +41,7 @@ static inline int muststuff(unsigned char c) * number of processed bytes */ static inline int cmd_loop(unsigned char c, unsigned char *src, int numbytes, - struct inbuf_t *inbuf) + struct inbuf_t *inbuf) { struct cardstate *cs = inbuf->cs; unsigned cbytes = cs->cbytes; @@ -55,10 +51,11 @@ static inline int cmd_loop(unsigned char c, unsigned char *src, int numbytes, for (;;) { cs->respdata[cbytes] = c; if (c == 10 || c == 13) { - dbg(DEBUG_TRANSCMD, "%s: End of Command (%d Bytes)", - __func__, cbytes); + gig_dbg(DEBUG_TRANSCMD, "%s: End of Command (%d Bytes)", + __func__, cbytes); cs->cbytes = cbytes; - gigaset_handle_modem_response(cs); /* can change cs->dle */ + gigaset_handle_modem_response(cs); /* can change + cs->dle */ cbytes = 0; if (cs->dle && @@ -71,7 +68,7 @@ static inline int cmd_loop(unsigned char c, unsigned char *src, int numbytes, if (cbytes < MAX_RESP_SIZE - 1) cbytes++; else - warn("response too large"); + dev_warn(cs->dev, "response too large\n"); } if (!numbytes) @@ -96,11 +93,12 @@ static inline int cmd_loop(unsigned char c, unsigned char *src, int numbytes, * number of processed bytes */ static inline int lock_loop(unsigned char *src, int numbytes, - struct inbuf_t *inbuf) + struct inbuf_t *inbuf) { struct cardstate *cs = inbuf->cs; - gigaset_dbg_buffer(DEBUG_LOCKCMD, "received response", numbytes, src, 0); + gigaset_dbg_buffer(DEBUG_LOCKCMD, "received response", + numbytes, src); gigaset_if_receive(cs, src, numbytes); return numbytes; @@ -115,24 +113,18 @@ static inline int lock_loop(unsigned char *src, int numbytes, * numbytes (all bytes processed) on error --FIXME */ static inline int hdlc_loop(unsigned char c, unsigned char *src, int numbytes, - struct inbuf_t *inbuf) + struct inbuf_t *inbuf) { struct cardstate *cs = inbuf->cs; struct bc_state *bcs = inbuf->bcs; - int inputstate; - __u16 fcs; - struct sk_buff *skb; + int inputstate = bcs->inputstate; + __u16 fcs = bcs->fcs; + struct sk_buff *skb = bcs->skb; unsigned char error; struct sk_buff *compskb; int startbytes = numbytes; int l; - IFNULLRETVAL(bcs, numbytes); - inputstate = bcs->inputstate; - fcs = bcs->fcs; - skb = bcs->skb; - IFNULLRETVAL(skb, numbytes); - if (unlikely(inputstate & INS_byte_stuff)) { inputstate &= ~INS_byte_stuff; goto byte_stuff; @@ -156,39 +148,37 @@ byte_stuff: c ^= PPP_TRANS; #ifdef CONFIG_GIGASET_DEBUG if (unlikely(!muststuff(c))) - dbg(DEBUG_HDLC, - "byte stuffed: 0x%02x", c); + gig_dbg(DEBUG_HDLC, "byte stuffed: 0x%02x", c); #endif } else if (unlikely(c == PPP_FLAG)) { if (unlikely(inputstate & INS_skip_frame)) { if (!(inputstate & INS_have_data)) { /* 7E 7E */ - //dbg(DEBUG_HDLC, "(7e)7e------------------------"); #ifdef CONFIG_GIGASET_DEBUG ++bcs->emptycount; #endif } else - dbg(DEBUG_HDLC, + gig_dbg(DEBUG_HDLC, "7e----------------------------"); /* end of frame */ error = 1; gigaset_rcv_error(NULL, cs, bcs); } else if (!(inputstate & INS_have_data)) { /* 7E 7E */ - //dbg(DEBUG_HDLC, "(7e)7e------------------------"); #ifdef CONFIG_GIGASET_DEBUG ++bcs->emptycount; #endif break; } else { - dbg(DEBUG_HDLC, - "7e----------------------------"); + gig_dbg(DEBUG_HDLC, + "7e----------------------------"); /* end of frame */ error = 0; if (unlikely(fcs != PPP_GOODFCS)) { - err("Packet checksum at %lu failed, " - "packet is corrupted (%u bytes)!", + dev_err(cs->dev, + "Packet checksum at %lu failed, " + "packet is corrupted (%u bytes)!\n", bcs->rcvbytes, skb->len); compskb = NULL; gigaset_rcv_error(compskb, cs, bcs); @@ -202,9 +192,11 @@ byte_stuff: skb = NULL; inputstate |= INS_skip_frame; if (l == 1) { - err("invalid packet size (1)!"); + dev_err(cs->dev, + "invalid packet size (1)!\n"); error = 1; - gigaset_rcv_error(NULL, cs, bcs); + gigaset_rcv_error(NULL, + cs, bcs); } } if (likely(!(error || @@ -227,7 +219,8 @@ byte_stuff: } else if (likely((skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)) { skb_reserve(skb, HW_HDR_LEN); } else { - warn("could not allocate new skb"); + dev_warn(cs->dev, + "could not allocate new skb\n"); inputstate |= INS_skip_frame; } @@ -235,7 +228,7 @@ byte_stuff: #ifdef CONFIG_GIGASET_DEBUG } else if (unlikely(muststuff(c))) { /* Should not happen. Possible after ZDLE=1. */ - dbg(DEBUG_HDLC, "not byte stuffed: 0x%02x", c); + gig_dbg(DEBUG_HDLC, "not byte stuffed: 0x%02x", c); #endif } @@ -243,8 +236,8 @@ byte_stuff: #ifdef CONFIG_GIGASET_DEBUG if (unlikely(!(inputstate & INS_have_data))) { - dbg(DEBUG_HDLC, - "7e (%d x) ================", bcs->emptycount); + gig_dbg(DEBUG_HDLC, "7e (%d x) ================", + bcs->emptycount); bcs->emptycount = 0; } #endif @@ -253,14 +246,13 @@ byte_stuff: if (likely(!(inputstate & INS_skip_frame))) { if (unlikely(skb->len == SBUFSIZE)) { - warn("received packet too long"); + dev_warn(cs->dev, "received packet too long\n"); dev_kfree_skb_any(skb); skb = NULL; inputstate |= INS_skip_frame; break; } - *gigaset_skb_put_quick(skb, 1) = c; - /* *__skb_put (skb, 1) = c; */ + *__skb_put(skb, 1) = c; fcs = crc_ccitt_byte(fcs, c); } @@ -289,19 +281,14 @@ byte_stuff: * numbytes (all bytes processed) on error --FIXME */ static inline int iraw_loop(unsigned char c, unsigned char *src, int numbytes, - struct inbuf_t *inbuf) + struct inbuf_t *inbuf) { struct cardstate *cs = inbuf->cs; struct bc_state *bcs = inbuf->bcs; - int inputstate; - struct sk_buff *skb; + int inputstate = bcs->inputstate; + struct sk_buff *skb = bcs->skb; int startbytes = numbytes; - IFNULLRETVAL(bcs, numbytes); - inputstate = bcs->inputstate; - skb = bcs->skb; - IFNULLRETVAL(skb, numbytes); - for (;;) { /* add character */ inputstate |= INS_have_data; @@ -309,13 +296,13 @@ static inline int iraw_loop(unsigned char c, unsigned char *src, int numbytes, if (likely(!(inputstate & INS_skip_frame))) { if (unlikely(skb->len == SBUFSIZE)) { //FIXME just pass skb up and allocate a new one - warn("received packet too long"); + dev_warn(cs->dev, "received packet too long\n"); dev_kfree_skb_any(skb); skb = NULL; inputstate |= INS_skip_frame; break; } - *gigaset_skb_put_quick(skb, 1) = gigaset_invtab[c]; + *__skb_put(skb, 1) = gigaset_invtab[c]; } if (unlikely(!numbytes)) @@ -343,7 +330,7 @@ static inline int iraw_loop(unsigned char c, unsigned char *src, int numbytes, != NULL)) { skb_reserve(skb, HW_HDR_LEN); } else { - warn("could not allocate new skb"); + dev_warn(cs->dev, "could not allocate new skb\n"); inputstate |= INS_skip_frame; } } @@ -364,13 +351,13 @@ void gigaset_m10x_input(struct inbuf_t *inbuf) head = atomic_read(&inbuf->head); tail = atomic_read(&inbuf->tail); - dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail); + gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail); if (head != tail) { cs = inbuf->cs; src = inbuf->data + head; numbytes = (head > tail ? RBUFSIZE : tail) - head; - dbg(DEBUG_INTR, "processing %u bytes", numbytes); + gig_dbg(DEBUG_INTR, "processing %u bytes", numbytes); while (numbytes) { if (atomic_read(&cs->mstate) == MS_LOCKED) { @@ -392,8 +379,7 @@ void gigaset_m10x_input(struct inbuf_t *inbuf) if (!(inbuf->inputstate & INS_DLE_char)) { - /* FIXME Einfach je nach Modus Funktionszeiger in cs setzen [hier+hdlc_loop]? */ - /* FIXME Spart folgendes "if" und ermoeglicht andere Protokolle */ + /* FIXME use function pointers? */ if (inbuf->inputstate & INS_command) procbytes = cmd_loop(c, src, numbytes, inbuf); else if (inbuf->bcs->proto2 == ISDN_PROTO_L2_HDLC) @@ -403,13 +389,14 @@ void gigaset_m10x_input(struct inbuf_t *inbuf) src += procbytes; numbytes -= procbytes; - } else { /* DLE-char */ + } else { /* DLE char */ inbuf->inputstate &= ~INS_DLE_char; switch (c) { case 'X': /*begin of command*/ #ifdef CONFIG_GIGASET_DEBUG if (inbuf->inputstate & INS_command) - err("received 'X' in command mode"); + dev_err(cs->dev, + "received 'X' in command mode\n"); #endif inbuf->inputstate |= INS_command | INS_DLE_command; @@ -417,7 +404,8 @@ void gigaset_m10x_input(struct inbuf_t *inbuf) case '.': /*end of command*/ #ifdef CONFIG_GIGASET_DEBUG if (!(inbuf->inputstate & INS_command)) - err("received '.' in hdlc mode"); + dev_err(cs->dev, + "received '.' in hdlc mode\n"); #endif inbuf->inputstate &= cs->dle ? ~(INS_DLE_command|INS_command) @@ -425,7 +413,9 @@ void gigaset_m10x_input(struct inbuf_t *inbuf) break; //case DLE_FLAG: /*DLE_FLAG in data stream*/ /* schon oben behandelt! */ default: - err("received 0x10 0x%02x!", (int) c); + dev_err(cs->dev, + "received 0x10 0x%02x!\n", + (int) c); /* FIXME: reset driver?? */ } } @@ -444,7 +434,7 @@ nextbyte: } } - dbg(DEBUG_INTR, "setting head to %u", head); + gig_dbg(DEBUG_INTR, "setting head to %u", head); atomic_set(&inbuf->head, head); } } @@ -479,14 +469,13 @@ static struct sk_buff *HDLC_Encode(struct sk_buff *skb, int head, int tail) stuf_cnt++; fcs = crc_ccitt_byte(fcs, *cp++); } - fcs ^= 0xffff; /* complement */ + fcs ^= 0xffff; /* complement */ /* size of new buffer: original size + number of stuffing bytes * + 2 bytes FCS + 2 stuffing bytes for FCS (if needed) + 2 flag bytes */ hdlc_skb = dev_alloc_skb(skb->len + stuf_cnt + 6 + tail + head); if (!hdlc_skb) { - err("unable to allocate memory for HDLC encoding!"); dev_kfree_skb(skb); return NULL; } @@ -508,7 +497,7 @@ static struct sk_buff *HDLC_Encode(struct sk_buff *skb, int head, int tail) } /* Finally add FCS (byte stuffed) and flag sequence */ - c = (fcs & 0x00ff); /* least significant byte first */ + c = (fcs & 0x00ff); /* least significant byte first */ if (muststuff(c)) { *(skb_put(hdlc_skb, 1)) = PPP_ESCAPE; c ^= PPP_TRANS; @@ -546,7 +535,6 @@ static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail) /* worst case: every byte must be stuffed */ iraw_skb = dev_alloc_skb(2*skb->len + tail + head); if (!iraw_skb) { - err("unable to allocate memory for HDLC encoding!"); dev_kfree_skb(skb); return NULL; } @@ -577,21 +565,23 @@ static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail) */ int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb) { - unsigned len; - - IFNULLRETVAL(bcs, -EFAULT); - IFNULLRETVAL(skb, -EFAULT); - len = skb->len; + unsigned len = skb->len; + unsigned long flags; if (bcs->proto2 == ISDN_PROTO_L2_HDLC) skb = HDLC_Encode(skb, HW_HDR_LEN, 0); else skb = iraw_encode(skb, HW_HDR_LEN, 0); - if (!skb) + if (!skb) { + err("unable to allocate memory for encoding!\n"); return -ENOMEM; + } skb_queue_tail(&bcs->squeue, skb); - tasklet_schedule(&bcs->cs->write_tasklet); + spin_lock_irqsave(&bcs->cs->lock, flags); + if (bcs->cs->connected) + tasklet_schedule(&bcs->cs->write_tasklet); + spin_unlock_irqrestore(&bcs->cs->lock, flags); return len; /* ok so far */ } diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c index 31f0f07832bc..f86ed6af3aa2 100644 --- a/drivers/isdn/gigaset/bas-gigaset.c +++ b/drivers/isdn/gigaset/bas-gigaset.c @@ -3,7 +3,7 @@ * * Copyright (c) 2001 by Hansjoerg Lipp , * Tilman Schmidt , - * Stefan Eilers . + * Stefan Eilers. * * Based on usb-gigaset.c. * @@ -13,10 +13,6 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * ===================================================================== - * ToDo: ... - * ===================================================================== - * Version: $Id: bas-gigaset.c,v 1.52.4.19 2006/02/04 18:28:16 hjlipp Exp $ - * ===================================================================== */ #include "gigaset.h" @@ -30,7 +26,7 @@ #include /* Version Information */ -#define DRIVER_AUTHOR "Tilman Schmidt , Hansjoerg Lipp , Stefan Eilers " +#define DRIVER_AUTHOR "Tilman Schmidt , Hansjoerg Lipp , Stefan Eilers" #define DRIVER_DESC "USB Driver for Gigaset 307x" @@ -70,9 +66,6 @@ static struct usb_device_id gigaset_table [] = { MODULE_DEVICE_TABLE(usb, gigaset_table); -/* Get a minor range for your devices from the usb maintainer */ -#define USB_SKEL_MINOR_BASE 200 - /*======================= local function prototypes =============================*/ /* This function is called if a new device is connected to the USB port. It @@ -88,25 +81,25 @@ static void gigaset_disconnect(struct usb_interface *interface); /*==============================================================================*/ struct bas_cardstate { - struct usb_device *udev; /* USB device pointer */ - struct usb_interface *interface; /* interface for this device */ + struct usb_device *udev; /* USB device pointer */ + struct usb_interface *interface; /* interface for this device */ unsigned char minor; /* starting minor number */ - struct urb *urb_ctrl; /* control pipe default URB */ + struct urb *urb_ctrl; /* control pipe default URB */ struct usb_ctrlrequest dr_ctrl; struct timer_list timer_ctrl; /* control request timeout */ struct timer_list timer_atrdy; /* AT command ready timeout */ - struct urb *urb_cmd_out; /* for sending AT commands */ + struct urb *urb_cmd_out; /* for sending AT commands */ struct usb_ctrlrequest dr_cmd_out; int retry_cmd_out; - struct urb *urb_cmd_in; /* for receiving AT replies */ + struct urb *urb_cmd_in; /* for receiving AT replies */ struct usb_ctrlrequest dr_cmd_in; struct timer_list timer_cmd_in; /* receive request timeout */ - unsigned char *rcvbuf; /* AT reply receive buffer */ + unsigned char *rcvbuf; /* AT reply receive buffer */ - struct urb *urb_int_in; /* URB for interrupt pipe */ + struct urb *urb_int_in; /* URB for interrupt pipe */ unsigned char int_in_buf[3]; spinlock_t lock; /* locks all following */ @@ -208,53 +201,54 @@ static inline char *usb_pipetype_str(int pipe) * write content of URB to syslog for debugging */ static inline void dump_urb(enum debuglevel level, const char *tag, - struct urb *urb) + struct urb *urb) { #ifdef CONFIG_GIGASET_DEBUG int i; - IFNULLRET(tag); - dbg(level, "%s urb(0x%08lx)->{", tag, (unsigned long) urb); + gig_dbg(level, "%s urb(0x%08lx)->{", tag, (unsigned long) urb); if (urb) { - dbg(level, - " dev=0x%08lx, pipe=%s:EP%d/DV%d:%s, " - "status=%d, hcpriv=0x%08lx, transfer_flags=0x%x,", - (unsigned long) urb->dev, - usb_pipetype_str(urb->pipe), - usb_pipeendpoint(urb->pipe), usb_pipedevice(urb->pipe), - usb_pipein(urb->pipe) ? "in" : "out", - urb->status, (unsigned long) urb->hcpriv, - urb->transfer_flags); - dbg(level, - " transfer_buffer=0x%08lx[%d], actual_length=%d, " - "bandwidth=%d, setup_packet=0x%08lx,", - (unsigned long) urb->transfer_buffer, - urb->transfer_buffer_length, urb->actual_length, - urb->bandwidth, (unsigned long) urb->setup_packet); - dbg(level, - " start_frame=%d, number_of_packets=%d, interval=%d, " - "error_count=%d,", - urb->start_frame, urb->number_of_packets, urb->interval, - urb->error_count); - dbg(level, - " context=0x%08lx, complete=0x%08lx, iso_frame_desc[]={", - (unsigned long) urb->context, - (unsigned long) urb->complete); + gig_dbg(level, + " dev=0x%08lx, pipe=%s:EP%d/DV%d:%s, " + "status=%d, hcpriv=0x%08lx, transfer_flags=0x%x,", + (unsigned long) urb->dev, + usb_pipetype_str(urb->pipe), + usb_pipeendpoint(urb->pipe), usb_pipedevice(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out", + urb->status, (unsigned long) urb->hcpriv, + urb->transfer_flags); + gig_dbg(level, + " transfer_buffer=0x%08lx[%d], actual_length=%d, " + "bandwidth=%d, setup_packet=0x%08lx,", + (unsigned long) urb->transfer_buffer, + urb->transfer_buffer_length, urb->actual_length, + urb->bandwidth, (unsigned long) urb->setup_packet); + gig_dbg(level, + " start_frame=%d, number_of_packets=%d, interval=%d, " + "error_count=%d,", + urb->start_frame, urb->number_of_packets, urb->interval, + urb->error_count); + gig_dbg(level, + " context=0x%08lx, complete=0x%08lx, " + "iso_frame_desc[]={", + (unsigned long) urb->context, + (unsigned long) urb->complete); for (i = 0; i < urb->number_of_packets; i++) { - struct usb_iso_packet_descriptor *pifd = &urb->iso_frame_desc[i]; - dbg(level, - " {offset=%u, length=%u, actual_length=%u, " - "status=%u}", - pifd->offset, pifd->length, pifd->actual_length, - pifd->status); + struct usb_iso_packet_descriptor *pifd + = &urb->iso_frame_desc[i]; + gig_dbg(level, + " {offset=%u, length=%u, actual_length=%u, " + "status=%u}", + pifd->offset, pifd->length, pifd->actual_length, + pifd->status); } } - dbg(level, "}}"); + gig_dbg(level, "}}"); #endif } /* read/set modem control bits etc. (m10x only) */ static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state, - unsigned new_state) + unsigned new_state) { return -EINVAL; } @@ -280,8 +274,8 @@ static inline void error_hangup(struct bc_state *bcs) { struct cardstate *cs = bcs->cs; - dbg(DEBUG_ANY, - "%s: scheduling HUP for channel %d", __func__, bcs->channel); + gig_dbg(DEBUG_ANY, "%s: scheduling HUP for channel %d", + __func__, bcs->channel); if (!gigaset_add_event(cs, &bcs->at_state, EV_HUP, NULL, 0, NULL)) { //FIXME what should we do? @@ -301,22 +295,19 @@ static inline void error_hangup(struct bc_state *bcs) static inline void error_reset(struct cardstate *cs) { //FIXME try to recover without bothering the user - err("unrecoverable error - please disconnect the Gigaset base to reset"); + dev_err(cs->dev, + "unrecoverable error - please disconnect Gigaset base to reset\n"); } /* check_pending * check for completion of pending control request * parameter: - * urb USB request block of completed request - * urb->context = hardware specific controller state structure + * ucs hardware specific controller state structure */ static void check_pending(struct bas_cardstate *ucs) { unsigned long flags; - IFNULLRET(ucs); - IFNULLRET(cardstate); - spin_lock_irqsave(&ucs->lock, flags); switch (ucs->pending) { case 0: @@ -336,8 +327,6 @@ static void check_pending(struct bas_cardstate *ucs) case HD_CLOSE_ATCHANNEL: if (!(atomic_read(&ucs->basstate) & BS_ATOPEN)) ucs->pending = 0; - //wake_up_interruptible(cs->initwait); - //FIXME need own wait queue? break; case HD_CLOSE_B1CHANNEL: if (!(atomic_read(&ucs->basstate) & BS_B1OPEN)) @@ -354,7 +343,9 @@ static void check_pending(struct bas_cardstate *ucs) * are handled separately and should never end up here */ default: - warn("unknown pending request 0x%02x cleared", ucs->pending); + dev_warn(&ucs->interface->dev, + "unknown pending request 0x%02x cleared\n", + ucs->pending); ucs->pending = 0; } @@ -372,27 +363,23 @@ static void check_pending(struct bas_cardstate *ucs) static void cmd_in_timeout(unsigned long data) { struct cardstate *cs = (struct cardstate *) data; - struct bas_cardstate *ucs; + struct bas_cardstate *ucs = cs->hw.bas; unsigned long flags; - IFNULLRET(cs); - ucs = cs->hw.bas; - IFNULLRET(ucs); - spin_lock_irqsave(&cs->lock, flags); - if (!atomic_read(&cs->connected)) { - dbg(DEBUG_USBREQ, "%s: disconnected", __func__); + if (unlikely(!cs->connected)) { + gig_dbg(DEBUG_USBREQ, "%s: disconnected", __func__); spin_unlock_irqrestore(&cs->lock, flags); return; } if (!ucs->rcvbuf_size) { - dbg(DEBUG_USBREQ, "%s: no receive in progress", __func__); + gig_dbg(DEBUG_USBREQ, "%s: no receive in progress", __func__); spin_unlock_irqrestore(&cs->lock, flags); return; } spin_unlock_irqrestore(&cs->lock, flags); - err("timeout reading AT response"); + dev_err(cs->dev, "timeout reading AT response\n"); error_reset(cs); //FIXME retry? } @@ -412,18 +399,15 @@ static void read_ctrl_callback(struct urb *urb, struct pt_regs *regs); */ static int atread_submit(struct cardstate *cs, int timeout) { - struct bas_cardstate *ucs; + struct bas_cardstate *ucs = cs->hw.bas; int ret; - IFNULLRETVAL(cs, -EINVAL); - ucs = cs->hw.bas; - IFNULLRETVAL(ucs, -EINVAL); - IFNULLRETVAL(ucs->urb_cmd_in, -EINVAL); - - dbg(DEBUG_USBREQ, "-------> HD_READ_ATMESSAGE (%d)", ucs->rcvbuf_size); + gig_dbg(DEBUG_USBREQ, "-------> HD_READ_ATMESSAGE (%d)", + ucs->rcvbuf_size); if (ucs->urb_cmd_in->status == -EINPROGRESS) { - err("could not submit HD_READ_ATMESSAGE: URB busy"); + dev_err(cs->dev, + "could not submit HD_READ_ATMESSAGE: URB busy\n"); return -EBUSY; } @@ -433,19 +417,19 @@ static int atread_submit(struct cardstate *cs, int timeout) ucs->dr_cmd_in.wIndex = 0; ucs->dr_cmd_in.wLength = cpu_to_le16(ucs->rcvbuf_size); usb_fill_control_urb(ucs->urb_cmd_in, ucs->udev, - usb_rcvctrlpipe(ucs->udev, 0), - (unsigned char*) & ucs->dr_cmd_in, - ucs->rcvbuf, ucs->rcvbuf_size, - read_ctrl_callback, cs->inbuf); + usb_rcvctrlpipe(ucs->udev, 0), + (unsigned char*) & ucs->dr_cmd_in, + ucs->rcvbuf, ucs->rcvbuf_size, + read_ctrl_callback, cs->inbuf); if ((ret = usb_submit_urb(ucs->urb_cmd_in, SLAB_ATOMIC)) != 0) { - err("could not submit HD_READ_ATMESSAGE: %s", - get_usb_statmsg(ret)); + dev_err(cs->dev, "could not submit HD_READ_ATMESSAGE: %s\n", + get_usb_statmsg(ret)); return ret; } if (timeout > 0) { - dbg(DEBUG_USBREQ, "setting timeout of %d/10 secs", timeout); + gig_dbg(DEBUG_USBREQ, "setting timeout of %d/10 secs", timeout); ucs->timer_cmd_in.expires = jiffies + timeout * HZ / 10; ucs->timer_cmd_in.data = (unsigned long) cs; ucs->timer_cmd_in.function = cmd_in_timeout; @@ -483,25 +467,14 @@ inline static void update_basstate(struct bas_cardstate *ucs, */ static void read_int_callback(struct urb *urb, struct pt_regs *regs) { - struct cardstate *cs; - struct bas_cardstate *ucs; + struct cardstate *cs = urb->context; + struct bas_cardstate *ucs = cs->hw.bas; struct bc_state *bcs; unsigned long flags; int status; unsigned l; int channel; - IFNULLRET(urb); - cs = (struct cardstate *) urb->context; - IFNULLRET(cs); - ucs = cs->hw.bas; - IFNULLRET(ucs); - - if (unlikely(!atomic_read(&cs->connected))) { - warn("%s: disconnected", __func__); - return; - } - switch (urb->status) { case 0: /* success */ break; @@ -509,11 +482,12 @@ static void read_int_callback(struct urb *urb, struct pt_regs *regs) case -ECONNRESET: /* canceled (async) */ case -EINPROGRESS: /* pending */ /* ignore silently */ - dbg(DEBUG_USBREQ, - "%s: %s", __func__, get_usb_statmsg(urb->status)); + gig_dbg(DEBUG_USBREQ, "%s: %s", + __func__, get_usb_statmsg(urb->status)); return; default: /* severe trouble */ - warn("interrupt read: %s", get_usb_statmsg(urb->status)); + dev_warn(cs->dev, "interrupt read: %s\n", + get_usb_statmsg(urb->status)); //FIXME corrective action? resubmission always ok? goto resubmit; } @@ -521,10 +495,9 @@ static void read_int_callback(struct urb *urb, struct pt_regs *regs) l = (unsigned) ucs->int_in_buf[1] + (((unsigned) ucs->int_in_buf[2]) << 8); - dbg(DEBUG_USBREQ, - "<-------%d: 0x%02x (%u [0x%02x 0x%02x])", urb->actual_length, - (int)ucs->int_in_buf[0], l, - (int)ucs->int_in_buf[1], (int)ucs->int_in_buf[2]); + gig_dbg(DEBUG_USBREQ, "<-------%d: 0x%02x (%u [0x%02x 0x%02x])", + urb->actual_length, (int)ucs->int_in_buf[0], l, + (int)ucs->int_in_buf[1], (int)ucs->int_in_buf[2]); channel = 0; @@ -570,28 +543,30 @@ static void read_int_callback(struct urb *urb, struct pt_regs *regs) case HD_B1_FLOW_CONTROL: bcs = cs->bcs + channel; atomic_add((l - BAS_NORMFRAME) * BAS_CORRFRAMES, - &bcs->hw.bas->corrbytes); - dbg(DEBUG_ISO, - "Flow control (channel %d, sub %d): 0x%02x => %d", - channel, bcs->hw.bas->numsub, l, - atomic_read(&bcs->hw.bas->corrbytes)); + &bcs->hw.bas->corrbytes); + gig_dbg(DEBUG_ISO, + "Flow control (channel %d, sub %d): 0x%02x => %d", + channel, bcs->hw.bas->numsub, l, + atomic_read(&bcs->hw.bas->corrbytes)); break; case HD_RECEIVEATDATA_ACK: /* AT response ready to be received */ if (!l) { - warn("HD_RECEIVEATDATA_ACK with length 0 ignored"); + dev_warn(cs->dev, + "HD_RECEIVEATDATA_ACK with length 0 ignored\n"); break; } spin_lock_irqsave(&cs->lock, flags); if (ucs->rcvbuf_size) { spin_unlock_irqrestore(&cs->lock, flags); - err("receive AT data overrun, %d bytes lost", l); + dev_err(cs->dev, + "receive AT data overrun, %d bytes lost\n", l); error_reset(cs); //FIXME reschedule break; } if ((ucs->rcvbuf = kmalloc(l, GFP_ATOMIC)) == NULL) { spin_unlock_irqrestore(&cs->lock, flags); - err("%s: out of memory, %d bytes lost", __func__, l); + dev_err(cs->dev, "out of memory, %d bytes lost\n", l); error_reset(cs); //FIXME reschedule break; } @@ -607,25 +582,28 @@ static void read_int_callback(struct urb *urb, struct pt_regs *regs) break; case HD_RESET_INTERRUPT_PIPE_ACK: - dbg(DEBUG_USBREQ, "HD_RESET_INTERRUPT_PIPE_ACK"); + gig_dbg(DEBUG_USBREQ, "HD_RESET_INTERRUPT_PIPE_ACK"); break; case HD_SUSPEND_END: - dbg(DEBUG_USBREQ, "HD_SUSPEND_END"); + gig_dbg(DEBUG_USBREQ, "HD_SUSPEND_END"); break; default: - warn("unknown Gigaset signal 0x%02x (%u) ignored", - (int) ucs->int_in_buf[0], l); + dev_warn(cs->dev, + "unknown Gigaset signal 0x%02x (%u) ignored\n", + (int) ucs->int_in_buf[0], l); } check_pending(ucs); resubmit: - status = usb_submit_urb(urb, SLAB_ATOMIC); + spin_lock_irqsave(&cs->lock, flags); + status = cs->connected ? usb_submit_urb(urb, SLAB_ATOMIC) : -ENODEV; + spin_unlock_irqrestore(&cs->lock, flags); if (unlikely(status)) { - err("could not resubmit interrupt URB: %s", - get_usb_statmsg(status)); + dev_err(cs->dev, "could not resubmit interrupt URB: %s\n", + get_usb_statmsg(status)); error_reset(cs); } } @@ -639,30 +617,22 @@ resubmit: */ static void read_ctrl_callback(struct urb *urb, struct pt_regs *regs) { - struct cardstate *cs; - struct bas_cardstate *ucs; + struct inbuf_t *inbuf = urb->context; + struct cardstate *cs = inbuf->cs; + struct bas_cardstate *ucs = cs->hw.bas; + int have_data = 0; unsigned numbytes; unsigned long flags; - struct inbuf_t *inbuf; - int have_data = 0; - - IFNULLRET(urb); - inbuf = (struct inbuf_t *) urb->context; - IFNULLRET(inbuf); - cs = inbuf->cs; - IFNULLRET(cs); - ucs = cs->hw.bas; - IFNULLRET(ucs); spin_lock_irqsave(&cs->lock, flags); - if (!atomic_read(&cs->connected)) { + if (unlikely(!cs->connected)) { warn("%s: disconnected", __func__); spin_unlock_irqrestore(&cs->lock, flags); return; } if (!ucs->rcvbuf_size) { - warn("%s: no receive in progress", __func__); + dev_warn(cs->dev, "%s: no receive in progress\n", __func__); spin_unlock_irqrestore(&cs->lock, flags); return; } @@ -673,12 +643,14 @@ static void read_ctrl_callback(struct urb *urb, struct pt_regs *regs) case 0: /* normal completion */ numbytes = urb->actual_length; if (unlikely(numbytes == 0)) { - warn("control read: empty block received"); + dev_warn(cs->dev, + "control read: empty block received\n"); goto retry; } if (unlikely(numbytes != ucs->rcvbuf_size)) { - warn("control read: received %d chars, expected %d", - numbytes, ucs->rcvbuf_size); + dev_warn(cs->dev, + "control read: received %d chars, expected %d\n", + numbytes, ucs->rcvbuf_size); if (numbytes > ucs->rcvbuf_size) numbytes = ucs->rcvbuf_size; } @@ -698,23 +670,26 @@ static void read_ctrl_callback(struct urb *urb, struct pt_regs *regs) case -ECONNRESET: /* canceled (async) */ case -EINPROGRESS: /* pending */ /* no action necessary */ - dbg(DEBUG_USBREQ, - "%s: %s", __func__, get_usb_statmsg(urb->status)); + gig_dbg(DEBUG_USBREQ, "%s: %s", + __func__, get_usb_statmsg(urb->status)); break; default: /* severe trouble */ - warn("control read: %s", get_usb_statmsg(urb->status)); + dev_warn(cs->dev, "control read: %s\n", + get_usb_statmsg(urb->status)); retry: if (ucs->retry_cmd_in++ < BAS_RETRY) { - notice("control read: retry %d", ucs->retry_cmd_in); + dev_notice(cs->dev, "control read: retry %d\n", + ucs->retry_cmd_in); if (atread_submit(cs, BAS_TIMEOUT) >= 0) { /* resubmitted - bypass regular exit block */ spin_unlock_irqrestore(&cs->lock, flags); return; } } else { - err("control read: giving up after %d tries", - ucs->retry_cmd_in); + dev_err(cs->dev, + "control read: giving up after %d tries\n", + ucs->retry_cmd_in); } error_reset(cs); } @@ -724,7 +699,7 @@ static void read_ctrl_callback(struct urb *urb, struct pt_regs *regs) ucs->rcvbuf_size = 0; spin_unlock_irqrestore(&cs->lock, flags); if (have_data) { - dbg(DEBUG_INTR, "%s-->BH", __func__); + gig_dbg(DEBUG_INTR, "%s-->BH", __func__); gigaset_schedule_event(cs); } } @@ -743,21 +718,16 @@ static void read_iso_callback(struct urb *urb, struct pt_regs *regs) unsigned long flags; int i, rc; - IFNULLRET(urb); - IFNULLRET(urb->context); - IFNULLRET(cardstate); - /* status codes not worth bothering the tasklet with */ if (unlikely(urb->status == -ENOENT || urb->status == -ECONNRESET || - urb->status == -EINPROGRESS)) { - dbg(DEBUG_ISO, - "%s: %s", __func__, get_usb_statmsg(urb->status)); + urb->status == -EINPROGRESS)) { + gig_dbg(DEBUG_ISO, "%s: %s", + __func__, get_usb_statmsg(urb->status)); return; } - bcs = (struct bc_state *) urb->context; + bcs = urb->context; ubc = bcs->hw.bas; - IFNULLRET(ubc); spin_lock_irqsave(&ubc->isoinlock, flags); if (likely(ubc->isoindone == NULL)) { @@ -777,14 +747,17 @@ static void read_iso_callback(struct urb *urb, struct pt_regs *regs) urb->iso_frame_desc[i].actual_length = 0; } if (likely(atomic_read(&ubc->running))) { - urb->dev = bcs->cs->hw.bas->udev; /* clobbered by USB subsystem */ + /* urb->dev is clobbered by USB subsystem */ + urb->dev = bcs->cs->hw.bas->udev; urb->transfer_flags = URB_ISO_ASAP; urb->number_of_packets = BAS_NUMFRAMES; - dbg(DEBUG_ISO, "%s: isoc read overrun/resubmit", __func__); + gig_dbg(DEBUG_ISO, "%s: isoc read overrun/resubmit", + __func__); rc = usb_submit_urb(urb, SLAB_ATOMIC); if (unlikely(rc != 0)) { - err("could not resubmit isochronous read URB: %s", - get_usb_statmsg(rc)); + dev_err(bcs->cs->dev, + "could not resubmit isochronous read " + "URB: %s\n", get_usb_statmsg(rc)); dump_urb(DEBUG_ISO, "isoc read", urb); error_hangup(bcs); } @@ -806,23 +779,17 @@ static void write_iso_callback(struct urb *urb, struct pt_regs *regs) struct bas_bc_state *ubc; unsigned long flags; - IFNULLRET(urb); - IFNULLRET(urb->context); - IFNULLRET(cardstate); - /* status codes not worth bothering the tasklet with */ if (unlikely(urb->status == -ENOENT || urb->status == -ECONNRESET || - urb->status == -EINPROGRESS)) { - dbg(DEBUG_ISO, - "%s: %s", __func__, get_usb_statmsg(urb->status)); + urb->status == -EINPROGRESS)) { + gig_dbg(DEBUG_ISO, "%s: %s", + __func__, get_usb_statmsg(urb->status)); return; } /* pass URB context to tasklet */ - ucx = (struct isow_urbctx_t *) urb->context; - IFNULLRET(ucx->bcs); + ucx = urb->context; ubc = ucx->bcs->hw.bas; - IFNULLRET(ubc); spin_lock_irqsave(&ubc->isooutlock, flags); ubc->isooutovfl = ubc->isooutdone; @@ -841,15 +808,11 @@ static void write_iso_callback(struct urb *urb, struct pt_regs *regs) */ static int starturbs(struct bc_state *bcs) { + struct bas_bc_state *ubc = bcs->hw.bas; struct urb *urb; - struct bas_bc_state *ubc; int j, k; int rc; - IFNULLRETVAL(bcs, -EFAULT); - ubc = bcs->hw.bas; - IFNULLRETVAL(ubc, -EFAULT); - /* initialize L2 reception */ if (bcs->proto2 == ISDN_PROTO_L2_HDLC) bcs->inputstate |= INS_flag_hunt; @@ -859,7 +822,7 @@ static int starturbs(struct bc_state *bcs) for (k = 0; k < BAS_INURBS; k++) { urb = ubc->isoinurbs[k]; if (!urb) { - err("isoinurbs[%d]==NULL", k); + dev_err(bcs->cs->dev, "isoinurbs[%d]==NULL\n", k); rc = -EFAULT; goto error; } @@ -882,8 +845,9 @@ static int starturbs(struct bc_state *bcs) dump_urb(DEBUG_ISO, "Initial isoc read", urb); if ((rc = usb_submit_urb(urb, SLAB_ATOMIC)) != 0) { - err("could not submit isochronous read URB %d: %s", - k, get_usb_statmsg(rc)); + dev_err(bcs->cs->dev, + "could not submit isochronous read URB %d: %s\n", + k, get_usb_statmsg(rc)); goto error; } } @@ -895,7 +859,7 @@ static int starturbs(struct bc_state *bcs) for (k = 0; k < BAS_OUTURBS; ++k) { urb = ubc->isoouturbs[k].urb; if (!urb) { - err("isoouturbs[%d].urb==NULL", k); + dev_err(bcs->cs->dev, "isoouturbs[%d].urb==NULL\n", k); rc = -EFAULT; goto error; } @@ -922,8 +886,9 @@ static int starturbs(struct bc_state *bcs) dump_urb(DEBUG_ISO, "Initial isoc write", urb); rc = usb_submit_urb(ubc->isoouturbs[k].urb, SLAB_ATOMIC); if (rc != 0) { - err("could not submit isochronous write URB %d: %s", - k, get_usb_statmsg(rc)); + dev_err(bcs->cs->dev, + "could not submit isochronous write URB %d: %s\n", + k, get_usb_statmsg(rc)); goto error; } } @@ -946,20 +911,20 @@ static void stopurbs(struct bas_bc_state *ubc) { int k, rc; - IFNULLRET(ubc); - atomic_set(&ubc->running, 0); for (k = 0; k < BAS_INURBS; ++k) { rc = usb_unlink_urb(ubc->isoinurbs[k]); - dbg(DEBUG_ISO, "%s: isoc input URB %d unlinked, result = %d", - __func__, k, rc); + gig_dbg(DEBUG_ISO, + "%s: isoc input URB %d unlinked, result = %d", + __func__, k, rc); } for (k = 0; k < BAS_OUTURBS; ++k) { rc = usb_unlink_urb(ubc->isoouturbs[k].urb); - dbg(DEBUG_ISO, "%s: isoc output URB %d unlinked, result = %d", - __func__, k, rc); + gig_dbg(DEBUG_ISO, + "%s: isoc output URB %d unlinked, result = %d", + __func__, k, rc); } } @@ -977,19 +942,14 @@ static void stopurbs(struct bas_bc_state *ubc) */ static int submit_iso_write_urb(struct isow_urbctx_t *ucx) { - struct urb *urb; - struct bas_bc_state *ubc; + struct urb *urb = ucx->urb; + struct bas_bc_state *ubc = ucx->bcs->hw.bas; struct usb_iso_packet_descriptor *ifd; int corrbytes, nframe, rc; + unsigned long flags; - IFNULLRETVAL(ucx, -EFAULT); - urb = ucx->urb; - IFNULLRETVAL(urb, -EFAULT); - IFNULLRETVAL(ucx->bcs, -EFAULT); - ubc = ucx->bcs->hw.bas; - IFNULLRETVAL(ubc, -EFAULT); - - urb->dev = ucx->bcs->cs->hw.bas->udev; /* clobbered by USB subsystem */ + /* urb->dev is clobbered by USB subsystem */ + urb->dev = ucx->bcs->cs->hw.bas->udev; urb->transfer_flags = URB_ISO_ASAP; urb->transfer_buffer = ubc->isooutbuf->data; urb->transfer_buffer_length = sizeof(ubc->isooutbuf->data); @@ -1000,7 +960,8 @@ static int submit_iso_write_urb(struct isow_urbctx_t *ucx) /* compute frame length according to flow control */ ifd->length = BAS_NORMFRAME; if ((corrbytes = atomic_read(&ubc->corrbytes)) != 0) { - dbg(DEBUG_ISO, "%s: corrbytes=%d", __func__, corrbytes); + gig_dbg(DEBUG_ISO, "%s: corrbytes=%d", + __func__, corrbytes); if (corrbytes > BAS_HIGHFRAME - BAS_NORMFRAME) corrbytes = BAS_HIGHFRAME - BAS_NORMFRAME; else if (corrbytes < BAS_LOWFRAME - BAS_NORMFRAME) @@ -1008,18 +969,21 @@ static int submit_iso_write_urb(struct isow_urbctx_t *ucx) ifd->length += corrbytes; atomic_add(-corrbytes, &ubc->corrbytes); } - //dbg(DEBUG_ISO, "%s: frame %d length=%d", __func__, nframe, ifd->length); /* retrieve block of data to send */ - ifd->offset = gigaset_isowbuf_getbytes(ubc->isooutbuf, ifd->length); + ifd->offset = gigaset_isowbuf_getbytes(ubc->isooutbuf, + ifd->length); if (ifd->offset < 0) { if (ifd->offset == -EBUSY) { - dbg(DEBUG_ISO, "%s: buffer busy at frame %d", - __func__, nframe); - /* tasklet will be restarted from gigaset_send_skb() */ + gig_dbg(DEBUG_ISO, + "%s: buffer busy at frame %d", + __func__, nframe); + /* tasklet will be restarted from + gigaset_send_skb() */ } else { - err("%s: buffer error %d at frame %d", - __func__, ifd->offset, nframe); + dev_err(ucx->bcs->cs->dev, + "%s: buffer error %d at frame %d\n", + __func__, ifd->offset, nframe); return ifd->offset; } break; @@ -1029,9 +993,14 @@ static int submit_iso_write_urb(struct isow_urbctx_t *ucx) ifd->actual_length = 0; } if ((urb->number_of_packets = nframe) > 0) { - if ((rc = usb_submit_urb(urb, SLAB_ATOMIC)) != 0) { - err("could not submit isochronous write URB: %s", - get_usb_statmsg(rc)); + spin_lock_irqsave(&ucx->bcs->cs->lock, flags); + rc = ucx->bcs->cs->connected ? usb_submit_urb(urb, SLAB_ATOMIC) : -ENODEV; + spin_unlock_irqrestore(&ucx->bcs->cs->lock, flags); + + if (rc) { + dev_err(ucx->bcs->cs->dev, + "could not submit isochronous write URB: %s\n", + get_usb_statmsg(rc)); dump_urb(DEBUG_ISO, "isoc write", urb); return rc; } @@ -1048,9 +1017,9 @@ static int submit_iso_write_urb(struct isow_urbctx_t *ucx) */ static void write_iso_tasklet(unsigned long data) { - struct bc_state *bcs; - struct bas_bc_state *ubc; - struct cardstate *cs; + struct bc_state *bcs = (struct bc_state *) data; + struct bas_bc_state *ubc = bcs->hw.bas; + struct cardstate *cs = bcs->cs; struct isow_urbctx_t *done, *next, *ovfl; struct urb *urb; struct usb_iso_packet_descriptor *ifd; @@ -1060,22 +1029,10 @@ static void write_iso_tasklet(unsigned long data) struct sk_buff *skb; int len; - bcs = (struct bc_state *) data; - IFNULLRET(bcs); - ubc = bcs->hw.bas; - IFNULLRET(ubc); - cs = bcs->cs; - IFNULLRET(cs); - /* loop while completed URBs arrive in time */ for (;;) { - if (unlikely(!atomic_read(&cs->connected))) { - warn("%s: disconnected", __func__); - return; - } - if (unlikely(!(atomic_read(&ubc->running)))) { - dbg(DEBUG_ISO, "%s: not running", __func__); + gig_dbg(DEBUG_ISO, "%s: not running", __func__); return; } @@ -1087,7 +1044,7 @@ static void write_iso_tasklet(unsigned long data) ubc->isooutovfl = NULL; spin_unlock_irqrestore(&ubc->isooutlock, flags); if (ovfl) { - err("isochronous write buffer underrun - buy a faster machine :-)"); + dev_err(cs->dev, "isochronous write buffer underrun\n"); error_hangup(bcs); break; } @@ -1110,7 +1067,8 @@ static void write_iso_tasklet(unsigned long data) spin_unlock_irqrestore(&ubc->isooutlock, flags); if (next) { /* couldn't put it back */ - err("losing isochronous write URB"); + dev_err(cs->dev, + "losing isochronous write URB\n"); error_hangup(bcs); } } @@ -1123,22 +1081,25 @@ static void write_iso_tasklet(unsigned long data) break; case -EXDEV: /* inspect individual frames */ /* assumptions (for lack of documentation): - * - actual_length bytes of the frame in error are successfully sent + * - actual_length bytes of the frame in error are + * successfully sent * - all following frames are not sent at all */ - dbg(DEBUG_ISO, "%s: URB partially completed", __func__); + gig_dbg(DEBUG_ISO, "%s: URB partially completed", + __func__); offset = done->limit; /* just in case */ for (i = 0; i < BAS_NUMFRAMES; i++) { ifd = &urb->iso_frame_desc[i]; if (ifd->status || ifd->actual_length != ifd->length) { - warn("isochronous write: frame %d: %s, " - "only %d of %d bytes sent", + dev_warn(cs->dev, + "isochronous write: frame %d: %s, " + "only %d of %d bytes sent\n", i, get_usb_statmsg(ifd->status), ifd->actual_length, ifd->length); offset = (ifd->offset + - ifd->actual_length) - % BAS_OUTBUFSIZE; + ifd->actual_length) + % BAS_OUTBUFSIZE; break; } } @@ -1148,25 +1109,26 @@ static void write_iso_tasklet(unsigned long data) ifd = &urb->iso_frame_desc[i]; if (ifd->status != -EINPROGRESS || ifd->actual_length != 0) { - warn("isochronous write: frame %d: %s, " - "%d of %d bytes sent", + dev_warn(cs->dev, + "isochronous write: frame %d: %s, " + "%d of %d bytes sent\n", i, get_usb_statmsg(ifd->status), ifd->actual_length, ifd->length); offset = (ifd->offset + - ifd->actual_length) - % BAS_OUTBUFSIZE; + ifd->actual_length) + % BAS_OUTBUFSIZE; break; } } #endif break; - case -EPIPE: //FIXME is this the code for "underrun"? - err("isochronous write stalled"); + case -EPIPE: //FIXME is this the code for "underrun"? + dev_err(cs->dev, "isochronous write stalled\n"); error_hangup(bcs); break; default: /* severe trouble */ - warn("isochronous write: %s", - get_usb_statmsg(urb->status)); + dev_warn(cs->dev, "isochronous write: %s\n", + get_usb_statmsg(urb->status)); } /* mark the write buffer area covered by this URB as free */ @@ -1194,8 +1156,8 @@ static void write_iso_tasklet(unsigned long data) if (gigaset_isoc_buildframe(bcs, skb->data, len) == -EAGAIN) { /* insufficient buffer space, push back onto queue */ skb_queue_head(&bcs->squeue, skb); - dbg(DEBUG_ISO, "%s: skb requeued, qlen=%d", - __func__, skb_queue_len(&bcs->squeue)); + gig_dbg(DEBUG_ISO, "%s: skb requeued, qlen=%d", + __func__, skb_queue_len(&bcs->squeue)); break; } skb_pull(skb, len); @@ -1215,28 +1177,16 @@ static void write_iso_tasklet(unsigned long data) */ static void read_iso_tasklet(unsigned long data) { - struct bc_state *bcs; - struct bas_bc_state *ubc; - struct cardstate *cs; + struct bc_state *bcs = (struct bc_state *) data; + struct bas_bc_state *ubc = bcs->hw.bas; + struct cardstate *cs = bcs->cs; struct urb *urb; char *rcvbuf; unsigned long flags; int totleft, numbytes, offset, frame, rc; - bcs = (struct bc_state *) data; - IFNULLRET(bcs); - ubc = bcs->hw.bas; - IFNULLRET(ubc); - cs = bcs->cs; - IFNULLRET(cs); - /* loop while more completed URBs arrive in the meantime */ for (;;) { - if (!atomic_read(&cs->connected)) { - warn("%s: disconnected", __func__); - return; - } - /* retrieve URB */ spin_lock_irqsave(&ubc->isoinlock, flags); if (!(urb = ubc->isoindone)) { @@ -1245,38 +1195,45 @@ static void read_iso_tasklet(unsigned long data) } ubc->isoindone = NULL; if (unlikely(ubc->loststatus != -EINPROGRESS)) { - warn("isochronous read overrun, dropped URB with status: %s, %d bytes lost", - get_usb_statmsg(ubc->loststatus), ubc->isoinlost); + dev_warn(cs->dev, + "isochronous read overrun, " + "dropped URB with status: %s, %d bytes lost\n", + get_usb_statmsg(ubc->loststatus), + ubc->isoinlost); ubc->loststatus = -EINPROGRESS; } spin_unlock_irqrestore(&ubc->isoinlock, flags); if (unlikely(!(atomic_read(&ubc->running)))) { - dbg(DEBUG_ISO, "%s: channel not running, dropped URB with status: %s", - __func__, get_usb_statmsg(urb->status)); + gig_dbg(DEBUG_ISO, + "%s: channel not running, " + "dropped URB with status: %s", + __func__, get_usb_statmsg(urb->status)); return; } switch (urb->status) { case 0: /* normal completion */ break; - case -EXDEV: /* inspect individual frames (we do that anyway) */ - dbg(DEBUG_ISO, "%s: URB partially completed", __func__); + case -EXDEV: /* inspect individual frames + (we do that anyway) */ + gig_dbg(DEBUG_ISO, "%s: URB partially completed", + __func__); break; case -ENOENT: case -ECONNRESET: - dbg(DEBUG_ISO, "%s: URB canceled", __func__); + gig_dbg(DEBUG_ISO, "%s: URB canceled", __func__); continue; /* -> skip */ case -EINPROGRESS: /* huh? */ - dbg(DEBUG_ISO, "%s: URB still pending", __func__); + gig_dbg(DEBUG_ISO, "%s: URB still pending", __func__); continue; /* -> skip */ case -EPIPE: - err("isochronous read stalled"); + dev_err(cs->dev, "isochronous read stalled\n"); error_hangup(bcs); continue; /* -> skip */ default: /* severe trouble */ - warn("isochronous read: %s", - get_usb_statmsg(urb->status)); + dev_warn(cs->dev, "isochronous read: %s\n", + get_usb_statmsg(urb->status)); goto error; } @@ -1284,33 +1241,44 @@ static void read_iso_tasklet(unsigned long data) totleft = urb->actual_length; for (frame = 0; totleft > 0 && frame < BAS_NUMFRAMES; frame++) { if (unlikely(urb->iso_frame_desc[frame].status)) { - warn("isochronous read: frame %d: %s", - frame, get_usb_statmsg(urb->iso_frame_desc[frame].status)); + dev_warn(cs->dev, + "isochronous read: frame %d: %s\n", + frame, + get_usb_statmsg( + urb->iso_frame_desc[frame].status)); break; } numbytes = urb->iso_frame_desc[frame].actual_length; if (unlikely(numbytes > BAS_MAXFRAME)) { - warn("isochronous read: frame %d: numbytes (%d) > BAS_MAXFRAME", - frame, numbytes); + dev_warn(cs->dev, + "isochronous read: frame %d: " + "numbytes (%d) > BAS_MAXFRAME\n", + frame, numbytes); break; } if (unlikely(numbytes > totleft)) { - warn("isochronous read: frame %d: numbytes (%d) > totleft (%d)", - frame, numbytes, totleft); + dev_warn(cs->dev, + "isochronous read: frame %d: " + "numbytes (%d) > totleft (%d)\n", + frame, numbytes, totleft); break; } offset = urb->iso_frame_desc[frame].offset; if (unlikely(offset + numbytes > BAS_INBUFSIZE)) { - warn("isochronous read: frame %d: offset (%d) + numbytes (%d) > BAS_INBUFSIZE", - frame, offset, numbytes); + dev_warn(cs->dev, + "isochronous read: frame %d: " + "offset (%d) + numbytes (%d) " + "> BAS_INBUFSIZE\n", + frame, offset, numbytes); break; } gigaset_isoc_receive(rcvbuf + offset, numbytes, bcs); totleft -= numbytes; } if (unlikely(totleft > 0)) - warn("isochronous read: %d data bytes missing", - totleft); + dev_warn(cs->dev, + "isochronous read: %d data bytes missing\n", + totleft); error: /* URB processed, resubmit */ @@ -1318,12 +1286,17 @@ static void read_iso_tasklet(unsigned long data) urb->iso_frame_desc[frame].status = 0; urb->iso_frame_desc[frame].actual_length = 0; } - urb->dev = bcs->cs->hw.bas->udev; /* clobbered by USB subsystem */ + /* urb->dev is clobbered by USB subsystem */ + urb->dev = bcs->cs->hw.bas->udev; urb->transfer_flags = URB_ISO_ASAP; urb->number_of_packets = BAS_NUMFRAMES; - if ((rc = usb_submit_urb(urb, SLAB_ATOMIC)) != 0) { - err("could not resubmit isochronous read URB: %s", - get_usb_statmsg(rc)); + spin_lock_irqsave(&cs->lock, flags); + rc = cs->connected ? usb_submit_urb(urb, SLAB_ATOMIC) : -ENODEV; + spin_unlock_irqrestore(&cs->lock, flags); + if (rc) { + dev_err(cs->dev, + "could not resubmit isochronous read URB: %s\n", + get_usb_statmsg(rc)); dump_urb(DEBUG_ISO, "resubmit iso read", urb); error_hangup(bcs); } @@ -1341,15 +1314,10 @@ static void read_iso_tasklet(unsigned long data) static void req_timeout(unsigned long data) { struct bc_state *bcs = (struct bc_state *) data; - struct bas_cardstate *ucs; + struct bas_cardstate *ucs = bcs->cs->hw.bas; int pending; unsigned long flags; - IFNULLRET(bcs); - IFNULLRET(bcs->cs); - ucs = bcs->cs->hw.bas; - IFNULLRET(ucs); - check_pending(ucs); spin_lock_irqsave(&ucs->lock, flags); @@ -1359,33 +1327,34 @@ static void req_timeout(unsigned long data) switch (pending) { case 0: /* no pending request */ - dbg(DEBUG_USBREQ, "%s: no request pending", __func__); + gig_dbg(DEBUG_USBREQ, "%s: no request pending", __func__); break; case HD_OPEN_ATCHANNEL: - err("timeout opening AT channel"); + dev_err(bcs->cs->dev, "timeout opening AT channel\n"); error_reset(bcs->cs); break; case HD_OPEN_B2CHANNEL: case HD_OPEN_B1CHANNEL: - err("timeout opening channel %d", bcs->channel + 1); + dev_err(bcs->cs->dev, "timeout opening channel %d\n", + bcs->channel + 1); error_hangup(bcs); break; case HD_CLOSE_ATCHANNEL: - err("timeout closing AT channel"); - //wake_up_interruptible(cs->initwait); - //FIXME need own wait queue? + dev_err(bcs->cs->dev, "timeout closing AT channel\n"); break; case HD_CLOSE_B2CHANNEL: case HD_CLOSE_B1CHANNEL: - err("timeout closing channel %d", bcs->channel + 1); + dev_err(bcs->cs->dev, "timeout closing channel %d\n", + bcs->channel + 1); break; default: - warn("request 0x%02x timed out, clearing", pending); + dev_warn(bcs->cs->dev, "request 0x%02x timed out, clearing\n", + pending); } } @@ -1398,18 +1367,14 @@ static void req_timeout(unsigned long data) */ static void write_ctrl_callback(struct urb *urb, struct pt_regs *regs) { - struct bas_cardstate *ucs; + struct bas_cardstate *ucs = urb->context; unsigned long flags; - IFNULLRET(urb); - IFNULLRET(urb->context); - IFNULLRET(cardstate); - - ucs = (struct bas_cardstate *) urb->context; spin_lock_irqsave(&ucs->lock, flags); if (urb->status && ucs->pending) { - err("control request 0x%02x failed: %s", - ucs->pending, get_usb_statmsg(urb->status)); + dev_err(&ucs->interface->dev, + "control request 0x%02x failed: %s\n", + ucs->pending, get_usb_statmsg(urb->status)); del_timer(&ucs->timer_ctrl); ucs->pending = 0; } @@ -1438,28 +1403,25 @@ static void write_ctrl_callback(struct urb *urb, struct pt_regs *regs) */ static int req_submit(struct bc_state *bcs, int req, int val, int timeout) { - struct bas_cardstate *ucs; + struct bas_cardstate *ucs = bcs->cs->hw.bas; int ret; unsigned long flags; - IFNULLRETVAL(bcs, -EINVAL); - IFNULLRETVAL(bcs->cs, -EINVAL); - ucs = bcs->cs->hw.bas; - IFNULLRETVAL(ucs, -EINVAL); - IFNULLRETVAL(ucs->urb_ctrl, -EINVAL); - - dbg(DEBUG_USBREQ, "-------> 0x%02x (%d)", req, val); + gig_dbg(DEBUG_USBREQ, "-------> 0x%02x (%d)", req, val); spin_lock_irqsave(&ucs->lock, flags); if (ucs->pending) { spin_unlock_irqrestore(&ucs->lock, flags); - err("submission of request 0x%02x failed: request 0x%02x still pending", - req, ucs->pending); + dev_err(bcs->cs->dev, + "submission of request 0x%02x failed: " + "request 0x%02x still pending\n", + req, ucs->pending); return -EBUSY; } if (ucs->urb_ctrl->status == -EINPROGRESS) { spin_unlock_irqrestore(&ucs->lock, flags); - err("could not submit request 0x%02x: URB busy", req); + dev_err(bcs->cs->dev, + "could not submit request 0x%02x: URB busy\n", req); return -EBUSY; } @@ -1469,19 +1431,19 @@ static int req_submit(struct bc_state *bcs, int req, int val, int timeout) ucs->dr_ctrl.wIndex = 0; ucs->dr_ctrl.wLength = 0; usb_fill_control_urb(ucs->urb_ctrl, ucs->udev, - usb_sndctrlpipe(ucs->udev, 0), - (unsigned char*) &ucs->dr_ctrl, NULL, 0, - write_ctrl_callback, ucs); + usb_sndctrlpipe(ucs->udev, 0), + (unsigned char*) &ucs->dr_ctrl, NULL, 0, + write_ctrl_callback, ucs); if ((ret = usb_submit_urb(ucs->urb_ctrl, SLAB_ATOMIC)) != 0) { - err("could not submit request 0x%02x: %s", - req, get_usb_statmsg(ret)); + dev_err(bcs->cs->dev, "could not submit request 0x%02x: %s\n", + req, get_usb_statmsg(ret)); spin_unlock_irqrestore(&ucs->lock, flags); return ret; } ucs->pending = req; if (timeout > 0) { - dbg(DEBUG_USBREQ, "setting timeout of %d/10 secs", timeout); + gig_dbg(DEBUG_USBREQ, "setting timeout of %d/10 secs", timeout); ucs->timer_ctrl.expires = jiffies + timeout * HZ / 10; ucs->timer_ctrl.data = (unsigned long) bcs; ucs->timer_ctrl.function = req_timeout; @@ -1504,19 +1466,18 @@ static int gigaset_init_bchannel(struct bc_state *bcs) { int req, ret; - IFNULLRETVAL(bcs, -EINVAL); - if ((ret = starturbs(bcs)) < 0) { - err("could not start isochronous I/O for channel %d", - bcs->channel + 1); + dev_err(bcs->cs->dev, + "could not start isochronous I/O for channel %d\n", + bcs->channel + 1); error_hangup(bcs); return ret; } req = bcs->channel ? HD_OPEN_B2CHANNEL : HD_OPEN_B1CHANNEL; if ((ret = req_submit(bcs, req, 0, BAS_TIMEOUT)) < 0) { - err("could not open channel %d: %s", - bcs->channel + 1, get_usb_statmsg(ret)); + dev_err(bcs->cs->dev, "could not open channel %d: %s\n", + bcs->channel + 1, get_usb_statmsg(ret)); stopurbs(bcs->hw.bas); error_hangup(bcs); } @@ -1537,8 +1498,6 @@ static int gigaset_close_bchannel(struct bc_state *bcs) { int req, ret; - IFNULLRETVAL(bcs, -EINVAL); - if (!(atomic_read(&bcs->cs->hw.bas->basstate) & (bcs->channel ? BS_B2OPEN : BS_B1OPEN))) { /* channel not running: just signal common.c */ @@ -1548,8 +1507,9 @@ static int gigaset_close_bchannel(struct bc_state *bcs) req = bcs->channel ? HD_CLOSE_B2CHANNEL : HD_CLOSE_B1CHANNEL; if ((ret = req_submit(bcs, req, 0, BAS_TIMEOUT)) < 0) - err("could not submit HD_CLOSE_BxCHANNEL request: %s", - get_usb_statmsg(ret)); + dev_err(bcs->cs->dev, + "could not submit HD_CLOSE_BxCHANNEL request: %s\n", + get_usb_statmsg(ret)); return ret; } @@ -1564,17 +1524,13 @@ static int gigaset_close_bchannel(struct bc_state *bcs) */ static void complete_cb(struct cardstate *cs) { - struct cmdbuf_t *cb; - - IFNULLRET(cs); - cb = cs->cmdbuf; - IFNULLRET(cb); + struct cmdbuf_t *cb = cs->cmdbuf; /* unqueue completed buffer */ cs->cmdbytes -= cs->curlen; - dbg(DEBUG_TRANSCMD | DEBUG_LOCKCMD, - "write_command: sent %u bytes, %u left", - cs->curlen, cs->cmdbytes); + gig_dbg(DEBUG_TRANSCMD|DEBUG_LOCKCMD, + "write_command: sent %u bytes, %u left", + cs->curlen, cs->cmdbytes); if ((cs->cmdbuf = cb->next) != NULL) { cs->cmdbuf->prev = NULL; cs->curlen = cs->cmdbuf->len; @@ -1600,15 +1556,9 @@ static int atwrite_submit(struct cardstate *cs, unsigned char *buf, int len); */ static void write_command_callback(struct urb *urb, struct pt_regs *regs) { - struct cardstate *cs; + struct cardstate *cs = urb->context; + struct bas_cardstate *ucs = cs->hw.bas; unsigned long flags; - struct bas_cardstate *ucs; - - IFNULLRET(urb); - cs = (struct cardstate *) urb->context; - IFNULLRET(cs); - ucs = cs->hw.bas; - IFNULLRET(ucs); /* check status */ switch (urb->status) { @@ -1618,22 +1568,27 @@ static void write_command_callback(struct urb *urb, struct pt_regs *regs) case -ECONNRESET: /* canceled (async) */ case -EINPROGRESS: /* pending */ /* ignore silently */ - dbg(DEBUG_USBREQ, - "%s: %s", __func__, get_usb_statmsg(urb->status)); + gig_dbg(DEBUG_USBREQ, "%s: %s", + __func__, get_usb_statmsg(urb->status)); return; default: /* any failure */ if (++ucs->retry_cmd_out > BAS_RETRY) { - warn("command write: %s, giving up after %d retries", - get_usb_statmsg(urb->status), ucs->retry_cmd_out); + dev_warn(cs->dev, + "command write: %s, " + "giving up after %d retries\n", + get_usb_statmsg(urb->status), + ucs->retry_cmd_out); break; } if (cs->cmdbuf == NULL) { - warn("command write: %s, cannot retry - cmdbuf gone", - get_usb_statmsg(urb->status)); + dev_warn(cs->dev, + "command write: %s, " + "cannot retry - cmdbuf gone\n", + get_usb_statmsg(urb->status)); break; } - notice("command write: %s, retry %d", - get_usb_statmsg(urb->status), ucs->retry_cmd_out); + dev_notice(cs->dev, "command write: %s, retry %d\n", + get_usb_statmsg(urb->status), ucs->retry_cmd_out); if (atwrite_submit(cs, cs->cmdbuf->buf, cs->cmdbuf->len) >= 0) /* resubmitted - bypass regular exit block */ return; @@ -1655,13 +1610,9 @@ static void write_command_callback(struct urb *urb, struct pt_regs *regs) static void atrdy_timeout(unsigned long data) { struct cardstate *cs = (struct cardstate *) data; - struct bas_cardstate *ucs; + struct bas_cardstate *ucs = cs->hw.bas; - IFNULLRET(cs); - ucs = cs->hw.bas; - IFNULLRET(ucs); - - warn("timeout waiting for HD_READY_SEND_ATDATA"); + dev_warn(cs->dev, "timeout waiting for HD_READY_SEND_ATDATA\n"); /* fake the missing signal - what else can I do? */ update_basstate(ucs, BS_ATREADY, BS_ATTIMER); @@ -1682,18 +1633,15 @@ static void atrdy_timeout(unsigned long data) */ static int atwrite_submit(struct cardstate *cs, unsigned char *buf, int len) { - struct bas_cardstate *ucs; + struct bas_cardstate *ucs = cs->hw.bas; + unsigned long flags; int ret; - IFNULLRETVAL(cs, -EFAULT); - ucs = cs->hw.bas; - IFNULLRETVAL(ucs, -EFAULT); - IFNULLRETVAL(ucs->urb_cmd_out, -EFAULT); - - dbg(DEBUG_USBREQ, "-------> HD_WRITE_ATMESSAGE (%d)", len); + gig_dbg(DEBUG_USBREQ, "-------> HD_WRITE_ATMESSAGE (%d)", len); if (ucs->urb_cmd_out->status == -EINPROGRESS) { - err("could not submit HD_WRITE_ATMESSAGE: URB busy"); + dev_err(cs->dev, + "could not submit HD_WRITE_ATMESSAGE: URB busy\n"); return -EBUSY; } @@ -1707,9 +1655,13 @@ static int atwrite_submit(struct cardstate *cs, unsigned char *buf, int len) (unsigned char*) &ucs->dr_cmd_out, buf, len, write_command_callback, cs); - if ((ret = usb_submit_urb(ucs->urb_cmd_out, SLAB_ATOMIC)) != 0) { - err("could not submit HD_WRITE_ATMESSAGE: %s", - get_usb_statmsg(ret)); + spin_lock_irqsave(&cs->lock, flags); + ret = cs->connected ? usb_submit_urb(ucs->urb_cmd_out, SLAB_ATOMIC) : -ENODEV; + spin_unlock_irqrestore(&cs->lock, flags); + + if (ret) { + dev_err(cs->dev, "could not submit HD_WRITE_ATMESSAGE: %s\n", + get_usb_statmsg(ret)); return ret; } @@ -1718,8 +1670,8 @@ static int atwrite_submit(struct cardstate *cs, unsigned char *buf, int len) /* start timeout if necessary */ if (!(atomic_read(&ucs->basstate) & BS_ATTIMER)) { - dbg(DEBUG_OUTPUT, - "setting ATREADY timeout of %d/10 secs", ATRDY_TIMEOUT); + gig_dbg(DEBUG_OUTPUT, "setting ATREADY timeout of %d/10 secs", + ATRDY_TIMEOUT); ucs->timer_atrdy.expires = jiffies + ATRDY_TIMEOUT * HZ / 10; ucs->timer_atrdy.data = (unsigned long) cs; ucs->timer_atrdy.function = atrdy_timeout; @@ -1740,21 +1692,17 @@ static int atwrite_submit(struct cardstate *cs, unsigned char *buf, int len) static int start_cbsend(struct cardstate *cs) { struct cmdbuf_t *cb; - struct bas_cardstate *ucs; + struct bas_cardstate *ucs = cs->hw.bas; unsigned long flags; int rc; int retval = 0; - IFNULLRETVAL(cs, -EFAULT); - ucs = cs->hw.bas; - IFNULLRETVAL(ucs, -EFAULT); - /* check if AT channel is open */ if (!(atomic_read(&ucs->basstate) & BS_ATOPEN)) { - dbg(DEBUG_TRANSCMD | DEBUG_LOCKCMD, "AT channel not open"); + gig_dbg(DEBUG_TRANSCMD|DEBUG_LOCKCMD, "AT channel not open"); rc = req_submit(cs->bcs, HD_OPEN_ATCHANNEL, 0, BAS_TIMEOUT); if (rc < 0) { - err("could not open AT channel"); + dev_err(cs->dev, "could not open AT channel\n"); /* flush command queue */ spin_lock_irqsave(&cs->cmdlock, flags); while (cs->cmdbuf != NULL) @@ -1792,27 +1740,23 @@ static int start_cbsend(struct cardstate *cs) * cs controller state structure * buf command string to send * len number of bytes to send (max. IF_WRITEBUF) - * wake_tasklet tasklet to run when transmission is completed (NULL if none) + * wake_tasklet tasklet to run when transmission is completed + * (NULL if none) * return value: * number of bytes queued on success * error code < 0 on error */ static int gigaset_write_cmd(struct cardstate *cs, - const unsigned char *buf, int len, - struct tasklet_struct *wake_tasklet) + const unsigned char *buf, int len, + struct tasklet_struct *wake_tasklet) { struct cmdbuf_t *cb; unsigned long flags; int status; gigaset_dbg_buffer(atomic_read(&cs->mstate) != MS_LOCKED ? - DEBUG_TRANSCMD : DEBUG_LOCKCMD, - "CMD Transmit", len, buf, 0); - - if (!atomic_read(&cs->connected)) { - err("%s: not connected", __func__); - return -ENODEV; - } + DEBUG_TRANSCMD : DEBUG_LOCKCMD, + "CMD Transmit", len, buf); if (len <= 0) return 0; /* nothing to do */ @@ -1820,7 +1764,7 @@ static int gigaset_write_cmd(struct cardstate *cs, if (len > IF_WRITEBUF) len = IF_WRITEBUF; if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) { - err("%s: out of memory", __func__); + dev_err(cs->dev, "%s: out of memory\n", __func__); return -ENOMEM; } @@ -1849,7 +1793,8 @@ static int gigaset_write_cmd(struct cardstate *cs, /* gigaset_write_room * tty_driver.write_room interface routine - * return number of characters the driver will accept to be written via gigaset_write_cmd + * return number of characters the driver will accept to be written via + * gigaset_write_cmd * parameter: * controller state structure * return value: @@ -1947,7 +1892,7 @@ static int gigaset_initbcshw(struct bc_state *bcs) return 0; } tasklet_init(&ubc->sent_tasklet, - &write_iso_tasklet, (unsigned long) bcs); + &write_iso_tasklet, (unsigned long) bcs); spin_lock_init(&ubc->isoinlock); for (i = 0; i < BAS_INURBS; ++i) @@ -1968,7 +1913,7 @@ static int gigaset_initbcshw(struct bc_state *bcs) ubc->shared0s = 0; ubc->stolen0s = 0; tasklet_init(&ubc->rcvd_tasklet, - &read_iso_tasklet, (unsigned long) bcs); + &read_iso_tasklet, (unsigned long) bcs); return 1; } @@ -2027,57 +1972,56 @@ static int gigaset_initcshw(struct cardstate *cs) */ static void freeurbs(struct cardstate *cs) { - struct bas_cardstate *ucs; + struct bas_cardstate *ucs = cs->hw.bas; struct bas_bc_state *ubc; int i, j; - IFNULLRET(cs); - ucs = cs->hw.bas; - IFNULLRET(ucs); - for (j = 0; j < 2; ++j) { ubc = cs->bcs[j].hw.bas; - IFNULLCONT(ubc); for (i = 0; i < BAS_OUTURBS; ++i) if (ubc->isoouturbs[i].urb) { usb_kill_urb(ubc->isoouturbs[i].urb); - dbg(DEBUG_INIT, - "%s: isoc output URB %d/%d unlinked", - __func__, j, i); + gig_dbg(DEBUG_INIT, + "%s: isoc output URB %d/%d unlinked", + __func__, j, i); usb_free_urb(ubc->isoouturbs[i].urb); ubc->isoouturbs[i].urb = NULL; } for (i = 0; i < BAS_INURBS; ++i) if (ubc->isoinurbs[i]) { usb_kill_urb(ubc->isoinurbs[i]); - dbg(DEBUG_INIT, - "%s: isoc input URB %d/%d unlinked", - __func__, j, i); + gig_dbg(DEBUG_INIT, + "%s: isoc input URB %d/%d unlinked", + __func__, j, i); usb_free_urb(ubc->isoinurbs[i]); ubc->isoinurbs[i] = NULL; } } if (ucs->urb_int_in) { usb_kill_urb(ucs->urb_int_in); - dbg(DEBUG_INIT, "%s: interrupt input URB unlinked", __func__); + gig_dbg(DEBUG_INIT, "%s: interrupt input URB unlinked", + __func__); usb_free_urb(ucs->urb_int_in); ucs->urb_int_in = NULL; } if (ucs->urb_cmd_out) { usb_kill_urb(ucs->urb_cmd_out); - dbg(DEBUG_INIT, "%s: command output URB unlinked", __func__); + gig_dbg(DEBUG_INIT, "%s: command output URB unlinked", + __func__); usb_free_urb(ucs->urb_cmd_out); ucs->urb_cmd_out = NULL; } if (ucs->urb_cmd_in) { usb_kill_urb(ucs->urb_cmd_in); - dbg(DEBUG_INIT, "%s: command input URB unlinked", __func__); + gig_dbg(DEBUG_INIT, "%s: command input URB unlinked", + __func__); usb_free_urb(ucs->urb_cmd_in); ucs->urb_cmd_in = NULL; } if (ucs->urb_ctrl) { usb_kill_urb(ucs->urb_ctrl); - dbg(DEBUG_INIT, "%s: control output URB unlinked", __func__); + gig_dbg(DEBUG_INIT, "%s: control output URB unlinked", + __func__); usb_free_urb(ucs->urb_ctrl); ucs->urb_ctrl = NULL; } @@ -2099,12 +2043,10 @@ static int gigaset_probe(struct usb_interface *interface, int i, j; int ret; - IFNULLRETVAL(udev, -ENODEV); - - dbg(DEBUG_ANY, - "%s: Check if device matches .. (Vendor: 0x%x, Product: 0x%x)", - __func__, le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct)); + gig_dbg(DEBUG_ANY, + "%s: Check if device matches .. (Vendor: 0x%x, Product: 0x%x)", + __func__, le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); /* See if the device offered us matches what we can accept */ if ((le16_to_cpu(udev->descriptor.idVendor) != USB_GIGA_VENDOR_ID) || @@ -2112,20 +2054,21 @@ static int gigaset_probe(struct usb_interface *interface, le16_to_cpu(udev->descriptor.idProduct) != USB_4175_PRODUCT_ID && le16_to_cpu(udev->descriptor.idProduct) != USB_SX303_PRODUCT_ID && le16_to_cpu(udev->descriptor.idProduct) != USB_SX353_PRODUCT_ID)) { - dbg(DEBUG_ANY, "%s: unmatched ID - exiting", __func__); + gig_dbg(DEBUG_ANY, "%s: unmatched ID - exiting", __func__); return -ENODEV; } /* set required alternate setting */ hostif = interface->cur_altsetting; if (hostif->desc.bAlternateSetting != 3) { - dbg(DEBUG_ANY, - "%s: wrong alternate setting %d - trying to switch", - __func__, hostif->desc.bAlternateSetting); + gig_dbg(DEBUG_ANY, + "%s: wrong alternate setting %d - trying to switch", + __func__, hostif->desc.bAlternateSetting); if (usb_set_interface(udev, hostif->desc.bInterfaceNumber, 3) < 0) { - warn("usb_set_interface failed, device %d interface %d altsetting %d", - udev->devnum, hostif->desc.bInterfaceNumber, - hostif->desc.bAlternateSetting); + dev_warn(&udev->dev, "usb_set_interface failed, " + "device %d interface %d altsetting %d\n", + udev->devnum, hostif->desc.bInterfaceNumber, + hostif->desc.bAlternateSetting); return -ENODEV; } hostif = interface->cur_altsetting; @@ -2134,23 +2077,28 @@ static int gigaset_probe(struct usb_interface *interface, /* Reject application specific interfaces */ if (hostif->desc.bInterfaceClass != 255) { - warn("%s: bInterfaceClass == %d", - __func__, hostif->desc.bInterfaceClass); + dev_warn(&udev->dev, "%s: bInterfaceClass == %d\n", + __func__, hostif->desc.bInterfaceClass); return -ENODEV; } - info("%s: Device matched (Vendor: 0x%x, Product: 0x%x)", - __func__, le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct)); + dev_info(&udev->dev, + "%s: Device matched (Vendor: 0x%x, Product: 0x%x)\n", + __func__, le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); cs = gigaset_getunassignedcs(driver); if (!cs) { - err("%s: no free cardstate", __func__); + dev_err(&udev->dev, "no free cardstate\n"); return -ENODEV; } ucs = cs->hw.bas; + + /* save off device structure ptrs for later use */ + usb_get_dev(udev); ucs->udev = udev; ucs->interface = interface; + cs->dev = &interface->dev; /* allocate URBs: * - one for the interrupt pipe @@ -2159,22 +2107,22 @@ static int gigaset_probe(struct usb_interface *interface, */ ucs->urb_int_in = usb_alloc_urb(0, SLAB_KERNEL); if (!ucs->urb_int_in) { - err("No free urbs available"); + dev_err(cs->dev, "no free urbs available\n"); goto error; } ucs->urb_cmd_in = usb_alloc_urb(0, SLAB_KERNEL); if (!ucs->urb_cmd_in) { - err("No free urbs available"); + dev_err(cs->dev, "no free urbs available\n"); goto error; } ucs->urb_cmd_out = usb_alloc_urb(0, SLAB_KERNEL); if (!ucs->urb_cmd_out) { - err("No free urbs available"); + dev_err(cs->dev, "no free urbs available\n"); goto error; } ucs->urb_ctrl = usb_alloc_urb(0, SLAB_KERNEL); if (!ucs->urb_ctrl) { - err("No free urbs available"); + dev_err(cs->dev, "no free urbs available\n"); goto error; } @@ -2184,7 +2132,7 @@ static int gigaset_probe(struct usb_interface *interface, ubc->isoouturbs[i].urb = usb_alloc_urb(BAS_NUMFRAMES, SLAB_KERNEL); if (!ubc->isoouturbs[i].urb) { - err("No free urbs available"); + dev_err(cs->dev, "no free urbs available\n"); goto error; } } @@ -2192,7 +2140,7 @@ static int gigaset_probe(struct usb_interface *interface, ubc->isoinurbs[i] = usb_alloc_urb(BAS_NUMFRAMES, SLAB_KERNEL); if (!ubc->isoinurbs[i]) { - err("No free urbs available"); + dev_err(cs->dev, "no free urbs available\n"); goto error; } } @@ -2204,13 +2152,14 @@ static int gigaset_probe(struct usb_interface *interface, /* Fill the interrupt urb and send it to the core */ endpoint = &hostif->endpoint[0].desc; usb_fill_int_urb(ucs->urb_int_in, udev, - usb_rcvintpipe(udev, - (endpoint->bEndpointAddress) & 0x0f), - ucs->int_in_buf, 3, read_int_callback, cs, - endpoint->bInterval); + usb_rcvintpipe(udev, + (endpoint->bEndpointAddress) & 0x0f), + ucs->int_in_buf, 3, read_int_callback, cs, + endpoint->bInterval); ret = usb_submit_urb(ucs->urb_int_in, SLAB_KERNEL); if (ret) { - err("could not submit interrupt URB: %s", get_usb_statmsg(ret)); + dev_err(cs->dev, "could not submit interrupt URB: %s\n", + get_usb_statmsg(ret)); goto error; } @@ -2221,18 +2170,18 @@ static int gigaset_probe(struct usb_interface *interface, /* tell common part that the device is ready */ if (startmode == SM_LOCKED) atomic_set(&cs->mstate, MS_LOCKED); - if (!gigaset_start(cs)) - goto error; /* save address of controller structure */ usb_set_intfdata(interface, cs); - /* set up device sysfs */ - gigaset_init_dev_sysfs(interface); + if (!gigaset_start(cs)) + goto error; + return 0; error: freeurbs(cs); + usb_set_intfdata(interface, NULL); gigaset_unassign(cs); return -ENODEV; } @@ -2245,23 +2194,22 @@ static void gigaset_disconnect(struct usb_interface *interface) struct cardstate *cs; struct bas_cardstate *ucs; - /* clear device sysfs */ - gigaset_free_dev_sysfs(interface); - cs = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); - IFNULLRET(cs); ucs = cs->hw.bas; - IFNULLRET(ucs); - info("disconnecting GigaSet base"); + dev_info(cs->dev, "disconnecting Gigaset base\n"); gigaset_stop(cs); freeurbs(cs); + usb_set_intfdata(interface, NULL); kfree(ucs->rcvbuf); ucs->rcvbuf = NULL; ucs->rcvbuf_size = 0; atomic_set(&ucs->basstate, 0); + usb_put_dev(ucs->udev); + ucs->interface = NULL; + ucs->udev = NULL; + cs->dev = NULL; gigaset_unassign(cs); } @@ -2293,13 +2241,14 @@ static int __init bas_gigaset_init(void) /* allocate memory for our driver state and intialize it */ if ((driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS, - GIGASET_MODULENAME, GIGASET_DEVNAME, - GIGASET_DEVFSNAME, &gigops, - THIS_MODULE)) == NULL) + GIGASET_MODULENAME, GIGASET_DEVNAME, + GIGASET_DEVFSNAME, &gigops, + THIS_MODULE)) == NULL) goto error; /* allocate memory for our device state and intialize it */ - cardstate = gigaset_initcs(driver, 2, 0, 0, cidmode, GIGASET_MODULENAME); + cardstate = gigaset_initcs(driver, 2, 0, 0, cidmode, + GIGASET_MODULENAME); if (!cardstate) goto error; @@ -2329,19 +2278,18 @@ error: if (cardstate) static void __exit bas_gigaset_exit(void) { gigaset_blockdriver(driver); /* => probe will fail - * => no gigaset_start any more - */ + * => no gigaset_start any more + */ gigaset_shutdown(cardstate); /* from now on, no isdn callback should be possible */ if (atomic_read(&cardstate->hw.bas->basstate) & BS_ATOPEN) { - dbg(DEBUG_ANY, "closing AT channel"); + gig_dbg(DEBUG_ANY, "closing AT channel"); if (req_submit(cardstate->bcs, - HD_CLOSE_ATCHANNEL, 0, BAS_TIMEOUT) >= 0) { - /* successfully submitted - wait for completion */ - //wait_event_interruptible(cs->initwait, !cs->hw.bas->pending); - //FIXME need own wait queue? wakeup? + HD_CLOSE_ATCHANNEL, 0, BAS_TIMEOUT) >= 0) { + /* successfully submitted */ + //FIXME wait for completion? } } diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c index 64371995c1a9..749b3da1236e 100644 --- a/drivers/isdn/gigaset/common.c +++ b/drivers/isdn/gigaset/common.c @@ -1,7 +1,7 @@ /* * Stuff used by all variants of the driver * - * Copyright (c) 2001 by Stefan Eilers , + * Copyright (c) 2001 by Stefan Eilers, * Hansjoerg Lipp , * Tilman Schmidt . * @@ -11,10 +11,6 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * ===================================================================== - * ToDo: ... - * ===================================================================== - * Version: $Id: common.c,v 1.104.4.22 2006/02/04 18:28:16 hjlipp Exp $ - * ===================================================================== */ #include "gigaset.h" @@ -23,7 +19,7 @@ #include /* Version Information */ -#define DRIVER_AUTHOR "Hansjoerg Lipp , Tilman Schmidt , Stefan Eilers " +#define DRIVER_AUTHOR "Hansjoerg Lipp , Tilman Schmidt , Stefan Eilers" #define DRIVER_DESC "Driver for Gigaset 307x" /* Module parameters */ @@ -32,21 +28,10 @@ EXPORT_SYMBOL_GPL(gigaset_debuglevel); module_param_named(debug, gigaset_debuglevel, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(debug, "debug level"); -/*====================================================================== - Prototypes of internal functions - */ - -//static void gigaset_process_response(int resp_code, int parameter, -// struct at_state_t *at_state, -// unsigned char ** pstring); -static struct cardstate *alloc_cs(struct gigaset_driver *drv); -static void free_cs(struct cardstate *cs); -static void make_valid(struct cardstate *cs, unsigned mask); -static void make_invalid(struct cardstate *cs, unsigned mask); - -#define VALID_MINOR 0x01 -#define VALID_ID 0x02 -#define ASSIGNED 0x04 +/* driver state flags */ +#define VALID_MINOR 0x01 +#define VALID_ID 0x02 +#define ASSIGNED 0x04 /* bitwise byte inversion table */ __u8 gigaset_invtab[256] = { @@ -86,42 +71,40 @@ __u8 gigaset_invtab[256] = { EXPORT_SYMBOL_GPL(gigaset_invtab); void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg, - size_t len, const unsigned char *buf, int from_user) + size_t len, const unsigned char *buf) { unsigned char outbuf[80]; - unsigned char inbuf[80 - 1]; - size_t numin; - const unsigned char *in; + unsigned char c; size_t space = sizeof outbuf - 1; unsigned char *out = outbuf; + size_t numin = len; - if (!from_user) { - in = buf; - numin = len; - } else { - numin = len < sizeof inbuf ? len : sizeof inbuf; - in = inbuf; - if (copy_from_user(inbuf, (const unsigned char __user *) buf, numin)) { - strncpy(inbuf, "", sizeof inbuf); - numin = sizeof "" - 1; + while (numin--) { + c = *buf++; + if (c == '~' || c == '^' || c == '\\') { + if (!space--) + break; + *out++ = '\\'; } - } - - for (; numin && space; --numin, ++in) { - --space; - if (*in >= 32) - *out++ = *in; - else { + if (c & 0x80) { + if (!space--) + break; + *out++ = '~'; + c ^= 0x80; + } + if (c < 0x20 || c == 0x7f) { + if (!space--) + break; *out++ = '^'; - if (space) { - *out++ = '@' + *in; - --space; - } + c ^= 0x40; } + if (!space--) + break; + *out++ = c; } *out = 0; - dbg(level, "%s (%u bytes): %s", msg, (unsigned) len, outbuf); + gig_dbg(level, "%s (%u bytes): %s", msg, (unsigned) len, outbuf); } EXPORT_SYMBOL_GPL(gigaset_dbg_buffer); @@ -146,11 +129,6 @@ int gigaset_enterconfigmode(struct cardstate *cs) { int i, r; - if (!atomic_read(&cs->connected)) { - err("not connected!"); - return -1; - } - cs->control_state = TIOCM_RTS; //FIXME r = setflags(cs, TIOCM_DTR, 200); @@ -174,7 +152,7 @@ int gigaset_enterconfigmode(struct cardstate *cs) return 0; error: - err("error %d on setuartbits!\n", -r); + dev_err(cs->dev, "error %d on setuartbits\n", -r); cs->control_state = TIOCM_RTS|TIOCM_DTR; // FIXME is this a good value? cs->ops->set_modem_ctrl(cs, 0, TIOCM_RTS|TIOCM_DTR); @@ -187,13 +165,13 @@ static int test_timeout(struct at_state_t *at_state) return 0; if (--at_state->timer_expires) { - dbg(DEBUG_MCMD, "decreased timer of %p to %lu", - at_state, at_state->timer_expires); + gig_dbg(DEBUG_MCMD, "decreased timer of %p to %lu", + at_state, at_state->timer_expires); return 0; } if (!gigaset_add_event(at_state->cs, at_state, EV_TIMEOUT, NULL, - atomic_read(&at_state->timer_index), NULL)) { + at_state->timer_index, NULL)) { //FIXME what should we do? } @@ -221,10 +199,10 @@ static void timer_tick(unsigned long data) if (test_timeout(at_state)) timeout = 1; - if (atomic_read(&cs->running)) { - mod_timer(&cs->timer, jiffies + GIG_TICK); + if (cs->running) { + mod_timer(&cs->timer, jiffies + msecs_to_jiffies(GIG_TICK)); if (timeout) { - dbg(DEBUG_CMD, "scheduling timeout"); + gig_dbg(DEBUG_CMD, "scheduling timeout"); tasklet_schedule(&cs->event_tasklet); } } @@ -238,13 +216,14 @@ int gigaset_get_channel(struct bc_state *bcs) spin_lock_irqsave(&bcs->cs->lock, flags); if (bcs->use_count) { - dbg(DEBUG_ANY, "could not allocate channel %d", bcs->channel); + gig_dbg(DEBUG_ANY, "could not allocate channel %d", + bcs->channel); spin_unlock_irqrestore(&bcs->cs->lock, flags); return 0; } ++bcs->use_count; bcs->busy = 1; - dbg(DEBUG_ANY, "allocated channel %d", bcs->channel); + gig_dbg(DEBUG_ANY, "allocated channel %d", bcs->channel); spin_unlock_irqrestore(&bcs->cs->lock, flags); return 1; } @@ -255,13 +234,13 @@ void gigaset_free_channel(struct bc_state *bcs) spin_lock_irqsave(&bcs->cs->lock, flags); if (!bcs->busy) { - dbg(DEBUG_ANY, "could not free channel %d", bcs->channel); + gig_dbg(DEBUG_ANY, "could not free channel %d", bcs->channel); spin_unlock_irqrestore(&bcs->cs->lock, flags); return; } --bcs->use_count; bcs->busy = 0; - dbg(DEBUG_ANY, "freed channel %d", bcs->channel); + gig_dbg(DEBUG_ANY, "freed channel %d", bcs->channel); spin_unlock_irqrestore(&bcs->cs->lock, flags); } @@ -274,14 +253,14 @@ int gigaset_get_channels(struct cardstate *cs) for (i = 0; i < cs->channels; ++i) if (cs->bcs[i].use_count) { spin_unlock_irqrestore(&cs->lock, flags); - dbg(DEBUG_ANY, "could not allocated all channels"); + gig_dbg(DEBUG_ANY, "could not allocate all channels"); return 0; } for (i = 0; i < cs->channels; ++i) ++cs->bcs[i].use_count; spin_unlock_irqrestore(&cs->lock, flags); - dbg(DEBUG_ANY, "allocated all channels"); + gig_dbg(DEBUG_ANY, "allocated all channels"); return 1; } @@ -291,7 +270,7 @@ void gigaset_free_channels(struct cardstate *cs) unsigned long flags; int i; - dbg(DEBUG_ANY, "unblocking all channels"); + gig_dbg(DEBUG_ANY, "unblocking all channels"); spin_lock_irqsave(&cs->lock, flags); for (i = 0; i < cs->channels; ++i) --cs->bcs[i].use_count; @@ -303,7 +282,7 @@ void gigaset_block_channels(struct cardstate *cs) unsigned long flags; int i; - dbg(DEBUG_ANY, "blocking all channels"); + gig_dbg(DEBUG_ANY, "blocking all channels"); spin_lock_irqsave(&cs->lock, flags); for (i = 0; i < cs->channels; ++i) ++cs->bcs[i].use_count; @@ -314,25 +293,27 @@ static void clear_events(struct cardstate *cs) { struct event_t *ev; unsigned head, tail; + unsigned long flags; - /* no locking needed (no reader/writer allowed) */ + spin_lock_irqsave(&cs->ev_lock, flags); - head = atomic_read(&cs->ev_head); - tail = atomic_read(&cs->ev_tail); + head = cs->ev_head; + tail = cs->ev_tail; while (tail != head) { ev = cs->events + head; kfree(ev->ptr); - head = (head + 1) % MAX_EVENTS; } - atomic_set(&cs->ev_head, tail); + cs->ev_head = tail; + + spin_unlock_irqrestore(&cs->ev_lock, flags); } struct event_t *gigaset_add_event(struct cardstate *cs, - struct at_state_t *at_state, int type, - void *ptr, int parameter, void *arg) + struct at_state_t *at_state, int type, + void *ptr, int parameter, void *arg) { unsigned long flags; unsigned next, tail; @@ -340,9 +321,9 @@ struct event_t *gigaset_add_event(struct cardstate *cs, spin_lock_irqsave(&cs->ev_lock, flags); - tail = atomic_read(&cs->ev_tail); + tail = cs->ev_tail; next = (tail + 1) % MAX_EVENTS; - if (unlikely(next == atomic_read(&cs->ev_head))) + if (unlikely(next == cs->ev_head)) err("event queue full"); else { event = cs->events + tail; @@ -352,7 +333,7 @@ struct event_t *gigaset_add_event(struct cardstate *cs, event->ptr = ptr; event->arg = arg; event->parameter = parameter; - atomic_set(&cs->ev_tail, next); + cs->ev_tail = next; } spin_unlock_irqrestore(&cs->ev_lock, flags); @@ -391,14 +372,14 @@ static void gigaset_freebcs(struct bc_state *bcs) { int i; - dbg(DEBUG_INIT, "freeing bcs[%d]->hw", bcs->channel); + gig_dbg(DEBUG_INIT, "freeing bcs[%d]->hw", bcs->channel); if (!bcs->cs->ops->freebcshw(bcs)) { - dbg(DEBUG_INIT, "failed"); + gig_dbg(DEBUG_INIT, "failed"); } - dbg(DEBUG_INIT, "clearing bcs[%d]->at_state", bcs->channel); + gig_dbg(DEBUG_INIT, "clearing bcs[%d]->at_state", bcs->channel); clear_at_state(&bcs->at_state); - dbg(DEBUG_INIT, "freeing bcs[%d]->skb", bcs->channel); + gig_dbg(DEBUG_INIT, "freeing bcs[%d]->skb", bcs->channel); if (bcs->skb) dev_kfree_skb(bcs->skb); @@ -408,6 +389,52 @@ static void gigaset_freebcs(struct bc_state *bcs) } } +static struct cardstate *alloc_cs(struct gigaset_driver *drv) +{ + unsigned long flags; + unsigned i; + static struct cardstate *ret = NULL; + + spin_lock_irqsave(&drv->lock, flags); + for (i = 0; i < drv->minors; ++i) { + if (!(drv->flags[i] & VALID_MINOR)) { + drv->flags[i] = VALID_MINOR; + ret = drv->cs + i; + } + if (ret) + break; + } + spin_unlock_irqrestore(&drv->lock, flags); + return ret; +} + +static void free_cs(struct cardstate *cs) +{ + unsigned long flags; + struct gigaset_driver *drv = cs->driver; + spin_lock_irqsave(&drv->lock, flags); + drv->flags[cs->minor_index] = 0; + spin_unlock_irqrestore(&drv->lock, flags); +} + +static void make_valid(struct cardstate *cs, unsigned mask) +{ + unsigned long flags; + struct gigaset_driver *drv = cs->driver; + spin_lock_irqsave(&drv->lock, flags); + drv->flags[cs->minor_index] |= mask; + spin_unlock_irqrestore(&drv->lock, flags); +} + +static void make_invalid(struct cardstate *cs, unsigned mask) +{ + unsigned long flags; + struct gigaset_driver *drv = cs->driver; + spin_lock_irqsave(&drv->lock, flags); + drv->flags[cs->minor_index] &= ~mask; + spin_unlock_irqrestore(&drv->lock, flags); +} + void gigaset_freecs(struct cardstate *cs) { int i; @@ -416,7 +443,7 @@ void gigaset_freecs(struct cardstate *cs) if (!cs) return; - down(&cs->sem); + mutex_lock(&cs->mutex); if (!cs->bcs) goto f_cs; @@ -424,8 +451,9 @@ void gigaset_freecs(struct cardstate *cs) goto f_bcs; spin_lock_irqsave(&cs->lock, flags); - atomic_set(&cs->running, 0); - spin_unlock_irqrestore(&cs->lock, flags); /* event handler and timer are not rescheduled below */ + cs->running = 0; + spin_unlock_irqrestore(&cs->lock, flags); /* event handler and timer are + not rescheduled below */ tasklet_kill(&cs->event_tasklet); del_timer_sync(&cs->timer); @@ -434,7 +462,7 @@ void gigaset_freecs(struct cardstate *cs) default: gigaset_if_free(cs); - dbg(DEBUG_INIT, "clearing hw"); + gig_dbg(DEBUG_INIT, "clearing hw"); cs->ops->freecshw(cs); //FIXME cmdbuf @@ -443,36 +471,36 @@ void gigaset_freecs(struct cardstate *cs) case 2: /* error in initcshw */ /* Deregister from LL */ make_invalid(cs, VALID_ID); - dbg(DEBUG_INIT, "clearing iif"); + gig_dbg(DEBUG_INIT, "clearing iif"); gigaset_i4l_cmd(cs, ISDN_STAT_UNLOAD); /* fall through */ case 1: /* error when regestering to LL */ - dbg(DEBUG_INIT, "clearing at_state"); + gig_dbg(DEBUG_INIT, "clearing at_state"); clear_at_state(&cs->at_state); dealloc_at_states(cs); /* fall through */ case 0: /* error in one call to initbcs */ for (i = 0; i < cs->channels; ++i) { - dbg(DEBUG_INIT, "clearing bcs[%d]", i); + gig_dbg(DEBUG_INIT, "clearing bcs[%d]", i); gigaset_freebcs(cs->bcs + i); } clear_events(cs); - dbg(DEBUG_INIT, "freeing inbuf"); + gig_dbg(DEBUG_INIT, "freeing inbuf"); kfree(cs->inbuf); } -f_bcs: dbg(DEBUG_INIT, "freeing bcs[]"); +f_bcs: gig_dbg(DEBUG_INIT, "freeing bcs[]"); kfree(cs->bcs); -f_cs: dbg(DEBUG_INIT, "freeing cs"); - up(&cs->sem); +f_cs: gig_dbg(DEBUG_INIT, "freeing cs"); + mutex_unlock(&cs->mutex); free_cs(cs); } EXPORT_SYMBOL_GPL(gigaset_freecs); void gigaset_at_init(struct at_state_t *at_state, struct bc_state *bcs, - struct cardstate *cs, int cid) + struct cardstate *cs, int cid) { int i; @@ -482,8 +510,8 @@ void gigaset_at_init(struct at_state_t *at_state, struct bc_state *bcs, at_state->pending_commands = 0; at_state->timer_expires = 0; at_state->timer_active = 0; - atomic_set(&at_state->timer_index, 0); - atomic_set(&at_state->seq_index, 0); + at_state->timer_index = 0; + at_state->seq_index = 0; at_state->ConState = 0; for (i = 0; i < STR_NUM; ++i) at_state->str_var[i] = NULL; @@ -501,7 +529,7 @@ void gigaset_at_init(struct at_state_t *at_state, struct bc_state *bcs, static void gigaset_inbuf_init(struct inbuf_t *inbuf, struct bc_state *bcs, - struct cardstate *cs, int inputstate) + struct cardstate *cs, int inputstate) /* inbuf->read must be allocated before! */ { atomic_set(&inbuf->head, 0); @@ -512,9 +540,50 @@ static void gigaset_inbuf_init(struct inbuf_t *inbuf, struct bc_state *bcs, inbuf->inputstate = inputstate; } +/* append received bytes to inbuf */ +int gigaset_fill_inbuf(struct inbuf_t *inbuf, const unsigned char *src, + unsigned numbytes) +{ + unsigned n, head, tail, bytesleft; + + gig_dbg(DEBUG_INTR, "received %u bytes", numbytes); + + if (!numbytes) + return 0; + + bytesleft = numbytes; + tail = atomic_read(&inbuf->tail); + head = atomic_read(&inbuf->head); + gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail); + + while (bytesleft) { + if (head > tail) + n = head - 1 - tail; + else if (head == 0) + n = (RBUFSIZE-1) - tail; + else + n = RBUFSIZE - tail; + if (!n) { + dev_err(inbuf->cs->dev, + "buffer overflow (%u bytes lost)", bytesleft); + break; + } + if (n > bytesleft) + n = bytesleft; + memcpy(inbuf->data + tail, src, n); + bytesleft -= n; + tail = (tail + n) % RBUFSIZE; + src += n; + } + gig_dbg(DEBUG_INTR, "setting tail to %u", tail); + atomic_set(&inbuf->tail, tail); + return numbytes != bytesleft; +} +EXPORT_SYMBOL_GPL(gigaset_fill_inbuf); + /* Initialize the b-channel structure */ static struct bc_state *gigaset_initbcs(struct bc_state *bcs, - struct cardstate *cs, int channel) + struct cardstate *cs, int channel) { int i; @@ -526,7 +595,7 @@ static struct bc_state *gigaset_initbcs(struct bc_state *bcs, bcs->trans_down = 0; bcs->trans_up = 0; - dbg(DEBUG_INIT, "setting up bcs[%d]->at_state", channel); + gig_dbg(DEBUG_INIT, "setting up bcs[%d]->at_state", channel); gigaset_at_init(&bcs->at_state, bcs, cs, -1); bcs->rcvbytes = 0; @@ -535,7 +604,7 @@ static struct bc_state *gigaset_initbcs(struct bc_state *bcs, bcs->emptycount = 0; #endif - dbg(DEBUG_INIT, "allocating bcs[%d]->skb", channel); + gig_dbg(DEBUG_INIT, "allocating bcs[%d]->skb", channel); bcs->fcs = PPP_INITFCS; bcs->inputstate = 0; if (cs->ignoreframes) { @@ -544,7 +613,7 @@ static struct bc_state *gigaset_initbcs(struct bc_state *bcs, } else if ((bcs->skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL) skb_reserve(bcs->skb, HW_HDR_LEN); else { - warn("could not allocate skb"); + dev_warn(cs->dev, "could not allocate skb\n"); bcs->inputstate |= INS_skip_frame; } @@ -559,14 +628,13 @@ static struct bc_state *gigaset_initbcs(struct bc_state *bcs, for (i = 0; i < AT_NUM; ++i) bcs->commands[i] = NULL; - dbg(DEBUG_INIT, " setting up bcs[%d]->hw", channel); + gig_dbg(DEBUG_INIT, " setting up bcs[%d]->hw", channel); if (cs->ops->initbcshw(bcs)) return bcs; -//error: - dbg(DEBUG_INIT, " failed"); + gig_dbg(DEBUG_INIT, " failed"); - dbg(DEBUG_INIT, " freeing bcs[%d]->skb", channel); + gig_dbg(DEBUG_INIT, " freeing bcs[%d]->skb", channel); if (bcs->skb) dev_kfree_skb(bcs->skb); @@ -578,9 +646,10 @@ static struct bc_state *gigaset_initbcs(struct bc_state *bcs, * Calls hardware dependent gigaset_initcshw() function * Calls B channel initialization function gigaset_initbcs() for each B channel * parameters: - * drv hardware driver the device belongs to + * drv hardware driver the device belongs to * channels number of B channels supported by device - * onechannel !=0: B channel data and AT commands share one communication channel + * onechannel !=0: B channel data and AT commands share one + * communication channel * ==0: B channels have separate communication channels * ignoreframes number of frames to ignore after setting up B channel * cidmode !=0: start in CallID mode @@ -593,17 +662,18 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, int cidmode, const char *modulename) { struct cardstate *cs = NULL; + unsigned long flags; int i; - dbg(DEBUG_INIT, "allocating cs"); + gig_dbg(DEBUG_INIT, "allocating cs"); cs = alloc_cs(drv); if (!cs) goto error; - dbg(DEBUG_INIT, "allocating bcs[0..%d]", channels - 1); + gig_dbg(DEBUG_INIT, "allocating bcs[0..%d]", channels - 1); cs->bcs = kmalloc(channels * sizeof(struct bc_state), GFP_KERNEL); if (!cs->bcs) goto error; - dbg(DEBUG_INIT, "allocating inbuf"); + gig_dbg(DEBUG_INIT, "allocating inbuf"); cs->inbuf = kmalloc(sizeof(struct inbuf_t), GFP_KERNEL); if (!cs->inbuf) goto error; @@ -613,19 +683,23 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, cs->onechannel = onechannel; cs->ignoreframes = ignoreframes; INIT_LIST_HEAD(&cs->temp_at_states); - atomic_set(&cs->running, 0); + cs->running = 0; init_timer(&cs->timer); /* clear next & prev */ spin_lock_init(&cs->ev_lock); - atomic_set(&cs->ev_tail, 0); - atomic_set(&cs->ev_head, 0); - init_MUTEX_LOCKED(&cs->sem); - tasklet_init(&cs->event_tasklet, &gigaset_handle_event, (unsigned long) cs); + cs->ev_tail = 0; + cs->ev_head = 0; + mutex_init(&cs->mutex); + mutex_lock(&cs->mutex); + + tasklet_init(&cs->event_tasklet, &gigaset_handle_event, + (unsigned long) cs); atomic_set(&cs->commands_pending, 0); cs->cur_at_seq = 0; cs->gotfwver = -1; cs->open_count = 0; + cs->dev = NULL; cs->tty = NULL; - atomic_set(&cs->cidmode, cidmode != 0); + cs->cidmode = cidmode != 0; //if(onechannel) { //FIXME cs->tabnocid = gigaset_tab_nocid_m10x; @@ -642,50 +716,43 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, atomic_set(&cs->mstate, MS_UNINITIALIZED); for (i = 0; i < channels; ++i) { - dbg(DEBUG_INIT, "setting up bcs[%d].read", i); + gig_dbg(DEBUG_INIT, "setting up bcs[%d].read", i); if (!gigaset_initbcs(cs->bcs + i, cs, i)) goto error; } ++cs->cs_init; - dbg(DEBUG_INIT, "setting up at_state"); + gig_dbg(DEBUG_INIT, "setting up at_state"); spin_lock_init(&cs->lock); gigaset_at_init(&cs->at_state, NULL, cs, 0); cs->dle = 0; cs->cbytes = 0; - dbg(DEBUG_INIT, "setting up inbuf"); + gig_dbg(DEBUG_INIT, "setting up inbuf"); if (onechannel) { //FIXME distinction necessary? gigaset_inbuf_init(cs->inbuf, cs->bcs, cs, INS_command); } else gigaset_inbuf_init(cs->inbuf, NULL, cs, INS_command); - atomic_set(&cs->connected, 0); + cs->connected = 0; + cs->isdn_up = 0; - dbg(DEBUG_INIT, "setting up cmdbuf"); + gig_dbg(DEBUG_INIT, "setting up cmdbuf"); cs->cmdbuf = cs->lastcmdbuf = NULL; spin_lock_init(&cs->cmdlock); cs->curlen = 0; cs->cmdbytes = 0; - /* - * Tell the ISDN4Linux subsystem (the LL) that - * a driver for a USB-Device is available ! - * If this is done, "isdnctrl" is able to bind a device for this driver even - * if no physical usb-device is currently connected. - * But this device will just be accessable if a physical USB device is connected - * (via "gigaset_probe") . - */ - dbg(DEBUG_INIT, "setting up iif"); + gig_dbg(DEBUG_INIT, "setting up iif"); if (!gigaset_register_to_LL(cs, modulename)) { - err("register_isdn=>error"); + err("register_isdn failed"); goto error; } make_valid(cs, VALID_ID); ++cs->cs_init; - dbg(DEBUG_INIT, "setting up hw"); + gig_dbg(DEBUG_INIT, "setting up hw"); if (!cs->ops->initcshw(cs)) goto error; @@ -693,27 +760,29 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, gigaset_if_init(cs); - atomic_set(&cs->running, 1); - cs->timer.data = (unsigned long) cs; - cs->timer.function = timer_tick; - cs->timer.expires = jiffies + GIG_TICK; + spin_lock_irqsave(&cs->lock, flags); + cs->running = 1; + spin_unlock_irqrestore(&cs->lock, flags); + setup_timer(&cs->timer, timer_tick, (unsigned long) cs); + cs->timer.expires = jiffies + msecs_to_jiffies(GIG_TICK); /* FIXME: can jiffies increase too much until the timer is added? * Same problem(?) with mod_timer() in timer_tick(). */ add_timer(&cs->timer); - dbg(DEBUG_INIT, "cs initialized!"); - up(&cs->sem); + gig_dbg(DEBUG_INIT, "cs initialized"); + mutex_unlock(&cs->mutex); return cs; error: if (cs) - up(&cs->sem); - dbg(DEBUG_INIT, "failed"); + mutex_unlock(&cs->mutex); + gig_dbg(DEBUG_INIT, "failed"); gigaset_freecs(cs); return NULL; } EXPORT_SYMBOL_GPL(gigaset_initcs); -/* ReInitialize the b-channel structure */ /* e.g. called on hangup, disconnect */ +/* ReInitialize the b-channel structure */ +/* e.g. called on hangup, disconnect */ void gigaset_bcs_reinit(struct bc_state *bcs) { struct sk_buff *skb; @@ -723,12 +792,12 @@ void gigaset_bcs_reinit(struct bc_state *bcs) while ((skb = skb_dequeue(&bcs->squeue)) != NULL) dev_kfree_skb(skb); - spin_lock_irqsave(&cs->lock, flags); //FIXME + spin_lock_irqsave(&cs->lock, flags); clear_at_state(&bcs->at_state); bcs->at_state.ConState = 0; bcs->at_state.timer_active = 0; bcs->at_state.timer_expires = 0; - bcs->at_state.cid = -1; /* No CID defined */ + bcs->at_state.cid = -1; /* No CID defined */ spin_unlock_irqrestore(&cs->lock, flags); bcs->inputstate = 0; @@ -803,11 +872,14 @@ static void cleanup_cs(struct cardstate *cs) int gigaset_start(struct cardstate *cs) { - if (down_interruptible(&cs->sem)) - return 0; - //info("USB device for Gigaset 307x now attached to Dev %d", ucs->minor); + unsigned long flags; - atomic_set(&cs->connected, 1); + if (mutex_lock_interruptible(&cs->mutex)) + return 0; + + spin_lock_irqsave(&cs->lock, flags); + cs->connected = 1; + spin_unlock_irqrestore(&cs->lock, flags); if (atomic_read(&cs->mstate) != MS_LOCKED) { cs->ops->set_modem_ctrl(cs, 0, TIOCM_DTR|TIOCM_RTS); @@ -826,23 +898,26 @@ int gigaset_start(struct cardstate *cs) goto error; } - dbg(DEBUG_CMD, "scheduling START"); + gig_dbg(DEBUG_CMD, "scheduling START"); gigaset_schedule_event(cs); wait_event(cs->waitqueue, !cs->waiting); - up(&cs->sem); + /* set up device sysfs */ + gigaset_init_dev_sysfs(cs); + + mutex_unlock(&cs->mutex); return 1; error: - up(&cs->sem); + mutex_unlock(&cs->mutex); return 0; } EXPORT_SYMBOL_GPL(gigaset_start); void gigaset_shutdown(struct cardstate *cs) { - down(&cs->sem); + mutex_lock(&cs->mutex); cs->waiting = 1; @@ -851,11 +926,11 @@ void gigaset_shutdown(struct cardstate *cs) goto exit; } - dbg(DEBUG_CMD, "scheduling SHUTDOWN"); + gig_dbg(DEBUG_CMD, "scheduling SHUTDOWN"); gigaset_schedule_event(cs); if (wait_event_interruptible(cs->waitqueue, !cs->waiting)) { - warn("aborted"); + warn("%s: aborted", __func__); //FIXME } @@ -872,15 +947,13 @@ void gigaset_shutdown(struct cardstate *cs) cleanup_cs(cs); exit: - up(&cs->sem); + mutex_unlock(&cs->mutex); } EXPORT_SYMBOL_GPL(gigaset_shutdown); void gigaset_stop(struct cardstate *cs) { - down(&cs->sem); - - atomic_set(&cs->connected, 0); + mutex_lock(&cs->mutex); cs->waiting = 1; @@ -889,21 +962,21 @@ void gigaset_stop(struct cardstate *cs) goto exit; } - dbg(DEBUG_CMD, "scheduling STOP"); + gig_dbg(DEBUG_CMD, "scheduling STOP"); gigaset_schedule_event(cs); if (wait_event_interruptible(cs->waitqueue, !cs->waiting)) { - warn("aborted"); + warn("%s: aborted", __func__); //FIXME } - /* Tell the LL that the device is not available .. */ - gigaset_i4l_cmd(cs, ISDN_STAT_STOP); // FIXME move to event layer? + /* clear device sysfs */ + gigaset_free_dev_sysfs(cs); cleanup_cs(cs); exit: - up(&cs->sem); + mutex_unlock(&cs->mutex); } EXPORT_SYMBOL_GPL(gigaset_stop); @@ -947,31 +1020,25 @@ void gigaset_debugdrivers(void) spin_lock_irqsave(&driver_lock, flags); list_for_each_entry(drv, &drivers, list) { - dbg(DEBUG_DRIVER, "driver %p", drv); + gig_dbg(DEBUG_DRIVER, "driver %p", drv); spin_lock(&drv->lock); for (i = 0; i < drv->minors; ++i) { - dbg(DEBUG_DRIVER, " index %u", i); - dbg(DEBUG_DRIVER, " flags 0x%02x", drv->flags[i]); + gig_dbg(DEBUG_DRIVER, " index %u", i); + gig_dbg(DEBUG_DRIVER, " flags 0x%02x", + drv->flags[i]); cs = drv->cs + i; - dbg(DEBUG_DRIVER, " cardstate %p", cs); - dbg(DEBUG_DRIVER, " minor_index %u", cs->minor_index); - dbg(DEBUG_DRIVER, " driver %p", cs->driver); - dbg(DEBUG_DRIVER, " i4l id %d", cs->myid); + gig_dbg(DEBUG_DRIVER, " cardstate %p", cs); + gig_dbg(DEBUG_DRIVER, " minor_index %u", + cs->minor_index); + gig_dbg(DEBUG_DRIVER, " driver %p", cs->driver); + gig_dbg(DEBUG_DRIVER, " i4l id %d", cs->myid); } spin_unlock(&drv->lock); } spin_unlock_irqrestore(&driver_lock, flags); } -EXPORT_SYMBOL_GPL(gigaset_debugdrivers); -struct cardstate *gigaset_get_cs_by_tty(struct tty_struct *tty) -{ - if (tty->index < 0 || tty->index >= tty->driver->num) - return NULL; - return gigaset_get_cs_by_minor(tty->index + tty->driver->minor_start); -} - -struct cardstate *gigaset_get_cs_by_minor(unsigned minor) +static struct cardstate *gigaset_get_cs_by_minor(unsigned minor) { unsigned long flags; static struct cardstate *ret = NULL; @@ -994,6 +1061,13 @@ struct cardstate *gigaset_get_cs_by_minor(unsigned minor) return ret; } +struct cardstate *gigaset_get_cs_by_tty(struct tty_struct *tty) +{ + if (tty->index < 0 || tty->index >= tty->driver->num) + return NULL; + return gigaset_get_cs_by_minor(tty->index + tty->driver->minor_start); +} + void gigaset_freedriver(struct gigaset_driver *drv) { unsigned long flags; @@ -1014,20 +1088,20 @@ EXPORT_SYMBOL_GPL(gigaset_freedriver); /* gigaset_initdriver * Allocate and initialize gigaset_driver structure. Initialize interface. * parameters: - * minor First minor number - * minors Number of minors this driver can handle - * procname Name of the driver (e.g. for /proc/tty/drivers, path in /proc/driver) - * devname Name of the device files (prefix without minor number) - * devfsname Devfs name of the device files without %d + * minor First minor number + * minors Number of minors this driver can handle + * procname Name of the driver + * devname Name of the device files (prefix without minor number) + * devfsname Devfs name of the device files without %d * return value: - * Pointer to the gigaset_driver structure on success, NULL on failure. + * Pointer to the gigaset_driver structure on success, NULL on failure. */ struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors, - const char *procname, - const char *devname, - const char *devfsname, - const struct gigaset_ops *ops, - struct module *owner) + const char *procname, + const char *devname, + const char *devfsname, + const struct gigaset_ops *ops, + struct module *owner) { struct gigaset_driver *drv; unsigned long flags; @@ -1036,8 +1110,9 @@ struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors, drv = kmalloc(sizeof *drv, GFP_KERNEL); if (!drv) return NULL; + if (!try_module_get(owner)) - return NULL; + goto out1; drv->cs = NULL; drv->have_tty = 0; @@ -1051,10 +1126,11 @@ struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors, drv->cs = kmalloc(minors * sizeof *drv->cs, GFP_KERNEL); if (!drv->cs) - goto out1; + goto out2; + drv->flags = kmalloc(minors * sizeof *drv->flags, GFP_KERNEL); if (!drv->flags) - goto out2; + goto out3; for (i = 0; i < minors; ++i) { drv->flags[i] = 0; @@ -1071,61 +1147,16 @@ struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors, return drv; -out2: +out3: kfree(drv->cs); +out2: + module_put(owner); out1: kfree(drv); - module_put(owner); return NULL; } EXPORT_SYMBOL_GPL(gigaset_initdriver); -static struct cardstate *alloc_cs(struct gigaset_driver *drv) -{ - unsigned long flags; - unsigned i; - static struct cardstate *ret = NULL; - - spin_lock_irqsave(&drv->lock, flags); - for (i = 0; i < drv->minors; ++i) { - if (!(drv->flags[i] & VALID_MINOR)) { - drv->flags[i] = VALID_MINOR; - ret = drv->cs + i; - } - if (ret) - break; - } - spin_unlock_irqrestore(&drv->lock, flags); - return ret; -} - -static void free_cs(struct cardstate *cs) -{ - unsigned long flags; - struct gigaset_driver *drv = cs->driver; - spin_lock_irqsave(&drv->lock, flags); - drv->flags[cs->minor_index] = 0; - spin_unlock_irqrestore(&drv->lock, flags); -} - -static void make_valid(struct cardstate *cs, unsigned mask) -{ - unsigned long flags; - struct gigaset_driver *drv = cs->driver; - spin_lock_irqsave(&drv->lock, flags); - drv->flags[cs->minor_index] |= mask; - spin_unlock_irqrestore(&drv->lock, flags); -} - -static void make_invalid(struct cardstate *cs, unsigned mask) -{ - unsigned long flags; - struct gigaset_driver *drv = cs->driver; - spin_lock_irqsave(&drv->lock, flags); - drv->flags[cs->minor_index] &= ~mask; - spin_unlock_irqrestore(&drv->lock, flags); -} - /* For drivers without fixed assignment device<->cardstate (usb) */ struct cardstate *gigaset_getunassignedcs(struct gigaset_driver *drv) { diff --git a/drivers/isdn/gigaset/ev-layer.c b/drivers/isdn/gigaset/ev-layer.c index fdcb80bb21c7..1ba3424a286b 100644 --- a/drivers/isdn/gigaset/ev-layer.c +++ b/drivers/isdn/gigaset/ev-layer.c @@ -1,7 +1,7 @@ /* * Stuff used by all variants of the driver * - * Copyright (c) 2001 by Stefan Eilers , + * Copyright (c) 2001 by Stefan Eilers, * Hansjoerg Lipp , * Tilman Schmidt . * @@ -11,82 +11,78 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * ===================================================================== - * ToDo: ... - * ===================================================================== - * Version: $Id: ev-layer.c,v 1.4.2.18 2006/02/04 18:28:16 hjlipp Exp $ - * ===================================================================== */ #include "gigaset.h" /* ========================================================== */ /* bit masks for pending commands */ -#define PC_INIT 0x004 -#define PC_DLE0 0x008 -#define PC_DLE1 0x010 -#define PC_CID 0x080 -#define PC_NOCID 0x100 -#define PC_HUP 0x002 -#define PC_DIAL 0x001 -#define PC_ACCEPT 0x040 -#define PC_SHUTDOWN 0x020 -#define PC_CIDMODE 0x200 -#define PC_UMMODE 0x400 +#define PC_DIAL 0x001 +#define PC_HUP 0x002 +#define PC_INIT 0x004 +#define PC_DLE0 0x008 +#define PC_DLE1 0x010 +#define PC_SHUTDOWN 0x020 +#define PC_ACCEPT 0x040 +#define PC_CID 0x080 +#define PC_NOCID 0x100 +#define PC_CIDMODE 0x200 +#define PC_UMMODE 0x400 /* types of modem responses */ -#define RT_NOTHING 0 -#define RT_ZSAU 1 -#define RT_RING 2 -#define RT_NUMBER 3 -#define RT_STRING 4 -#define RT_HEX 5 -#define RT_ZCAU 6 +#define RT_NOTHING 0 +#define RT_ZSAU 1 +#define RT_RING 2 +#define RT_NUMBER 3 +#define RT_STRING 4 +#define RT_HEX 5 +#define RT_ZCAU 6 /* Possible ASCII responses */ -#define RSP_OK 0 -//#define RSP_BUSY 1 -//#define RSP_CONNECT 2 -#define RSP_ZGCI 3 -#define RSP_RING 4 -#define RSP_ZAOC 5 -#define RSP_ZCSTR 6 -#define RSP_ZCFGT 7 -#define RSP_ZCFG 8 -#define RSP_ZCCR 9 -#define RSP_EMPTY 10 -#define RSP_ZLOG 11 -#define RSP_ZCAU 12 -#define RSP_ZMWI 13 -#define RSP_ZABINFO 14 -#define RSP_ZSMLSTCHG 15 -#define RSP_VAR 100 -#define RSP_ZSAU (RSP_VAR + VAR_ZSAU) -#define RSP_ZDLE (RSP_VAR + VAR_ZDLE) -#define RSP_ZVLS (RSP_VAR + VAR_ZVLS) -#define RSP_ZCTP (RSP_VAR + VAR_ZCTP) -#define RSP_STR (RSP_VAR + VAR_NUM) -#define RSP_NMBR (RSP_STR + STR_NMBR) -#define RSP_ZCPN (RSP_STR + STR_ZCPN) -#define RSP_ZCON (RSP_STR + STR_ZCON) -#define RSP_ZBC (RSP_STR + STR_ZBC) -#define RSP_ZHLC (RSP_STR + STR_ZHLC) -#define RSP_ERROR -1 /* ERROR */ -#define RSP_WRONG_CID -2 /* unknown cid in cmd */ -//#define RSP_EMPTY -3 -#define RSP_UNKNOWN -4 /* unknown response */ -#define RSP_FAIL -5 /* internal error */ -#define RSP_INVAL -6 /* invalid response */ +#define RSP_OK 0 +//#define RSP_BUSY 1 +//#define RSP_CONNECT 2 +#define RSP_ZGCI 3 +#define RSP_RING 4 +#define RSP_ZAOC 5 +#define RSP_ZCSTR 6 +#define RSP_ZCFGT 7 +#define RSP_ZCFG 8 +#define RSP_ZCCR 9 +#define RSP_EMPTY 10 +#define RSP_ZLOG 11 +#define RSP_ZCAU 12 +#define RSP_ZMWI 13 +#define RSP_ZABINFO 14 +#define RSP_ZSMLSTCHG 15 +#define RSP_VAR 100 +#define RSP_ZSAU (RSP_VAR + VAR_ZSAU) +#define RSP_ZDLE (RSP_VAR + VAR_ZDLE) +#define RSP_ZVLS (RSP_VAR + VAR_ZVLS) +#define RSP_ZCTP (RSP_VAR + VAR_ZCTP) +#define RSP_STR (RSP_VAR + VAR_NUM) +#define RSP_NMBR (RSP_STR + STR_NMBR) +#define RSP_ZCPN (RSP_STR + STR_ZCPN) +#define RSP_ZCON (RSP_STR + STR_ZCON) +#define RSP_ZBC (RSP_STR + STR_ZBC) +#define RSP_ZHLC (RSP_STR + STR_ZHLC) +#define RSP_ERROR -1 /* ERROR */ +#define RSP_WRONG_CID -2 /* unknown cid in cmd */ +//#define RSP_EMPTY -3 +#define RSP_UNKNOWN -4 /* unknown response */ +#define RSP_FAIL -5 /* internal error */ +#define RSP_INVAL -6 /* invalid response */ -#define RSP_NONE -19 -#define RSP_STRING -20 -#define RSP_NULL -21 -//#define RSP_RETRYFAIL -22 -//#define RSP_RETRY -23 -//#define RSP_SKIP -24 -#define RSP_INIT -27 -#define RSP_ANY -26 -#define RSP_LAST -28 -#define RSP_NODEV -9 +#define RSP_NONE -19 +#define RSP_STRING -20 +#define RSP_NULL -21 +//#define RSP_RETRYFAIL -22 +//#define RSP_RETRY -23 +//#define RSP_SKIP -24 +#define RSP_INIT -27 +#define RSP_ANY -26 +#define RSP_LAST -28 +#define RSP_NODEV -9 /* actions for process_response */ #define ACT_NOTHING 0 @@ -112,7 +108,7 @@ #define ACT_DISCONNECT 20 #define ACT_CONNECT 21 #define ACT_REMOTEREJECT 22 -#define ACT_CONNTIMEOUT 23 +#define ACT_CONNTIMEOUT 23 #define ACT_REMOTEHUP 24 #define ACT_ABORTHUP 25 #define ACT_ICALL 26 @@ -127,40 +123,40 @@ #define ACT_ERROR 35 #define ACT_ABORTCID 36 #define ACT_ZCAU 37 -#define ACT_NOTIFY_BC_DOWN 38 -#define ACT_NOTIFY_BC_UP 39 -#define ACT_DIAL 40 -#define ACT_ACCEPT 41 -#define ACT_PROTO_L2 42 -#define ACT_HUP 43 -#define ACT_IF_LOCK 44 -#define ACT_START 45 -#define ACT_STOP 46 -#define ACT_FAKEDLE0 47 -#define ACT_FAKEHUP 48 -#define ACT_FAKESDOWN 49 -#define ACT_SHUTDOWN 50 -#define ACT_PROC_CIDMODE 51 -#define ACT_UMODESET 52 -#define ACT_FAILUMODE 53 -#define ACT_CMODESET 54 -#define ACT_FAILCMODE 55 -#define ACT_IF_VER 56 +#define ACT_NOTIFY_BC_DOWN 38 +#define ACT_NOTIFY_BC_UP 39 +#define ACT_DIAL 40 +#define ACT_ACCEPT 41 +#define ACT_PROTO_L2 42 +#define ACT_HUP 43 +#define ACT_IF_LOCK 44 +#define ACT_START 45 +#define ACT_STOP 46 +#define ACT_FAKEDLE0 47 +#define ACT_FAKEHUP 48 +#define ACT_FAKESDOWN 49 +#define ACT_SHUTDOWN 50 +#define ACT_PROC_CIDMODE 51 +#define ACT_UMODESET 52 +#define ACT_FAILUMODE 53 +#define ACT_CMODESET 54 +#define ACT_FAILCMODE 55 +#define ACT_IF_VER 56 #define ACT_CMD 100 /* at command sequences */ -#define SEQ_NONE 0 -#define SEQ_INIT 100 -#define SEQ_DLE0 200 -#define SEQ_DLE1 250 -#define SEQ_CID 300 -#define SEQ_NOCID 350 -#define SEQ_HUP 400 -#define SEQ_DIAL 600 -#define SEQ_ACCEPT 720 -#define SEQ_SHUTDOWN 500 -#define SEQ_CIDMODE 10 -#define SEQ_UMMODE 11 +#define SEQ_NONE 0 +#define SEQ_INIT 100 +#define SEQ_DLE0 200 +#define SEQ_DLE1 250 +#define SEQ_CID 300 +#define SEQ_NOCID 350 +#define SEQ_HUP 400 +#define SEQ_DIAL 600 +#define SEQ_ACCEPT 720 +#define SEQ_SHUTDOWN 500 +#define SEQ_CIDMODE 10 +#define SEQ_UMMODE 11 // 100: init, 200: dle0, 250:dle1, 300: get cid (dial), 350: "hup" (no cid), 400: hup, 500: reset, 600: dial, 700: ring @@ -175,7 +171,7 @@ struct reply_t gigaset_tab_nocid_m10x[]= /* with dle mode */ // {ACT_TIMEOUT}}, {RSP_INIT, -1, -1,SEQ_INIT, 100, INIT_TIMEOUT, - {ACT_TIMEOUT}}, /* wait until device is ready */ + {ACT_TIMEOUT}}, /* wait until device is ready */ {EV_TIMEOUT, 100,100, -1, 101, 3, {0}, "Z\r"}, /* device in transparent mode? try to initialize it. */ {RSP_OK, 101,103, -1, 120, 5, {ACT_GETSTRING}, "+GMR\r"}, /* get version */ @@ -190,8 +186,8 @@ struct reply_t gigaset_tab_nocid_m10x[]= /* with dle mode */ {RSP_ERROR, 108,108, -1, 0, 0, {ACT_FAILINIT}}, {EV_TIMEOUT, 108,108, -1, 105, 2, {ACT_SETDLE0, - ACT_HUPMODEM, - ACT_TIMEOUT}}, /* still timeout => connection in unimodem mode? */ + ACT_HUPMODEM, + ACT_TIMEOUT}}, /* still timeout => connection in unimodem mode? */ {EV_TIMEOUT, 105,105, -1, 103, 5, {0}, "Z\r"}, {RSP_ERROR, 102,102, -1, 107, 5, {0}, "^GETPRE\r"}, /* ERROR on ATZ => maybe in config mode? */ @@ -393,7 +389,7 @@ struct reply_t gigaset_tab_cid_m10x[] = /* for M10x */ #if 0 -static struct reply_t tab_nocid[]= /* no dle mode */ //FIXME aenderungen uebernehmen +static struct reply_t tab_nocid[]= /* no dle mode */ //FIXME { /* resp_code, min_ConState, max_ConState, parameter, new_ConState, timeout, action, command */ @@ -401,7 +397,7 @@ static struct reply_t tab_nocid[]= /* no dle mode */ //FIXME aenderungen ueberne {RSP_LAST,0,0,0,0,0,0} }; -static struct reply_t tab_cid[] = /* no dle mode */ //FIXME aenderungen uebernehmen +static struct reply_t tab_cid[] = /* no dle mode */ //FIXME { /* resp_code, min_ConState, max_ConState, parameter, new_ConState, timeout, action, command */ @@ -412,30 +408,30 @@ static struct reply_t tab_cid[] = /* no dle mode */ //FIXME aenderungen ueberneh static struct resp_type_t resp_type[]= { - /*{"", RSP_EMPTY, RT_NOTHING},*/ - {"OK", RSP_OK, RT_NOTHING}, - {"ERROR", RSP_ERROR, RT_NOTHING}, - {"ZSAU", RSP_ZSAU, RT_ZSAU}, - {"ZCAU", RSP_ZCAU, RT_ZCAU}, - {"RING", RSP_RING, RT_RING}, - {"ZGCI", RSP_ZGCI, RT_NUMBER}, - {"ZVLS", RSP_ZVLS, RT_NUMBER}, - {"ZCTP", RSP_ZCTP, RT_NUMBER}, - {"ZDLE", RSP_ZDLE, RT_NUMBER}, - {"ZCFGT", RSP_ZCFGT, RT_NUMBER}, - {"ZCCR", RSP_ZCCR, RT_NUMBER}, - {"ZMWI", RSP_ZMWI, RT_NUMBER}, - {"ZHLC", RSP_ZHLC, RT_STRING}, - {"ZBC", RSP_ZBC, RT_STRING}, - {"NMBR", RSP_NMBR, RT_STRING}, - {"ZCPN", RSP_ZCPN, RT_STRING}, - {"ZCON", RSP_ZCON, RT_STRING}, - {"ZAOC", RSP_ZAOC, RT_STRING}, - {"ZCSTR", RSP_ZCSTR, RT_STRING}, - {"ZCFG", RSP_ZCFG, RT_HEX}, - {"ZLOG", RSP_ZLOG, RT_NOTHING}, - {"ZABINFO", RSP_ZABINFO, RT_NOTHING}, - {"ZSMLSTCHG", RSP_ZSMLSTCHG, RT_NOTHING}, + /*{"", RSP_EMPTY, RT_NOTHING},*/ + {"OK", RSP_OK, RT_NOTHING}, + {"ERROR", RSP_ERROR, RT_NOTHING}, + {"ZSAU", RSP_ZSAU, RT_ZSAU}, + {"ZCAU", RSP_ZCAU, RT_ZCAU}, + {"RING", RSP_RING, RT_RING}, + {"ZGCI", RSP_ZGCI, RT_NUMBER}, + {"ZVLS", RSP_ZVLS, RT_NUMBER}, + {"ZCTP", RSP_ZCTP, RT_NUMBER}, + {"ZDLE", RSP_ZDLE, RT_NUMBER}, + {"ZCFGT", RSP_ZCFGT, RT_NUMBER}, + {"ZCCR", RSP_ZCCR, RT_NUMBER}, + {"ZMWI", RSP_ZMWI, RT_NUMBER}, + {"ZHLC", RSP_ZHLC, RT_STRING}, + {"ZBC", RSP_ZBC, RT_STRING}, + {"NMBR", RSP_NMBR, RT_STRING}, + {"ZCPN", RSP_ZCPN, RT_STRING}, + {"ZCON", RSP_ZCON, RT_STRING}, + {"ZAOC", RSP_ZAOC, RT_STRING}, + {"ZCSTR", RSP_ZCSTR, RT_STRING}, + {"ZCFG", RSP_ZCFG, RT_HEX}, + {"ZLOG", RSP_ZLOG, RT_NOTHING}, + {"ZABINFO", RSP_ZABINFO, RT_NOTHING}, + {"ZSMLSTCHG", RSP_ZSMLSTCHG, RT_NOTHING}, {NULL,0,0} }; @@ -446,9 +442,7 @@ static int isdn_getnum(char *p) { int v = -1; - IFNULLRETVAL(p, -1); - - dbg(DEBUG_TRANSCMD, "string: %s", p); + gig_dbg(DEBUG_TRANSCMD, "string: %s", p); while (*p >= '0' && *p <= '9') v = ((v < 0) ? 0 : (v * 10)) + (int) ((*p++) - '0'); @@ -465,9 +459,7 @@ static int isdn_gethex(char *p) int v = 0; int c; - IFNULLRETVAL(p, -1); - - dbg(DEBUG_TRANSCMD, "string: %s", p); + gig_dbg(DEBUG_TRANSCMD, "string: %s", p); if (!*p) return -1; @@ -490,14 +482,6 @@ static int isdn_gethex(char *p) return v; } -static inline void new_index(atomic_t *index, int max) -{ - if (atomic_read(index) == max) //FIXME race? - atomic_set(index, 0); - else - atomic_inc(index); -} - /* retrieve CID from parsed response * returns 0 if no CID, -1 if invalid CID, or CID value 1..65535 */ @@ -536,16 +520,14 @@ void gigaset_handle_modem_response(struct cardstate *cs) int cid; int rawstring; - IFNULLRET(cs); - len = cs->cbytes; if (!len) { /* ignore additional LFs/CRs (M10x config mode or cx100) */ - dbg(DEBUG_MCMD, "skipped EOL [%02X]", cs->respdata[len]); + gig_dbg(DEBUG_MCMD, "skipped EOL [%02X]", cs->respdata[len]); return; } cs->respdata[len] = 0; - dbg(DEBUG_TRANSCMD, "raw string: '%s'", cs->respdata); + gig_dbg(DEBUG_TRANSCMD, "raw string: '%s'", cs->respdata); argv[0] = cs->respdata; params = 1; if (cs->at_state.getstring) { @@ -561,7 +543,8 @@ void gigaset_handle_modem_response(struct cardstate *cs) case ',': case '=': if (params > MAX_REC_PARAMS) { - warn("too many parameters in response"); + dev_warn(cs->dev, + "too many parameters in response\n"); /* need last parameter (might be CID) */ params--; } @@ -572,33 +555,33 @@ void gigaset_handle_modem_response(struct cardstate *cs) cid = params > 1 ? cid_of_response(argv[params-1]) : 0; if (cid < 0) { gigaset_add_event(cs, &cs->at_state, RSP_INVAL, - NULL, 0, NULL); + NULL, 0, NULL); return; } for (j = 1; j < params; ++j) argv[j][-1] = 0; - dbg(DEBUG_TRANSCMD, "CMD received: %s", argv[0]); + gig_dbg(DEBUG_TRANSCMD, "CMD received: %s", argv[0]); if (cid) { --params; - dbg(DEBUG_TRANSCMD, "CID: %s", argv[params]); + gig_dbg(DEBUG_TRANSCMD, "CID: %s", argv[params]); } - dbg(DEBUG_TRANSCMD, "available params: %d", params - 1); + gig_dbg(DEBUG_TRANSCMD, "available params: %d", params - 1); for (j = 1; j < params; j++) - dbg(DEBUG_TRANSCMD, "param %d: %s", j, argv[j]); + gig_dbg(DEBUG_TRANSCMD, "param %d: %s", j, argv[j]); } spin_lock_irqsave(&cs->ev_lock, flags); - head = atomic_read(&cs->ev_head); - tail = atomic_read(&cs->ev_tail); + head = cs->ev_head; + tail = cs->ev_tail; abort = 1; curarg = 0; while (curarg < params) { next = (tail + 1) % MAX_EVENTS; if (unlikely(next == head)) { - err("event queue full"); + dev_err(cs->dev, "event queue full\n"); break; } @@ -619,8 +602,9 @@ void gigaset_handle_modem_response(struct cardstate *cs) if (!rt->response) { event->type = RSP_UNKNOWN; - warn("unknown modem response: %s", - argv[curarg]); + dev_warn(cs->dev, + "unknown modem response: %s\n", + argv[curarg]); break; } @@ -636,7 +620,8 @@ void gigaset_handle_modem_response(struct cardstate *cs) break; case RT_RING: if (!cid) { - err("received RING without CID!"); + dev_err(cs->dev, + "received RING without CID!\n"); event->type = RSP_INVAL; abort = 1; } else { @@ -664,27 +649,25 @@ void gigaset_handle_modem_response(struct cardstate *cs) event->parameter = ZSAU_DISCONNECT_REQ; else { event->parameter = ZSAU_UNKNOWN; - warn("%s: unknown parameter %s after ZSAU", - __func__, argv[curarg]); + dev_warn(cs->dev, + "%s: unknown parameter %s after ZSAU\n", + __func__, argv[curarg]); } ++curarg; break; case RT_STRING: if (curarg < params) { - len = strlen(argv[curarg]) + 1; - event->ptr = kmalloc(len, GFP_ATOMIC); - if (event->ptr) - memcpy(event->ptr, argv[curarg], len); - else - err("no memory for string!"); + event->ptr = kstrdup(argv[curarg], GFP_ATOMIC); + if (!event->ptr) + dev_err(cs->dev, "out of memory\n"); ++curarg; } #ifdef CONFIG_GIGASET_DEBUG if (!event->ptr) - dbg(DEBUG_CMD, "string==NULL"); + gig_dbg(DEBUG_CMD, "string==NULL"); else - dbg(DEBUG_CMD, - "string==%s", (char *) event->ptr); + gig_dbg(DEBUG_CMD, "string==%s", + (char *) event->ptr); #endif break; case RT_ZCAU: @@ -694,7 +677,7 @@ void gigaset_handle_modem_response(struct cardstate *cs) j = isdn_gethex(argv[curarg + 1]); if (i >= 0 && i < 256 && j >= 0 && j < 256) event->parameter = (unsigned) i << 8 - | j; + | j; curarg += 2; } else curarg = params - 1; @@ -712,7 +695,7 @@ void gigaset_handle_modem_response(struct cardstate *cs) } else event->parameter = -1; #ifdef CONFIG_GIGASET_DEBUG - dbg(DEBUG_CMD, "parameter==%d", event->parameter); + gig_dbg(DEBUG_CMD, "parameter==%d", event->parameter); #endif break; } @@ -724,12 +707,13 @@ void gigaset_handle_modem_response(struct cardstate *cs) break; } - atomic_set(&cs->ev_tail, tail); + cs->ev_tail = tail; spin_unlock_irqrestore(&cs->ev_lock, flags); if (curarg != params) - dbg(DEBUG_ANY, "invalid number of processed parameters: %d/%d", - curarg, params); + gig_dbg(DEBUG_ANY, + "invalid number of processed parameters: %d/%d", + curarg, params); } EXPORT_SYMBOL_GPL(gigaset_handle_modem_response); @@ -739,23 +723,19 @@ EXPORT_SYMBOL_GPL(gigaset_handle_modem_response); static void disconnect(struct at_state_t **at_state_p) { unsigned long flags; - struct bc_state *bcs; - struct cardstate *cs; + struct bc_state *bcs = (*at_state_p)->bcs; + struct cardstate *cs = (*at_state_p)->cs; - IFNULLRET(at_state_p); - IFNULLRET(*at_state_p); - bcs = (*at_state_p)->bcs; - cs = (*at_state_p)->cs; - IFNULLRET(cs); - - new_index(&(*at_state_p)->seq_index, MAX_SEQ_INDEX); + spin_lock_irqsave(&cs->lock, flags); + ++(*at_state_p)->seq_index; /* revert to selected idle mode */ - if (!atomic_read(&cs->cidmode)) { + if (!cs->cidmode) { cs->at_state.pending_commands |= PC_UMMODE; atomic_set(&cs->commands_pending, 1); //FIXME - dbg(DEBUG_CMD, "Scheduling PC_UMMODE"); + gig_dbg(DEBUG_CMD, "Scheduling PC_UMMODE"); } + spin_unlock_irqrestore(&cs->lock, flags); if (bcs) { /* B channel assigned: invoke hardware specific handler */ @@ -777,7 +757,7 @@ static void disconnect(struct at_state_t **at_state_p) * The structure should be freed by calling disconnect() after use. */ static inline struct at_state_t *get_free_channel(struct cardstate *cs, - int cid) + int cid) /* cids: >0: siemens-cid 0: without cid -1: no cid assigned yet @@ -826,7 +806,7 @@ static void init_failed(struct cardstate *cs, int mode) static void schedule_init(struct cardstate *cs, int state) { if (cs->at_state.pending_commands & PC_INIT) { - dbg(DEBUG_CMD, "not scheduling PC_INIT again"); + gig_dbg(DEBUG_CMD, "not scheduling PC_INIT again"); return; } atomic_set(&cs->mstate, state); @@ -834,52 +814,56 @@ static void schedule_init(struct cardstate *cs, int state) gigaset_block_channels(cs); cs->at_state.pending_commands |= PC_INIT; atomic_set(&cs->commands_pending, 1); - dbg(DEBUG_CMD, "Scheduling PC_INIT"); + gig_dbg(DEBUG_CMD, "Scheduling PC_INIT"); } -/* Add "AT" to a command, add the cid, dle encode it, send the result to the hardware. */ +/* Add "AT" to a command, add the cid, dle encode it, send the result to the + hardware. */ static void send_command(struct cardstate *cs, const char *cmd, int cid, - int dle, gfp_t kmallocflags) + int dle, gfp_t kmallocflags) { size_t cmdlen, buflen; char *cmdpos, *cmdbuf, *cmdtail; cmdlen = strlen(cmd); buflen = 11 + cmdlen; + if (unlikely(buflen <= cmdlen)) { + dev_err(cs->dev, "integer overflow in buflen\n"); + return; + } - if (likely(buflen > cmdlen)) { - cmdbuf = kmalloc(buflen, kmallocflags); - if (likely(cmdbuf != NULL)) { - cmdpos = cmdbuf + 9; - cmdtail = cmdpos + cmdlen; - memcpy(cmdpos, cmd, cmdlen); + cmdbuf = kmalloc(buflen, kmallocflags); + if (unlikely(!cmdbuf)) { + dev_err(cs->dev, "out of memory\n"); + return; + } - if (cid > 0 && cid <= 65535) { - do { - *--cmdpos = '0' + cid % 10; - cid /= 10; - ++cmdlen; - } while (cid); - } + cmdpos = cmdbuf + 9; + cmdtail = cmdpos + cmdlen; + memcpy(cmdpos, cmd, cmdlen); - cmdlen += 2; - *--cmdpos = 'T'; - *--cmdpos = 'A'; + if (cid > 0 && cid <= 65535) { + do { + *--cmdpos = '0' + cid % 10; + cid /= 10; + ++cmdlen; + } while (cid); + } - if (dle) { - cmdlen += 4; - *--cmdpos = '('; - *--cmdpos = 0x10; - *cmdtail++ = 0x10; - *cmdtail++ = ')'; - } + cmdlen += 2; + *--cmdpos = 'T'; + *--cmdpos = 'A'; - cs->ops->write_cmd(cs, cmdpos, cmdlen, NULL); - kfree(cmdbuf); - } else - err("no memory for command buffer"); - } else - err("overflow in buflen"); + if (dle) { + cmdlen += 4; + *--cmdpos = '('; + *--cmdpos = 0x10; + *cmdtail++ = 0x10; + *cmdtail++ = ')'; + } + + cs->ops->write_cmd(cs, cmdpos, cmdlen, NULL); + kfree(cmdbuf); } static struct at_state_t *at_state_from_cid(struct cardstate *cs, int cid) @@ -910,9 +894,6 @@ static struct at_state_t *at_state_from_cid(struct cardstate *cs, int cid) static void bchannel_down(struct bc_state *bcs) { - IFNULLRET(bcs); - IFNULLRET(bcs->cs); - if (bcs->chstate & CHS_B_UP) { bcs->chstate &= ~CHS_B_UP; gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BHUP); @@ -930,16 +911,15 @@ static void bchannel_down(struct bc_state *bcs) static void bchannel_up(struct bc_state *bcs) { - IFNULLRET(bcs); - if (!(bcs->chstate & CHS_D_UP)) { - notice("%s: D channel not up", __func__); + dev_notice(bcs->cs->dev, "%s: D channel not up\n", __func__); bcs->chstate |= CHS_D_UP; gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DCONN); } if (bcs->chstate & CHS_B_UP) { - notice("%s: B channel already up", __func__); + dev_notice(bcs->cs->dev, "%s: B channel already up\n", + __func__); return; } @@ -947,17 +927,21 @@ static void bchannel_up(struct bc_state *bcs) gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BCONN); } -static void start_dial(struct at_state_t *at_state, void *data, int seq_index) +static void start_dial(struct at_state_t *at_state, void *data, unsigned seq_index) { struct bc_state *bcs = at_state->bcs; struct cardstate *cs = at_state->cs; int retval; + unsigned long flags; bcs->chstate |= CHS_NOTIFY_LL; - //atomic_set(&bcs->status, BCS_INIT); - if (atomic_read(&at_state->seq_index) != seq_index) + spin_lock_irqsave(&cs->lock, flags); + if (at_state->seq_index != seq_index) { + spin_unlock_irqrestore(&cs->lock, flags); goto error; + } + spin_unlock_irqrestore(&cs->lock, flags); retval = gigaset_isdn_setup_dial(at_state, data); if (retval != 0) @@ -965,20 +949,14 @@ static void start_dial(struct at_state_t *at_state, void *data, int seq_index) at_state->pending_commands |= PC_CID; - dbg(DEBUG_CMD, "Scheduling PC_CID"); -//#ifdef GIG_MAYINITONDIAL -// if (atomic_read(&cs->MState) == MS_UNKNOWN) { -// cs->at_state.pending_commands |= PC_INIT; -// dbg(DEBUG_CMD, "Scheduling PC_INIT"); -// } -//#endif - atomic_set(&cs->commands_pending, 1); //FIXME + gig_dbg(DEBUG_CMD, "Scheduling PC_CID"); + atomic_set(&cs->commands_pending, 1); return; error: at_state->pending_commands |= PC_NOCID; - dbg(DEBUG_CMD, "Scheduling PC_NOCID"); - atomic_set(&cs->commands_pending, 1); //FIXME + gig_dbg(DEBUG_CMD, "Scheduling PC_NOCID"); + atomic_set(&cs->commands_pending, 1); return; } @@ -991,13 +969,13 @@ static void start_accept(struct at_state_t *at_state) if (retval == 0) { at_state->pending_commands |= PC_ACCEPT; - dbg(DEBUG_CMD, "Scheduling PC_ACCEPT"); - atomic_set(&cs->commands_pending, 1); //FIXME + gig_dbg(DEBUG_CMD, "Scheduling PC_ACCEPT"); + atomic_set(&cs->commands_pending, 1); } else { //FIXME at_state->pending_commands |= PC_HUP; - dbg(DEBUG_CMD, "Scheduling PC_HUP"); - atomic_set(&cs->commands_pending, 1); //FIXME + gig_dbg(DEBUG_CMD, "Scheduling PC_HUP"); + atomic_set(&cs->commands_pending, 1); } } @@ -1008,9 +986,10 @@ static void do_start(struct cardstate *cs) if (atomic_read(&cs->mstate) != MS_LOCKED) schedule_init(cs, MS_INIT); + cs->isdn_up = 1; gigaset_i4l_cmd(cs, ISDN_STAT_RUN); - // FIXME: not in locked mode - // FIXME 2: only after init sequence + // FIXME: not in locked mode + // FIXME 2: only after init sequence cs->waiting = 0; wake_up(&cs->waitqueue); @@ -1023,6 +1002,12 @@ static void finish_shutdown(struct cardstate *cs) atomic_set(&cs->mode, M_UNKNOWN); } + /* Tell the LL that the device is not available .. */ + if (cs->isdn_up) { + cs->isdn_up = 0; + gigaset_i4l_cmd(cs, ISDN_STAT_STOP); + } + /* The rest is done by cleanup_cs () in user mode. */ cs->cmd_result = -ENODEV; @@ -1037,15 +1022,20 @@ static void do_shutdown(struct cardstate *cs) if (atomic_read(&cs->mstate) == MS_READY) { atomic_set(&cs->mstate, MS_SHUTDOWN); cs->at_state.pending_commands |= PC_SHUTDOWN; - atomic_set(&cs->commands_pending, 1); //FIXME - dbg(DEBUG_CMD, "Scheduling PC_SHUTDOWN"); //FIXME - //gigaset_schedule_event(cs); //FIXME + atomic_set(&cs->commands_pending, 1); + gig_dbg(DEBUG_CMD, "Scheduling PC_SHUTDOWN"); } else finish_shutdown(cs); } static void do_stop(struct cardstate *cs) { + unsigned long flags; + + spin_lock_irqsave(&cs->lock, flags); + cs->connected = 0; + spin_unlock_irqrestore(&cs->lock, flags); + do_shutdown(cs); } @@ -1069,9 +1059,11 @@ static int reinit_and_retry(struct cardstate *cs, int channel) return 0; if (channel < 0) - warn("Could not enter cid mode. Reinit device and try again."); + dev_warn(cs->dev, + "Could not enter cid mode. Reinit device and try again.\n"); else { - warn("Could not get a call id. Reinit device and try again."); + dev_warn(cs->dev, + "Could not get a call id. Reinit device and try again.\n"); cs->bcs[channel].at_state.pending_commands |= PC_CID; } schedule_init(cs, MS_INIT); @@ -1079,7 +1071,7 @@ static int reinit_and_retry(struct cardstate *cs, int channel) } static int at_state_invalid(struct cardstate *cs, - struct at_state_t *test_ptr) + struct at_state_t *test_ptr) { unsigned long flags; unsigned channel; @@ -1116,7 +1108,7 @@ static void handle_icall(struct cardstate *cs, struct bc_state *bcs, case ICALL_ACCEPT: break; default: - err("internal error: disposition=%d", retval); + dev_err(cs->dev, "internal error: disposition=%d\n", retval); /* --v-- fall through --v-- */ case ICALL_IGNORE: case ICALL_REJECT: @@ -1160,7 +1152,6 @@ static int do_lock(struct cardstate *cs) mode = atomic_read(&cs->mode); atomic_set(&cs->mstate, MS_LOCKED); atomic_set(&cs->mode, M_UNKNOWN); - //FIXME reset card state / at states / bcs states return mode; } @@ -1173,8 +1164,7 @@ static int do_unlock(struct cardstate *cs) atomic_set(&cs->mstate, MS_UNINITIALIZED); atomic_set(&cs->mode, M_UNKNOWN); gigaset_free_channels(cs); - //FIXME reset card state / at states / bcs states - if (atomic_read(&cs->connected)) + if (cs->connected) schedule_init(cs, MS_INIT); return 0; @@ -1203,21 +1193,23 @@ static void do_action(int action, struct cardstate *cs, at_state->waiting = 1; break; case ACT_INIT: - //FIXME setup everything cs->at_state.pending_commands &= ~PC_INIT; cs->cur_at_seq = SEQ_NONE; atomic_set(&cs->mode, M_UNIMODEM); - if (!atomic_read(&cs->cidmode)) { + spin_lock_irqsave(&cs->lock, flags); + if (!cs->cidmode) { + spin_unlock_irqrestore(&cs->lock, flags); gigaset_free_channels(cs); atomic_set(&cs->mstate, MS_READY); break; } + spin_unlock_irqrestore(&cs->lock, flags); cs->at_state.pending_commands |= PC_CIDMODE; - atomic_set(&cs->commands_pending, 1); //FIXME - dbg(DEBUG_CMD, "Scheduling PC_CIDMODE"); + atomic_set(&cs->commands_pending, 1); + gig_dbg(DEBUG_CMD, "Scheduling PC_CIDMODE"); break; case ACT_FAILINIT: - warn("Could not initialize the device."); + dev_warn(cs->dev, "Could not initialize the device.\n"); cs->dle = 0; init_failed(cs, M_UNKNOWN); cs->cur_at_seq = SEQ_NONE; @@ -1273,8 +1265,8 @@ static void do_action(int action, struct cardstate *cs, /* get fresh AT state structure for new CID */ at_state2 = get_free_channel(cs, ev->parameter); if (!at_state2) { - warn("RING ignored: " - "could not allocate channel structure"); + dev_warn(cs->dev, + "RING ignored: could not allocate channel structure\n"); break; } @@ -1302,7 +1294,7 @@ static void do_action(int action, struct cardstate *cs, at_state = *p_at_state; break; case ACT_FAILSDOWN: - warn("Could not shut down the device."); + dev_warn(cs->dev, "Could not shut down the device.\n"); /* fall through */ case ACT_FAKESDOWN: case ACT_SDOWN: @@ -1355,7 +1347,7 @@ static void do_action(int action, struct cardstate *cs, break; case ACT_ABORTHUP: cs->cur_at_seq = SEQ_NONE; - warn("Could not hang up."); + dev_warn(cs->dev, "Could not hang up.\n"); at_state->cid = -1; if (bcs && cs->onechannel) at_state->pending_commands |= PC_DLE0; @@ -1367,14 +1359,15 @@ static void do_action(int action, struct cardstate *cs, break; case ACT_FAILDLE0: cs->cur_at_seq = SEQ_NONE; - warn("Could not leave DLE mode."); + dev_warn(cs->dev, "Could not leave DLE mode.\n"); at_state2 = &cs->bcs[cs->curchannel].at_state; disconnect(&at_state2); schedule_init(cs, MS_RECOVER); break; case ACT_FAILDLE1: cs->cur_at_seq = SEQ_NONE; - warn("Could not enter DLE mode. Try to hang up."); + dev_warn(cs->dev, + "Could not enter DLE mode. Trying to hang up.\n"); channel = cs->curchannel; cs->bcs[channel].at_state.pending_commands |= PC_HUP; atomic_set(&cs->commands_pending, 1); @@ -1395,7 +1388,8 @@ static void do_action(int action, struct cardstate *cs, cs->cur_at_seq = SEQ_NONE; channel = cs->curchannel; if (!reinit_and_retry(cs, channel)) { - warn("Could not get a call id. Dialing not possible"); + dev_warn(cs->dev, + "Could not get a call ID. Cannot dial.\n"); at_state2 = &cs->bcs[channel].at_state; disconnect(&at_state2); } @@ -1428,7 +1422,8 @@ static void do_action(int action, struct cardstate *cs, at_state->pending_commands |= PC_HUP; atomic_set(&cs->commands_pending, 1); break; - case ACT_GETSTRING: /* warning: RING, ZDLE, ... are not handled properly any more */ + case ACT_GETSTRING: /* warning: RING, ZDLE, ... + are not handled properly anymore */ at_state->getstring = 1; break; case ACT_SETVER: @@ -1469,16 +1464,16 @@ static void do_action(int action, struct cardstate *cs, case ACT_GOTVER: if (cs->gotfwver == 0) { cs->gotfwver = 1; - dbg(DEBUG_ANY, - "firmware version %02d.%03d.%02d.%02d", - cs->fwver[0], cs->fwver[1], - cs->fwver[2], cs->fwver[3]); + gig_dbg(DEBUG_ANY, + "firmware version %02d.%03d.%02d.%02d", + cs->fwver[0], cs->fwver[1], + cs->fwver[2], cs->fwver[3]); break; } /* fall through */ case ACT_FAILVER: cs->gotfwver = -1; - err("could not read firmware version."); + dev_err(cs->dev, "could not read firmware version.\n"); break; #ifdef CONFIG_GIGASET_DEBUG case ACT_ERROR: @@ -1496,16 +1491,16 @@ static void do_action(int action, struct cardstate *cs, break; #endif case ACT_DEBUG: - dbg(DEBUG_ANY, "%s: resp_code %d in ConState %d", + gig_dbg(DEBUG_ANY, "%s: resp_code %d in ConState %d", __func__, ev->type, at_state->ConState); break; case ACT_WARN: - warn("%s: resp_code %d in ConState %d!", - __func__, ev->type, at_state->ConState); + dev_warn(cs->dev, "%s: resp_code %d in ConState %d!\n", + __func__, ev->type, at_state->ConState); break; case ACT_ZCAU: - warn("cause code %04x in connection state %d.", - ev->parameter, at_state->ConState); + dev_warn(cs->dev, "cause code %04x in connection state %d.\n", + ev->parameter, at_state->ConState); break; /* events from the LL */ @@ -1516,14 +1511,14 @@ static void do_action(int action, struct cardstate *cs, start_accept(at_state); break; case ACT_PROTO_L2: - dbg(DEBUG_CMD, - "set protocol to %u", (unsigned) ev->parameter); + gig_dbg(DEBUG_CMD, "set protocol to %u", + (unsigned) ev->parameter); at_state->bcs->proto2 = ev->parameter; break; case ACT_HUP: at_state->pending_commands |= PC_HUP; - atomic_set(&cs->commands_pending, 1); //FIXME - dbg(DEBUG_CMD, "Scheduling PC_HUP"); + atomic_set(&cs->commands_pending, 1); + gig_dbg(DEBUG_CMD, "Scheduling PC_HUP"); break; /* hotplug events */ @@ -1555,17 +1550,19 @@ static void do_action(int action, struct cardstate *cs, /* events from the proc file system */ // FIXME without ACT_xxxx? case ACT_PROC_CIDMODE: - if (ev->parameter != atomic_read(&cs->cidmode)) { - atomic_set(&cs->cidmode, ev->parameter); + spin_lock_irqsave(&cs->lock, flags); + if (ev->parameter != cs->cidmode) { + cs->cidmode = ev->parameter; if (ev->parameter) { cs->at_state.pending_commands |= PC_CIDMODE; - dbg(DEBUG_CMD, "Scheduling PC_CIDMODE"); + gig_dbg(DEBUG_CMD, "Scheduling PC_CIDMODE"); } else { cs->at_state.pending_commands |= PC_UMMODE; - dbg(DEBUG_CMD, "Scheduling PC_UMMODE"); + gig_dbg(DEBUG_CMD, "Scheduling PC_UMMODE"); } atomic_set(&cs->commands_pending, 1); } + spin_unlock_irqrestore(&cs->lock, flags); cs->waiting = 0; wake_up(&cs->waitqueue); break; @@ -1590,7 +1587,7 @@ static void do_action(int action, struct cardstate *cs, *p_resp_code = RSP_NULL; } } else - err("%s: action==%d!", __func__, action); + dev_err(cs->dev, "%s: action==%d!\n", __func__, action); } } @@ -1609,47 +1606,46 @@ static void process_event(struct cardstate *cs, struct event_t *ev) int curact; unsigned long flags; - IFNULLRET(cs); - IFNULLRET(ev); - if (ev->cid >= 0) { at_state = at_state_from_cid(cs, ev->cid); if (!at_state) { gigaset_add_event(cs, &cs->at_state, RSP_WRONG_CID, - NULL, 0, NULL); + NULL, 0, NULL); return; } } else { at_state = ev->at_state; if (at_state_invalid(cs, at_state)) { - dbg(DEBUG_ANY, - "event for invalid at_state %p", at_state); + gig_dbg(DEBUG_ANY, "event for invalid at_state %p", + at_state); return; } } - dbg(DEBUG_CMD, - "connection state %d, event %d", at_state->ConState, ev->type); + gig_dbg(DEBUG_CMD, "connection state %d, event %d", + at_state->ConState, ev->type); bcs = at_state->bcs; sendcid = at_state->cid; /* Setting the pointer to the dial array */ rep = at_state->replystruct; - IFNULLRET(rep); + spin_lock_irqsave(&cs->lock, flags); if (ev->type == EV_TIMEOUT) { - if (ev->parameter != atomic_read(&at_state->timer_index) + if (ev->parameter != at_state->timer_index || !at_state->timer_active) { ev->type = RSP_NONE; /* old timeout */ - dbg(DEBUG_ANY, "old timeout"); + gig_dbg(DEBUG_ANY, "old timeout"); } else if (!at_state->waiting) - dbg(DEBUG_ANY, "timeout occured"); + gig_dbg(DEBUG_ANY, "timeout occurred"); else - dbg(DEBUG_ANY, "stopped waiting"); + gig_dbg(DEBUG_ANY, "stopped waiting"); } + spin_unlock_irqrestore(&cs->lock, flags); - /* if the response belongs to a variable in at_state->int_var[VAR_XXXX] or at_state->str_var[STR_XXXX], set it */ + /* if the response belongs to a variable in at_state->int_var[VAR_XXXX] + or at_state->str_var[STR_XXXX], set it */ if (ev->type >= RSP_VAR && ev->type < RSP_VAR + VAR_NUM) { index = ev->type - RSP_VAR; at_state->int_var[index] = ev->parameter; @@ -1657,20 +1653,22 @@ static void process_event(struct cardstate *cs, struct event_t *ev) index = ev->type - RSP_STR; kfree(at_state->str_var[index]); at_state->str_var[index] = ev->ptr; - ev->ptr = NULL; /* prevent process_events() from deallocating ptr */ + ev->ptr = NULL; /* prevent process_events() from + deallocating ptr */ } if (ev->type == EV_TIMEOUT || ev->type == RSP_STRING) at_state->getstring = 0; - /* Search row in dial array which matches modem response and current constate */ + /* Search row in dial array which matches modem response and current + constate */ for (;; rep++) { rcode = rep->resp_code; - /* dbg (DEBUG_ANY, "rcode %d", rcode); */ if (rcode == RSP_LAST) { /* found nothing...*/ - warn("%s: rcode=RSP_LAST: resp_code %d in ConState %d!", - __func__, ev->type, at_state->ConState); + dev_warn(cs->dev, "%s: rcode=RSP_LAST: " + "resp_code %d in ConState %d!\n", + __func__, ev->type, at_state->ConState); return; } if ((rcode == RSP_ANY || rcode == ev->type) @@ -1706,14 +1704,14 @@ static void process_event(struct cardstate *cs, struct event_t *ev) } else { /* Send command to modem if not NULL... */ if (p_command/*rep->command*/) { - if (atomic_read(&cs->connected)) + if (cs->connected) send_command(cs, p_command, - sendcid, cs->dle, - GFP_ATOMIC); + sendcid, cs->dle, + GFP_ATOMIC); else gigaset_add_event(cs, at_state, - RSP_NODEV, - NULL, 0, NULL); + RSP_NODEV, + NULL, 0, NULL); } spin_lock_irqsave(&cs->lock, flags); @@ -1723,8 +1721,7 @@ static void process_event(struct cardstate *cs, struct event_t *ev) } else if (rep->timeout > 0) { /* new timeout */ at_state->timer_expires = rep->timeout * 10; at_state->timer_active = 1; - new_index(&at_state->timer_index, - MAX_TIMER_INDEX); + ++at_state->timer_index; } spin_unlock_irqrestore(&cs->lock, flags); } @@ -1744,17 +1741,16 @@ static void process_command_flags(struct cardstate *cs) struct bc_state *bcs; int i; int sequence; - - IFNULLRET(cs); + unsigned long flags; atomic_set(&cs->commands_pending, 0); if (cs->cur_at_seq) { - dbg(DEBUG_CMD, "not searching scheduled commands: busy"); + gig_dbg(DEBUG_CMD, "not searching scheduled commands: busy"); return; } - dbg(DEBUG_CMD, "searching scheduled commands"); + gig_dbg(DEBUG_CMD, "searching scheduled commands"); sequence = SEQ_NONE; @@ -1795,8 +1791,9 @@ static void process_command_flags(struct cardstate *cs) } /* only switch back to unimodem mode, if no commands are pending and no channels are up */ + spin_lock_irqsave(&cs->lock, flags); if (cs->at_state.pending_commands == PC_UMMODE - && !atomic_read(&cs->cidmode) + && !cs->cidmode && list_empty(&cs->temp_at_states) && atomic_read(&cs->mode) == M_CID) { sequence = SEQ_UMMODE; @@ -1810,6 +1807,7 @@ static void process_command_flags(struct cardstate *cs) } } } + spin_unlock_irqrestore(&cs->lock, flags); cs->at_state.pending_commands &= ~PC_UMMODE; if (sequence != SEQ_NONE) { schedule_sequence(cs, at_state, sequence); @@ -1865,11 +1863,7 @@ static void process_command_flags(struct cardstate *cs) if (cs->at_state.pending_commands & PC_CIDMODE) { cs->at_state.pending_commands &= ~PC_CIDMODE; if (atomic_read(&cs->mode) == M_UNIMODEM) { -#if 0 - cs->retry_count = 2; -#else cs->retry_count = 1; -#endif schedule_sequence(cs, &cs->at_state, SEQ_CIDMODE); return; } @@ -1897,7 +1891,7 @@ static void process_command_flags(struct cardstate *cs) switch (atomic_read(&cs->mode)) { case M_UNIMODEM: cs->at_state.pending_commands |= PC_CIDMODE; - dbg(DEBUG_CMD, "Scheduling PC_CIDMODE"); + gig_dbg(DEBUG_CMD, "Scheduling PC_CIDMODE"); atomic_set(&cs->commands_pending, 1); return; #ifdef GIG_MAYINITONDIAL @@ -1926,18 +1920,21 @@ static void process_events(struct cardstate *cs) int i; int check_flags = 0; int was_busy; + unsigned long flags; - /* no locking needed (only one reader) */ - head = atomic_read(&cs->ev_head); + spin_lock_irqsave(&cs->ev_lock, flags); + head = cs->ev_head; for (i = 0; i < 2 * MAX_EVENTS; ++i) { - tail = atomic_read(&cs->ev_tail); + tail = cs->ev_tail; if (tail == head) { if (!check_flags && !atomic_read(&cs->commands_pending)) break; check_flags = 0; + spin_unlock_irqrestore(&cs->ev_lock, flags); process_command_flags(cs); - tail = atomic_read(&cs->ev_tail); + spin_lock_irqsave(&cs->ev_lock, flags); + tail = cs->ev_tail; if (tail == head) { if (!atomic_read(&cs->commands_pending)) break; @@ -1947,18 +1944,23 @@ static void process_events(struct cardstate *cs) ev = cs->events + head; was_busy = cs->cur_at_seq != SEQ_NONE; + spin_unlock_irqrestore(&cs->ev_lock, flags); process_event(cs, ev); + spin_lock_irqsave(&cs->ev_lock, flags); kfree(ev->ptr); ev->ptr = NULL; if (was_busy && cs->cur_at_seq == SEQ_NONE) check_flags = 1; head = (head + 1) % MAX_EVENTS; - atomic_set(&cs->ev_head, head); + cs->ev_head = head; } + spin_unlock_irqrestore(&cs->ev_lock, flags); + if (i == 2 * MAX_EVENTS) { - err("infinite loop in process_events; aborting."); + dev_err(cs->dev, + "infinite loop in process_events; aborting.\n"); } } @@ -1970,12 +1972,9 @@ void gigaset_handle_event(unsigned long data) { struct cardstate *cs = (struct cardstate *) data; - IFNULLRET(cs); - IFNULLRET(cs->inbuf); - /* handle incoming data on control/common channel */ if (atomic_read(&cs->inbuf->head) != atomic_read(&cs->inbuf->tail)) { - dbg(DEBUG_INTR, "processing new data"); + gig_dbg(DEBUG_INTR, "processing new data"); cs->ops->handle_input(cs->inbuf); } diff --git a/drivers/isdn/gigaset/gigaset.h b/drivers/isdn/gigaset/gigaset.h index 729edcdb6dac..9d21ba8757b0 100644 --- a/drivers/isdn/gigaset/gigaset.h +++ b/drivers/isdn/gigaset/gigaset.h @@ -1,11 +1,16 @@ -/* Siemens Gigaset 307x driver +/* + * Siemens Gigaset 307x driver * Common header file for all connection variants * - * Written by Stefan Eilers + * Written by Stefan Eilers * and Hansjoerg Lipp * - * Version: $Id: gigaset.h,v 1.97.4.26 2006/02/04 18:28:16 hjlipp Exp $ - * =========================================================================== + * ===================================================================== + * 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. + * ===================================================================== */ #ifndef GIGASET_H @@ -15,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -27,21 +31,22 @@ #include #include #include +#include #define GIG_VERSION {0,5,0,0} #define GIG_COMPAT {0,4,0,0} -#define MAX_REC_PARAMS 10 /* Max. number of params in response string */ -#define MAX_RESP_SIZE 512 /* Max. size of a response string */ -#define HW_HDR_LEN 2 /* Header size used to store ack info */ +#define MAX_REC_PARAMS 10 /* Max. number of params in response string */ +#define MAX_RESP_SIZE 512 /* Max. size of a response string */ +#define HW_HDR_LEN 2 /* Header size used to store ack info */ -#define MAX_EVENTS 64 /* size of event queue */ +#define MAX_EVENTS 64 /* size of event queue */ #define RBUFSIZE 8192 -#define SBUFSIZE 4096 /* sk_buff payload size */ +#define SBUFSIZE 4096 /* sk_buff payload size */ -#define MAX_BUF_SIZE (SBUFSIZE - 2) /* Max. size of a data packet from LL */ -#define TRANSBUFSIZE 768 /* bytes per skb for transparent receive */ +#define TRANSBUFSIZE 768 /* bytes per skb for transparent receive */ +#define MAX_BUF_SIZE (SBUFSIZE - 2) /* Max. size of a data packet from LL */ /* compile time options */ #define GIG_MAJOR 0 @@ -50,10 +55,7 @@ #define GIG_RETRYCID #define GIG_X75 -#define MAX_TIMER_INDEX 1000 -#define MAX_SEQ_INDEX 1000 - -#define GIG_TICK (HZ / 10) +#define GIG_TICK 100 /* in milliseconds */ /* timeout values (unit: 1 sec) */ #define INIT_TIMEOUT 1 @@ -67,74 +69,89 @@ #define MAXACT 3 -#define IFNULL(a) if (unlikely(!(a))) -#define IFNULLRET(a) if (unlikely(!(a))) {err("%s==NULL at %s:%d!", #a, __FILE__, __LINE__); return; } -#define IFNULLRETVAL(a,b) if (unlikely(!(a))) {err("%s==NULL at %s:%d!", #a, __FILE__, __LINE__); return (b); } -#define IFNULLCONT(a) if (unlikely(!(a))) {err("%s==NULL at %s:%d!", #a, __FILE__, __LINE__); continue; } -#define IFNULLGOTO(a,b) if (unlikely(!(a))) {err("%s==NULL at %s:%d!", #a, __FILE__, __LINE__); goto b; } - extern int gigaset_debuglevel; /* "needs" cast to (enum debuglevel) */ -/* any combination of these can be given with the 'debug=' parameter to insmod, e.g. - * 'insmod usb_gigaset.o debug=0x2c' will set DEBUG_OPEN, DEBUG_CMD and DEBUG_INTR. */ +/* any combination of these can be given with the 'debug=' parameter to insmod, + * e.g. 'insmod usb_gigaset.o debug=0x2c' will set DEBUG_OPEN, DEBUG_CMD and + * DEBUG_INTR. + */ enum debuglevel { /* up to 24 bits (atomic_t) */ DEBUG_REG = 0x0002, /* serial port I/O register operations */ DEBUG_OPEN = 0x0004, /* open/close serial port */ DEBUG_INTR = 0x0008, /* interrupt processing */ - DEBUG_INTR_DUMP = 0x0010, /* Activating hexdump debug output on interrupt - requests, not available as run-time option */ + DEBUG_INTR_DUMP = 0x0010, /* Activating hexdump debug output on + interrupt requests, not available as + run-time option */ DEBUG_CMD = 0x00020, /* sent/received LL commands */ DEBUG_STREAM = 0x00040, /* application data stream I/O events */ DEBUG_STREAM_DUMP = 0x00080, /* application data stream content */ DEBUG_LLDATA = 0x00100, /* sent/received LL data */ - DEBUG_INTR_0 = 0x00200, /* serial port output interrupt processing */ + DEBUG_INTR_0 = 0x00200, /* serial port interrupt processing */ DEBUG_DRIVER = 0x00400, /* driver structure */ DEBUG_HDLC = 0x00800, /* M10x HDLC processing */ DEBUG_WRITE = 0x01000, /* M105 data write */ - DEBUG_TRANSCMD = 0x02000, /*AT-COMMANDS+RESPONSES*/ - DEBUG_MCMD = 0x04000, /*COMMANDS THAT ARE SENT VERY OFTEN*/ - DEBUG_INIT = 0x08000, /* (de)allocation+initialization of data structures */ + DEBUG_TRANSCMD = 0x02000, /* AT-COMMANDS+RESPONSES */ + DEBUG_MCMD = 0x04000, /* COMMANDS THAT ARE SENT VERY OFTEN */ + DEBUG_INIT = 0x08000, /* (de)allocation+initialization of data + structures */ DEBUG_LOCK = 0x10000, /* semaphore operations */ DEBUG_OUTPUT = 0x20000, /* output to device */ - DEBUG_ISO = 0x40000, /* isochronous transfers */ + DEBUG_ISO = 0x40000, /* isochronous transfers */ DEBUG_IF = 0x80000, /* character device operations */ - DEBUG_USBREQ = 0x100000, /* USB communication (except payload data) */ - DEBUG_LOCKCMD = 0x200000, /* AT commands and responses when MS_LOCKED */ + DEBUG_USBREQ = 0x100000, /* USB communication (except payload + data) */ + DEBUG_LOCKCMD = 0x200000, /* AT commands and responses when + MS_LOCKED */ - DEBUG_ANY = 0x3fffff, /* print message if any of the others is activated */ + DEBUG_ANY = 0x3fffff, /* print message if any of the others is + activated */ }; -#ifdef CONFIG_GIGASET_DEBUG -#define DEBUG_DEFAULT (DEBUG_INIT | DEBUG_TRANSCMD | DEBUG_CMD | DEBUG_USBREQ) -//#define DEBUG_DEFAULT (DEBUG_LOCK | DEBUG_INIT | DEBUG_TRANSCMD | DEBUG_CMD | DEBUF_IF | DEBUG_DRIVER | DEBUG_OUTPUT | DEBUG_INTR) -#else -#define DEBUG_DEFAULT 0 +/* missing from linux/device.h ... */ +#ifndef dev_notice +#define dev_notice(dev, format, arg...) \ + dev_printk(KERN_NOTICE , dev , format , ## arg) #endif -/* redefine syslog macros to prepend module name instead of entire source path */ -/* The space before the comma in ", ##" is needed by gcc 2.95 */ -#undef info -#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", THIS_MODULE ? THIS_MODULE->name : "gigaset_hw" , ## arg) - -#undef notice -#define notice(format, arg...) printk(KERN_NOTICE "%s: " format "\n", THIS_MODULE ? THIS_MODULE->name : "gigaset_hw" , ## arg) - -#undef warn -#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", THIS_MODULE ? THIS_MODULE->name : "gigaset_hw" , ## arg) - +/* Kernel message macros for situations where dev_printk and friends cannot be + * used for lack of reliable access to a device structure. + * linux/usb.h already contains these but in an obsolete form which clutters + * the log needlessly, and according to the USB maintainer those should be + * removed rather than fixed anyway. + */ #undef err -#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", THIS_MODULE ? THIS_MODULE->name : "gigaset_hw" , ## arg) +#undef info +#undef warn +#undef notice + +#define err(format, arg...) printk(KERN_ERR KBUILD_MODNAME ": " \ + format "\n" , ## arg) +#define info(format, arg...) printk(KERN_INFO KBUILD_MODNAME ": " \ + format "\n" , ## arg) +#define warn(format, arg...) printk(KERN_WARNING KBUILD_MODNAME ": " \ + format "\n" , ## arg) +#define notice(format, arg...) printk(KERN_NOTICE KBUILD_MODNAME ": " \ + format "\n" , ## arg) -#undef dbg #ifdef CONFIG_GIGASET_DEBUG -#define dbg(level, format, arg...) do { if (unlikely(((enum debuglevel)gigaset_debuglevel) & (level))) \ - printk(KERN_DEBUG "%s: " format "\n", THIS_MODULE ? THIS_MODULE->name : "gigaset_hw" , ## arg); } while (0) + +#define gig_dbg(level, format, arg...) \ + do { \ + if (unlikely(((enum debuglevel)gigaset_debuglevel) & (level))) \ + printk(KERN_DEBUG KBUILD_MODNAME ": " format "\n", \ + ## arg); \ + } while (0) +#define DEBUG_DEFAULT (DEBUG_INIT | DEBUG_TRANSCMD | DEBUG_CMD | DEBUG_USBREQ) + #else -#define dbg(level, format, arg...) do {} while (0) + +#define gig_dbg(level, format, arg...) do {} while (0) +#define DEBUG_DEFAULT 0 + #endif void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg, - size_t len, const unsigned char *buf, int from_user); + size_t len, const unsigned char *buf); /* connection state */ #define ZSAU_NONE 0 @@ -148,13 +165,14 @@ void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg, #define ZSAU_UNKNOWN -1 /* USB control transfer requests */ -#define OUT_VENDOR_REQ (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT) -#define IN_VENDOR_REQ (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT) +#define OUT_VENDOR_REQ (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT) +#define IN_VENDOR_REQ (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT) /* int-in-events 3070 */ #define HD_B1_FLOW_CONTROL 0x80 #define HD_B2_FLOW_CONTROL 0x81 -#define HD_RECEIVEATDATA_ACK (0x35) // 3070 // att: HD_RECEIVE>>AT<>AT< ignore */ - int max_ConState; /* <0 => ignore */ - int parameter; /* e.g. ZSAU_XXXX <0: ignore*/ - int new_ConState; /* <0 => ignore */ - int timeout; /* >0 => *HZ; <=0 => TOUT_XXXX*/ - int action[MAXACT]; /* ACT_XXXX */ - char *command; /* NULL==none */ + int resp_code; /* RSP_XXXX */ + int min_ConState; /* <0 => ignore */ + int max_ConState; /* <0 => ignore */ + int parameter; /* e.g. ZSAU_XXXX <0: ignore*/ + int new_ConState; /* <0 => ignore */ + int timeout; /* >0 => *HZ; <=0 => TOUT_XXXX*/ + int action[MAXACT]; /* ACT_XXXX */ + char *command; /* NULL==none */ }; extern struct reply_t gigaset_tab_cid_m10x[]; extern struct reply_t gigaset_tab_nocid_m10x[]; struct inbuf_t { - unsigned char *rcvbuf; /* usb-gigaset receive buffer */ + unsigned char *rcvbuf; /* usb-gigaset receive buffer */ struct bc_state *bcs; - struct cardstate *cs; - int inputstate; - - atomic_t head, tail; - unsigned char data[RBUFSIZE]; + struct cardstate *cs; + int inputstate; + atomic_t head, tail; + unsigned char data[RBUFSIZE]; }; /* isochronous write buffer structure @@ -319,16 +337,9 @@ struct inbuf_t { * if writesem <= 0, data[write..read-1] is currently being written to * - idle contains the byte value to repeat when the end of valid data is * reached; if nextread==write (buffer contains no data to send), either the - * BAS_OUTBUFPAD bytes immediately before data[write] (if write>=BAS_OUTBUFPAD) - * or those of the pad area (if write=BAS_OUTBUFPAD) or those of the pad area (if write for modem reponses (and incomming data for M10x) - * -> on timeout - * -> after setting bits in xxx.at_state.pending_command - * (e.g. command from LL) */ - struct tasklet_struct write_tasklet; /* tasklet for serial output - * (not used in base driver) */ + int dle; /* !=0 if modem commands/responses are + dle encoded */ + int cur_at_seq; /* sequence of AT commands being + processed */ + int curchannel; /* channel those commands are meant + for */ + atomic_t commands_pending; /* flag(s) in xxx.commands_pending have + been set */ + struct tasklet_struct event_tasklet; + /* tasklet for serializing AT commands. + * Scheduled + * -> for modem reponses (and + * incoming data for M10x) + * -> on timeout + * -> after setting bits in + * xxx.at_state.pending_command + * (e.g. command from LL) */ + struct tasklet_struct write_tasklet; + /* tasklet for serial output + * (not used in base driver) */ /* event queue */ struct event_t events[MAX_EVENTS]; - atomic_t ev_tail, ev_head; + unsigned ev_tail, ev_head; spinlock_t ev_lock; /* current modem response */ unsigned char respdata[MAX_RESP_SIZE]; unsigned cbytes; - /* hardware drivers */ + /* private data of hardware drivers */ union { - struct usb_cardstate *usb; /* private data of USB hardware driver */ - struct ser_cardstate *ser; /* private data of serial hardware driver */ - struct bas_cardstate *bas; /* private data of base hardware driver */ + struct usb_cardstate *usb; /* USB hardware driver (m105) */ + struct ser_cardstate *ser; /* serial hardware driver */ + struct bas_cardstate *bas; /* USB hardware driver (base) */ } hw; }; struct gigaset_driver { struct list_head list; - spinlock_t lock; /* locks minor tables and blocked */ - //struct semaphore sem; /* locks this structure */ + spinlock_t lock; /* locks minor tables and blocked */ struct tty_driver *tty; unsigned have_tty; unsigned minor; @@ -553,37 +571,42 @@ struct bas_bc_state { struct isow_urbctx_t isoouturbs[BAS_OUTURBS]; struct isow_urbctx_t *isooutdone, *isooutfree, *isooutovfl; struct isowbuf_t *isooutbuf; - unsigned numsub; /* submitted URB counter (for diagnostic messages only) */ + unsigned numsub; /* submitted URB counter + (for diagnostic messages only) */ struct tasklet_struct sent_tasklet; /* isochronous input state */ spinlock_t isoinlock; struct urb *isoinurbs[BAS_INURBS]; unsigned char isoinbuf[BAS_INBUFSIZE * BAS_INURBS]; - struct urb *isoindone; /* completed isoc read URB */ - int loststatus; /* status of dropped URB */ - unsigned isoinlost; /* number of bytes lost */ - /* state of bit unstuffing algorithm (in addition to BC_state.inputstate) */ - unsigned seqlen; /* number of '1' bits not yet unstuffed */ - unsigned inbyte, inbits; /* collected bits for next byte */ + struct urb *isoindone; /* completed isoc read URB */ + int loststatus; /* status of dropped URB */ + unsigned isoinlost; /* number of bytes lost */ + /* state of bit unstuffing algorithm + (in addition to BC_state.inputstate) */ + unsigned seqlen; /* number of '1' bits not yet + unstuffed */ + unsigned inbyte, inbits; /* collected bits for next byte */ /* statistics */ - unsigned goodbytes; /* bytes correctly received */ - unsigned alignerrs; /* frames with incomplete byte at end */ - unsigned fcserrs; /* FCS errors */ - unsigned frameerrs; /* framing errors */ - unsigned giants; /* long frames */ - unsigned runts; /* short frames */ - unsigned aborts; /* HDLC aborts */ - unsigned shared0s; /* '0' bits shared between flags */ - unsigned stolen0s; /* '0' stuff bits also serving as leading flag bits */ + unsigned goodbytes; /* bytes correctly received */ + unsigned alignerrs; /* frames with incomplete byte at end */ + unsigned fcserrs; /* FCS errors */ + unsigned frameerrs; /* framing errors */ + unsigned giants; /* long frames */ + unsigned runts; /* short frames */ + unsigned aborts; /* HDLC aborts */ + unsigned shared0s; /* '0' bits shared between flags */ + unsigned stolen0s; /* '0' stuff bits also serving as + leading flag bits */ struct tasklet_struct rcvd_tasklet; }; struct gigaset_ops { - /* Called from ev-layer.c/interface.c for sending AT commands to the device */ + /* Called from ev-layer.c/interface.c for sending AT commands to the + device */ int (*write_cmd)(struct cardstate *cs, - const unsigned char *buf, int len, - struct tasklet_struct *wake_tasklet); + const unsigned char *buf, int len, + struct tasklet_struct *wake_tasklet); /* Called from interface.c for additional device control */ int (*write_room)(struct cardstate *cs); @@ -604,7 +627,8 @@ struct gigaset_ops { /* Called by gigaset_freecs() for freeing bcs->hw.xxx */ int (*freebcshw)(struct bc_state *bcs); - /* Called by gigaset_stop() or gigaset_bchannel_down() for resetting bcs->hw.xxx */ + /* Called by gigaset_stop() or gigaset_bchannel_down() for resetting + bcs->hw.xxx */ void (*reinitbcshw)(struct bc_state *bcs); /* Called by gigaset_initcs() for setting up cs->hw.xxx */ @@ -613,13 +637,10 @@ struct gigaset_ops { /* Called by gigaset_freecs() for freeing cs->hw.xxx */ void (*freecshw)(struct cardstate *cs); - ///* Called by gigaset_stop() for killing URBs, shutting down the device, ... - // hardwareup: ==0: don't try to shut down the device, hardware is really not accessible - // !=0: hardware still up */ - //void (*stophw)(struct cardstate *cs, int hardwareup); - - /* Called from common.c/interface.c for additional serial port control */ - int (*set_modem_ctrl)(struct cardstate *cs, unsigned old_state, unsigned new_state); + /* Called from common.c/interface.c for additional serial port + control */ + int (*set_modem_ctrl)(struct cardstate *cs, unsigned old_state, + unsigned new_state); int (*baud_rate)(struct cardstate *cs, unsigned cflag); int (*set_line_ctrl)(struct cardstate *cs, unsigned cflag); @@ -639,7 +660,7 @@ struct gigaset_ops { * : 0x10 * : ((a-z)* | (A-Z)* | (0-10)*)+ */ -#define DLE_FLAG 0x10 +#define DLE_FLAG 0x10 /* =========================================================================== * Functions implemented in asyncdata.c @@ -667,7 +688,8 @@ void gigaset_isoc_input(struct inbuf_t *inbuf); /* Called from bas-gigaset.c to process a block of data * received through the isochronous channel */ -void gigaset_isoc_receive(unsigned char *src, unsigned count, struct bc_state *bcs); +void gigaset_isoc_receive(unsigned char *src, unsigned count, + struct bc_state *bcs); /* Called from bas-gigaset.c to put a block of data * into the isochronous output buffer */ @@ -703,7 +725,7 @@ static inline void gigaset_isdn_rcv_err(struct bc_state *bcs) isdn_ctrl response; /* error -> LL */ - dbg(DEBUG_CMD, "sending L1ERR"); + gig_dbg(DEBUG_CMD, "sending L1ERR"); response.driver = bcs->cs->myid; response.command = ISDN_STAT_L1ERR; response.arg = bcs->channel; @@ -727,8 +749,8 @@ void gigaset_handle_modem_response(struct cardstate *cs); */ /* initialize sysfs for device */ -void gigaset_init_dev_sysfs(struct usb_interface *interface); -void gigaset_free_dev_sysfs(struct usb_interface *interface); +void gigaset_init_dev_sysfs(struct cardstate *cs); +void gigaset_free_dev_sysfs(struct cardstate *cs); /* =========================================================================== * Functions implemented in common.c/gigaset.h @@ -736,7 +758,7 @@ void gigaset_free_dev_sysfs(struct usb_interface *interface); void gigaset_bcs_reinit(struct bc_state *bcs); void gigaset_at_init(struct at_state_t *at_state, struct bc_state *bcs, - struct cardstate *cs, int cid); + struct cardstate *cs, int cid); int gigaset_get_channel(struct bc_state *bcs); void gigaset_free_channel(struct bc_state *bcs); int gigaset_get_channels(struct cardstate *cs); @@ -745,16 +767,15 @@ void gigaset_block_channels(struct cardstate *cs); /* Allocate and initialize driver structure. */ struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors, - const char *procname, - const char *devname, - const char *devfsname, - const struct gigaset_ops *ops, - struct module *owner); + const char *procname, + const char *devname, + const char *devfsname, + const struct gigaset_ops *ops, + struct module *owner); /* Deallocate driver structure. */ void gigaset_freedriver(struct gigaset_driver *drv); void gigaset_debugdrivers(void); -struct cardstate *gigaset_get_cs_by_minor(unsigned minor); struct cardstate *gigaset_get_cs_by_tty(struct tty_struct *tty); struct cardstate *gigaset_get_cs_by_id(int id); @@ -763,7 +784,8 @@ struct cardstate *gigaset_getunassignedcs(struct gigaset_driver *drv); void gigaset_unassign(struct cardstate *cs); void gigaset_blockdriver(struct gigaset_driver *drv); -/* Allocate and initialize card state. Calls hardware dependent gigaset_init[b]cs(). */ +/* Allocate and initialize card state. Calls hardware dependent + gigaset_init[b]cs(). */ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, int onechannel, int ignoreframes, int cidmode, const char *modulename); @@ -788,8 +810,8 @@ void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb); * ptr must be kmalloc()ed (and not be freed by the caller). */ struct event_t *gigaset_add_event(struct cardstate *cs, - struct at_state_t *at_state, int type, - void *ptr, int parameter, void *arg); + struct at_state_t *at_state, int type, + void *ptr, int parameter, void *arg); /* Called on CONFIG1 command from frontend. */ int gigaset_enterconfigmode(struct cardstate *cs); //0: success <0: errorcode @@ -799,7 +821,7 @@ static inline void gigaset_schedule_event(struct cardstate *cs) { unsigned long flags; spin_lock_irqsave(&cs->lock, flags); - if (atomic_read(&cs->running)) + if (cs->running) tasklet_schedule(&cs->event_tasklet); spin_unlock_irqrestore(&cs->lock, flags); } @@ -810,7 +832,7 @@ static inline void gigaset_bchannel_down(struct bc_state *bcs) { gigaset_add_event(bcs->cs, &bcs->at_state, EV_BC_CLOSED, NULL, 0, NULL); - dbg(DEBUG_CMD, "scheduling BC_CLOSED"); + gig_dbg(DEBUG_CMD, "scheduling BC_CLOSED"); gigaset_schedule_event(bcs->cs); } @@ -820,36 +842,19 @@ static inline void gigaset_bchannel_up(struct bc_state *bcs) { gigaset_add_event(bcs->cs, &bcs->at_state, EV_BC_OPEN, NULL, 0, NULL); - dbg(DEBUG_CMD, "scheduling BC_OPEN"); + gig_dbg(DEBUG_CMD, "scheduling BC_OPEN"); gigaset_schedule_event(bcs->cs); } /* handling routines for sk_buff */ /* ============================= */ -/* private version of __skb_put() - * append 'len' bytes to the content of 'skb', already knowing that the - * existing buffer can accomodate them - * returns a pointer to the location where the new bytes should be copied to - * This function does not take any locks so it must be called with the - * appropriate locks held only. - */ -static inline unsigned char *gigaset_skb_put_quick(struct sk_buff *skb, - unsigned int len) -{ - unsigned char *tmp = skb->tail; - /*SKB_LINEAR_ASSERT(skb);*/ /* not needed here */ - skb->tail += len; - skb->len += len; - return tmp; -} - /* pass received skb to LL * Warning: skb must not be accessed anymore! */ static inline void gigaset_rcv_skb(struct sk_buff *skb, - struct cardstate *cs, - struct bc_state *bcs) + struct cardstate *cs, + struct bc_state *bcs) { cs->iif.rcvcallb_skb(cs->myid, bcs->channel, skb); bcs->trans_down++; @@ -859,8 +864,8 @@ static inline void gigaset_rcv_skb(struct sk_buff *skb, * Warning: skb must not be accessed anymore! */ static inline void gigaset_rcv_error(struct sk_buff *procskb, - struct cardstate *cs, - struct bc_state *bcs) + struct cardstate *cs, + struct bc_state *bcs) { if (procskb) dev_kfree_skb(procskb); @@ -877,46 +882,9 @@ static inline void gigaset_rcv_error(struct sk_buff *procskb, /* bitwise byte inversion table */ extern __u8 gigaset_invtab[]; /* in common.c */ - /* append received bytes to inbuf */ -static inline int gigaset_fill_inbuf(struct inbuf_t *inbuf, - const unsigned char *src, - unsigned numbytes) -{ - unsigned n, head, tail, bytesleft; - - dbg(DEBUG_INTR, "received %u bytes", numbytes); - - if (!numbytes) - return 0; - - bytesleft = numbytes; - tail = atomic_read(&inbuf->tail); - head = atomic_read(&inbuf->head); - dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail); - - while (bytesleft) { - if (head > tail) - n = head - 1 - tail; - else if (head == 0) - n = (RBUFSIZE-1) - tail; - else - n = RBUFSIZE - tail; - if (!n) { - err("buffer overflow (%u bytes lost)", bytesleft); - break; - } - if (n > bytesleft) - n = bytesleft; - memcpy(inbuf->data + tail, src, n); - bytesleft -= n; - tail = (tail + n) % RBUFSIZE; - src += n; - } - dbg(DEBUG_INTR, "setting tail to %u", tail); - atomic_set(&inbuf->tail, tail); - return numbytes != bytesleft; -} +int gigaset_fill_inbuf(struct inbuf_t *inbuf, const unsigned char *src, + unsigned numbytes); /* =========================================================================== * Functions implemented in interface.c @@ -924,7 +892,7 @@ static inline int gigaset_fill_inbuf(struct inbuf_t *inbuf, /* initialize interface */ void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname, - const char *devname, const char *devfsname); + const char *devname, const char *devfsname); /* release interface */ void gigaset_if_freedriver(struct gigaset_driver *drv); /* add minor */ @@ -933,6 +901,6 @@ void gigaset_if_init(struct cardstate *cs); void gigaset_if_free(struct cardstate *cs); /* device received data */ void gigaset_if_receive(struct cardstate *cs, - unsigned char *buffer, size_t len); + unsigned char *buffer, size_t len); #endif diff --git a/drivers/isdn/gigaset/i4l.c b/drivers/isdn/gigaset/i4l.c index 731a675f21b0..0815dbfb8291 100644 --- a/drivers/isdn/gigaset/i4l.c +++ b/drivers/isdn/gigaset/i4l.c @@ -1,9 +1,9 @@ /* * Stuff used by all variants of the driver * - * Copyright (c) 2001 by Stefan Eilers (Eilers.Stefan@epost.de), - * Hansjoerg Lipp (hjlipp@web.de), - * Tilman Schmidt (tilman@imap.cc). + * Copyright (c) 2001 by Stefan Eilers, + * Hansjoerg Lipp , + * Tilman Schmidt . * * ===================================================================== * This program is free software; you can redistribute it and/or @@ -11,15 +11,11 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * ===================================================================== - * ToDo: ... - * ===================================================================== - * Version: $Id: i4l.c,v 1.3.2.9 2006/02/04 18:28:16 hjlipp Exp $ - * ===================================================================== */ #include "gigaset.h" -/* == Handling of I4L IO ============================================================================*/ +/* == Handling of I4L IO =====================================================*/ /* writebuf_from_LL * called by LL to transmit data on an open channel @@ -29,14 +25,16 @@ * parameters: * driverID driver ID as assigned by LL * channel channel number - * ack if != 0 LL wants to be notified on completion via statcallb(ISDN_STAT_BSENT) + * ack if != 0 LL wants to be notified on completion via + * statcallb(ISDN_STAT_BSENT) * skb skb containing data to send * return value: * number of accepted bytes * 0 if temporarily unable to accept data (out of buffer space) * <0 on error (eg. -EINVAL) */ -static int writebuf_from_LL(int driverID, int channel, int ack, struct sk_buff *skb) +static int writebuf_from_LL(int driverID, int channel, int ack, + struct sk_buff *skb) { struct cardstate *cs; struct bc_state *bcs; @@ -54,31 +52,28 @@ static int writebuf_from_LL(int driverID, int channel, int ack, struct sk_buff * bcs = &cs->bcs[channel]; len = skb->len; - dbg(DEBUG_LLDATA, - "Receiving data from LL (id: %d, channel: %d, ack: %d, size: %d)", - driverID, channel, ack, len); + gig_dbg(DEBUG_LLDATA, + "Receiving data from LL (id: %d, ch: %d, ack: %d, sz: %d)", + driverID, channel, ack, len); if (!len) { if (ack) - warn("not ACKing empty packet from LL"); + notice("%s: not ACKing empty packet", __func__); return 0; } if (len > MAX_BUF_SIZE) { - err("%s: packet too large (%d bytes)", __func__, channel); + err("%s: packet too large (%d bytes)", __func__, len); return -EINVAL; } - if (!atomic_read(&cs->connected)) - return -ENODEV; - skblen = ack ? len : 0; skb->head[0] = skblen & 0xff; skb->head[1] = skblen >> 8; - dbg(DEBUG_MCMD, "skb: len=%u, skblen=%u: %02x %02x", len, skblen, - (unsigned) skb->head[0], (unsigned) skb->head[1]); + gig_dbg(DEBUG_MCMD, "skb: len=%u, skblen=%u: %02x %02x", + len, skblen, (unsigned) skb->head[0], (unsigned) skb->head[1]); /* pass to device-specific module */ - return cs->ops->send_skb(bcs, skb); + return cs->ops->send_skb(bcs, skb); //FIXME cs->ops->send_skb() must handle !cs->connected correctly } void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb) @@ -89,14 +84,14 @@ void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb) ++bcs->trans_up; if (skb->len) - warn("%s: skb->len==%d", __func__, skb->len); + dev_warn(bcs->cs->dev, "%s: skb->len==%d\n", + __func__, skb->len); len = (unsigned char) skb->head[0] | (unsigned) (unsigned char) skb->head[1] << 8; if (len) { - dbg(DEBUG_MCMD, - "Acknowledge sending to LL (id: %d, channel: %d size: %u)", - bcs->cs->myid, bcs->channel, len); + gig_dbg(DEBUG_MCMD, "ACKing to LL (id: %d, ch: %d, sz: %u)", + bcs->cs->myid, bcs->channel, len); response.driver = bcs->cs->myid; response.command = ISDN_STAT_BSENT; @@ -119,15 +114,12 @@ static int command_from_LL(isdn_ctrl *cntrl) struct bc_state *bcs; int retval = 0; struct setup_parm *sp; + unsigned param; + unsigned long flags; - //dbg(DEBUG_ANY, "Gigaset_HW: Receiving command"); gigaset_debugdrivers(); - /* Terminate this call if no device is present. Bt if the command is "ISDN_CMD_LOCK" or - * "ISDN_CMD_UNLOCK" then execute it due to the fact that they are device independent ! - */ - //FIXME "remove test for &connected" - if ((!cs || !atomic_read(&cs->connected))) { + if (!cs) { warn("LL tried to access unknown device with nr. %d", cntrl->driver); return -ENODEV; @@ -135,29 +127,30 @@ static int command_from_LL(isdn_ctrl *cntrl) switch (cntrl->command) { case ISDN_CMD_IOCTL: - - dbg(DEBUG_ANY, "ISDN_CMD_IOCTL (driver:%d,arg: %ld)", - cntrl->driver, cntrl->arg); + gig_dbg(DEBUG_ANY, "ISDN_CMD_IOCTL (driver: %d, arg: %ld)", + cntrl->driver, cntrl->arg); warn("ISDN_CMD_IOCTL is not supported."); return -EINVAL; case ISDN_CMD_DIAL: - dbg(DEBUG_ANY, "ISDN_CMD_DIAL (driver: %d, channel: %ld, " - "phone: %s,ownmsn: %s, si1: %d, si2: %d)", - cntrl->driver, cntrl->arg, - cntrl->parm.setup.phone, cntrl->parm.setup.eazmsn, - cntrl->parm.setup.si1, cntrl->parm.setup.si2); + gig_dbg(DEBUG_ANY, + "ISDN_CMD_DIAL (driver: %d, ch: %ld, " + "phone: %s, ownmsn: %s, si1: %d, si2: %d)", + cntrl->driver, cntrl->arg, + cntrl->parm.setup.phone, cntrl->parm.setup.eazmsn, + cntrl->parm.setup.si1, cntrl->parm.setup.si2); if (cntrl->arg >= cs->channels) { - err("invalid channel (%d)", (int) cntrl->arg); + err("ISDN_CMD_DIAL: invalid channel (%d)", + (int) cntrl->arg); return -EINVAL; } bcs = cs->bcs + cntrl->arg; if (!gigaset_get_channel(bcs)) { - err("channel not free"); + err("ISDN_CMD_DIAL: channel not free"); return -EBUSY; } @@ -169,42 +162,46 @@ static int command_from_LL(isdn_ctrl *cntrl) } *sp = cntrl->parm.setup; - if (!gigaset_add_event(cs, &bcs->at_state, EV_DIAL, sp, - atomic_read(&bcs->at_state.seq_index), - NULL)) { + spin_lock_irqsave(&cs->lock, flags); + param = bcs->at_state.seq_index; + spin_unlock_irqrestore(&cs->lock, flags); + + if (!gigaset_add_event(cs, &bcs->at_state, EV_DIAL, sp, param, + NULL)) { //FIXME what should we do? kfree(sp); gigaset_free_channel(bcs); return -ENOMEM; } - dbg(DEBUG_CMD, "scheduling DIAL"); + gig_dbg(DEBUG_CMD, "scheduling DIAL"); gigaset_schedule_event(cs); break; case ISDN_CMD_ACCEPTD: //FIXME - dbg(DEBUG_ANY, "ISDN_CMD_ACCEPTD"); + gig_dbg(DEBUG_ANY, "ISDN_CMD_ACCEPTD"); if (cntrl->arg >= cs->channels) { - err("invalid channel (%d)", (int) cntrl->arg); + err("ISDN_CMD_ACCEPTD: invalid channel (%d)", + (int) cntrl->arg); return -EINVAL; } if (!gigaset_add_event(cs, &cs->bcs[cntrl->arg].at_state, - EV_ACCEPT, NULL, 0, NULL)) { + EV_ACCEPT, NULL, 0, NULL)) { //FIXME what should we do? return -ENOMEM; } - dbg(DEBUG_CMD, "scheduling ACCEPT"); + gig_dbg(DEBUG_CMD, "scheduling ACCEPT"); gigaset_schedule_event(cs); break; case ISDN_CMD_ACCEPTB: - dbg(DEBUG_ANY, "ISDN_CMD_ACCEPTB"); + gig_dbg(DEBUG_ANY, "ISDN_CMD_ACCEPTB"); break; case ISDN_CMD_HANGUP: - dbg(DEBUG_ANY, - "ISDN_CMD_HANGUP (channel: %d)", (int) cntrl->arg); + gig_dbg(DEBUG_ANY, "ISDN_CMD_HANGUP (ch: %d)", + (int) cntrl->arg); if (cntrl->arg >= cs->channels) { err("ISDN_CMD_HANGUP: invalid channel (%u)", @@ -213,66 +210,68 @@ static int command_from_LL(isdn_ctrl *cntrl) } if (!gigaset_add_event(cs, &cs->bcs[cntrl->arg].at_state, - EV_HUP, NULL, 0, NULL)) { + EV_HUP, NULL, 0, NULL)) { //FIXME what should we do? return -ENOMEM; } - dbg(DEBUG_CMD, "scheduling HUP"); + gig_dbg(DEBUG_CMD, "scheduling HUP"); gigaset_schedule_event(cs); break; - case ISDN_CMD_CLREAZ: /* Do not signal incoming signals */ //FIXME - dbg(DEBUG_ANY, "ISDN_CMD_CLREAZ"); + case ISDN_CMD_CLREAZ: /* Do not signal incoming signals */ //FIXME + gig_dbg(DEBUG_ANY, "ISDN_CMD_CLREAZ"); break; - case ISDN_CMD_SETEAZ: /* Signal incoming calls for given MSN */ //FIXME - dbg(DEBUG_ANY, - "ISDN_CMD_SETEAZ (id:%d, channel: %ld, number: %s)", - cntrl->driver, cntrl->arg, cntrl->parm.num); + case ISDN_CMD_SETEAZ: /* Signal incoming calls for given MSN */ //FIXME + gig_dbg(DEBUG_ANY, + "ISDN_CMD_SETEAZ (id: %d, ch: %ld, number: %s)", + cntrl->driver, cntrl->arg, cntrl->parm.num); break; - case ISDN_CMD_SETL2: /* Set L2 to given protocol */ - dbg(DEBUG_ANY, "ISDN_CMD_SETL2 (Channel: %ld, Proto: %lx)", - cntrl->arg & 0xff, (cntrl->arg >> 8)); + case ISDN_CMD_SETL2: /* Set L2 to given protocol */ + gig_dbg(DEBUG_ANY, "ISDN_CMD_SETL2 (ch: %ld, proto: %lx)", + cntrl->arg & 0xff, (cntrl->arg >> 8)); if ((cntrl->arg & 0xff) >= cs->channels) { - err("invalid channel (%u)", + err("ISDN_CMD_SETL2: invalid channel (%u)", (unsigned) cntrl->arg & 0xff); return -EINVAL; } if (!gigaset_add_event(cs, &cs->bcs[cntrl->arg & 0xff].at_state, - EV_PROTO_L2, NULL, cntrl->arg >> 8, - NULL)) { + EV_PROTO_L2, NULL, cntrl->arg >> 8, + NULL)) { //FIXME what should we do? return -ENOMEM; } - dbg(DEBUG_CMD, "scheduling PROTO_L2"); + gig_dbg(DEBUG_CMD, "scheduling PROTO_L2"); gigaset_schedule_event(cs); break; - case ISDN_CMD_SETL3: /* Set L3 to given protocol */ - dbg(DEBUG_ANY, "ISDN_CMD_SETL3 (Channel: %ld, Proto: %lx)", - cntrl->arg & 0xff, (cntrl->arg >> 8)); + case ISDN_CMD_SETL3: /* Set L3 to given protocol */ + gig_dbg(DEBUG_ANY, "ISDN_CMD_SETL3 (ch: %ld, proto: %lx)", + cntrl->arg & 0xff, (cntrl->arg >> 8)); if ((cntrl->arg & 0xff) >= cs->channels) { - err("invalid channel (%u)", + err("ISDN_CMD_SETL3: invalid channel (%u)", (unsigned) cntrl->arg & 0xff); return -EINVAL; } if (cntrl->arg >> 8 != ISDN_PROTO_L3_TRANS) { - err("invalid protocol %lu", cntrl->arg >> 8); + err("ISDN_CMD_SETL3: invalid protocol %lu", + cntrl->arg >> 8); return -EINVAL; } break; case ISDN_CMD_PROCEED: - dbg(DEBUG_ANY, "ISDN_CMD_PROCEED"); //FIXME + gig_dbg(DEBUG_ANY, "ISDN_CMD_PROCEED"); //FIXME break; case ISDN_CMD_ALERT: - dbg(DEBUG_ANY, "ISDN_CMD_ALERT"); //FIXME + gig_dbg(DEBUG_ANY, "ISDN_CMD_ALERT"); //FIXME if (cntrl->arg >= cs->channels) { - err("invalid channel (%d)", (int) cntrl->arg); + err("ISDN_CMD_ALERT: invalid channel (%d)", + (int) cntrl->arg); return -EINVAL; } //bcs = cs->bcs + cntrl->arg; @@ -280,32 +279,31 @@ static int command_from_LL(isdn_ctrl *cntrl) // FIXME break; case ISDN_CMD_REDIR: - dbg(DEBUG_ANY, "ISDN_CMD_REDIR"); //FIXME + gig_dbg(DEBUG_ANY, "ISDN_CMD_REDIR"); //FIXME break; case ISDN_CMD_PROT_IO: - dbg(DEBUG_ANY, "ISDN_CMD_PROT_IO"); + gig_dbg(DEBUG_ANY, "ISDN_CMD_PROT_IO"); break; case ISDN_CMD_FAXCMD: - dbg(DEBUG_ANY, "ISDN_CMD_FAXCMD"); + gig_dbg(DEBUG_ANY, "ISDN_CMD_FAXCMD"); break; case ISDN_CMD_GETL2: - dbg(DEBUG_ANY, "ISDN_CMD_GETL2"); + gig_dbg(DEBUG_ANY, "ISDN_CMD_GETL2"); break; case ISDN_CMD_GETL3: - dbg(DEBUG_ANY, "ISDN_CMD_GETL3"); + gig_dbg(DEBUG_ANY, "ISDN_CMD_GETL3"); break; case ISDN_CMD_GETEAZ: - dbg(DEBUG_ANY, "ISDN_CMD_GETEAZ"); + gig_dbg(DEBUG_ANY, "ISDN_CMD_GETEAZ"); break; case ISDN_CMD_SETSIL: - dbg(DEBUG_ANY, "ISDN_CMD_SETSIL"); + gig_dbg(DEBUG_ANY, "ISDN_CMD_SETSIL"); break; case ISDN_CMD_GETSIL: - dbg(DEBUG_ANY, "ISDN_CMD_GETSIL"); + gig_dbg(DEBUG_ANY, "ISDN_CMD_GETSIL"); break; default: - err("unknown command %d from LL", - cntrl->command); + err("unknown command %d from LL", cntrl->command); return -EINVAL; } @@ -350,7 +348,8 @@ int gigaset_isdn_setup_dial(struct at_state_t *at_state, void *data) proto = 2; /* 0: Bitsynchron, 1: HDLC, 2: voice */ break; default: - err("invalid protocol: %u", bcs->proto2); + dev_err(bcs->cs->dev, "%s: invalid L2 protocol: %u\n", + __func__, bcs->proto2); return -EINVAL; } @@ -378,7 +377,7 @@ int gigaset_isdn_setup_dial(struct at_state_t *at_state, void *data) bcs->commands[i] = NULL; if (length[i] && !(bcs->commands[i] = kmalloc(length[i], GFP_ATOMIC))) { - err("out of memory"); + dev_err(bcs->cs->dev, "out of memory\n"); return -ENOMEM; } } @@ -396,10 +395,14 @@ int gigaset_isdn_setup_dial(struct at_state_t *at_state, void *data) } if (bcs->commands[AT_MSN]) - snprintf(bcs->commands[AT_MSN], length[AT_MSN], "^SMSN=%s\r", sp->eazmsn); - snprintf(bcs->commands[AT_BC ], length[AT_BC ], "^SBC=%s\r", bc); - snprintf(bcs->commands[AT_PROTO], length[AT_PROTO], "^SBPR=%u\r", proto); - snprintf(bcs->commands[AT_ISO ], length[AT_ISO ], "^SISO=%u\r", (unsigned)bcs->channel + 1); + snprintf(bcs->commands[AT_MSN], length[AT_MSN], + "^SMSN=%s\r", sp->eazmsn); + snprintf(bcs->commands[AT_BC ], length[AT_BC ], + "^SBC=%s\r", bc); + snprintf(bcs->commands[AT_PROTO], length[AT_PROTO], + "^SBPR=%u\r", proto); + snprintf(bcs->commands[AT_ISO ], length[AT_ISO ], + "^SISO=%u\r", (unsigned)bcs->channel + 1); return 0; } @@ -419,7 +422,8 @@ int gigaset_isdn_setup_accept(struct at_state_t *at_state) proto = 2; /* 0: Bitsynchron, 1: HDLC, 2: voice */ break; default: - err("invalid protocol: %u", bcs->proto2); + dev_err(at_state->cs->dev, "%s: invalid protocol: %u\n", + __func__, bcs->proto2); return -EINVAL; } @@ -436,13 +440,15 @@ int gigaset_isdn_setup_accept(struct at_state_t *at_state) bcs->commands[i] = NULL; if (length[i] && !(bcs->commands[i] = kmalloc(length[i], GFP_ATOMIC))) { - err("out of memory"); + dev_err(at_state->cs->dev, "out of memory\n"); return -ENOMEM; } } - snprintf(bcs->commands[AT_PROTO], length[AT_PROTO], "^SBPR=%u\r", proto); - snprintf(bcs->commands[AT_ISO ], length[AT_ISO ], "^SISO=%u\r", (unsigned) bcs->channel + 1); + snprintf(bcs->commands[AT_PROTO], length[AT_PROTO], + "^SBPR=%u\r", proto); + snprintf(bcs->commands[AT_ISO ], length[AT_ISO ], + "^SISO=%u\r", (unsigned) bcs->channel + 1); return 0; } @@ -473,7 +479,7 @@ int gigaset_isdn_icall(struct at_state_t *at_state) response.parm.setup.si1 = 1; response.parm.setup.si2 = 2; } else { - warn("RING ignored - unsupported BC %s", + dev_warn(cs->dev, "RING ignored - unsupported BC %s\n", at_state->str_var[STR_ZBC]); return ICALL_IGNORE; } @@ -491,18 +497,17 @@ int gigaset_isdn_icall(struct at_state_t *at_state) response.parm.setup.eazmsn[0] = 0; if (!bcs) { - notice("no channel for incoming call"); - dbg(DEBUG_CMD, "Sending ICALLW"); + dev_notice(cs->dev, "no channel for incoming call\n"); response.command = ISDN_STAT_ICALLW; response.arg = 0; //FIXME } else { - dbg(DEBUG_CMD, "Sending ICALL"); + gig_dbg(DEBUG_CMD, "Sending ICALL"); response.command = ISDN_STAT_ICALL; response.arg = bcs->channel; //FIXME } response.driver = cs->myid; retval = cs->iif.statcallb(&response); - dbg(DEBUG_CMD, "Response: %d", retval); + gig_dbg(DEBUG_CMD, "Response: %d", retval); switch (retval) { case 0: /* no takers */ return ICALL_IGNORE; @@ -512,7 +517,8 @@ int gigaset_isdn_icall(struct at_state_t *at_state) case 2: /* reject */ return ICALL_REJECT; case 3: /* incomplete */ - warn("LL requested unsupported feature: Incomplete Number"); + dev_warn(cs->dev, + "LL requested unsupported feature: Incomplete Number\n"); return ICALL_IGNORE; case 4: /* proceeding */ /* Gigaset will send ALERTING anyway. @@ -520,10 +526,11 @@ int gigaset_isdn_icall(struct at_state_t *at_state) */ return ICALL_ACCEPT; case 5: /* deflect */ - warn("LL requested unsupported feature: Call Deflection"); + dev_warn(cs->dev, + "LL requested unsupported feature: Call Deflection\n"); return ICALL_IGNORE; default: - err("LL error %d on ICALL", retval); + dev_err(cs->dev, "LL error %d on ICALL\n", retval); return ICALL_IGNORE; } } @@ -533,7 +540,7 @@ int gigaset_register_to_LL(struct cardstate *cs, const char *isdnid) { isdn_if *iif = &cs->iif; - dbg(DEBUG_ANY, "Register driver capabilities to LL"); + gig_dbg(DEBUG_ANY, "Register driver capabilities to LL"); //iif->id[sizeof(iif->id) - 1]=0; //strncpy(iif->id, isdnid, sizeof(iif->id) - 1); @@ -542,26 +549,26 @@ int gigaset_register_to_LL(struct cardstate *cs, const char *isdnid) return -ENOMEM; //FIXME EINVAL/...?? iif->owner = THIS_MODULE; - iif->channels = cs->channels; /* I am supporting just one channel *//* I was supporting...*/ + iif->channels = cs->channels; iif->maxbufsize = MAX_BUF_SIZE; - iif->features = ISDN_FEATURE_L2_TRANS | /* Our device is very advanced, therefore */ + iif->features = ISDN_FEATURE_L2_TRANS | ISDN_FEATURE_L2_HDLC | #ifdef GIG_X75 ISDN_FEATURE_L2_X75I | #endif ISDN_FEATURE_L3_TRANS | ISDN_FEATURE_P_EURO; - iif->hl_hdrlen = HW_HDR_LEN; /* Area for storing ack */ + iif->hl_hdrlen = HW_HDR_LEN; /* Area for storing ack */ iif->command = command_from_LL; iif->writebuf_skb = writebuf_from_LL; - iif->writecmd = NULL; /* Don't support isdnctrl */ - iif->readstat = NULL; /* Don't support isdnctrl */ - iif->rcvcallb_skb = NULL; /* Will be set by LL */ - iif->statcallb = NULL; /* Will be set by LL */ + iif->writecmd = NULL; /* Don't support isdnctrl */ + iif->readstat = NULL; /* Don't support isdnctrl */ + iif->rcvcallb_skb = NULL; /* Will be set by LL */ + iif->statcallb = NULL; /* Will be set by LL */ if (!register_isdn(iif)) return 0; - cs->myid = iif->channels; /* Set my device id */ + cs->myid = iif->channels; /* Set my device id */ return 1; } diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c index 3a81d9c65141..08e4c4eea14d 100644 --- a/drivers/isdn/gigaset/interface.c +++ b/drivers/isdn/gigaset/interface.c @@ -9,8 +9,6 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * ===================================================================== - * Version: $Id: interface.c,v 1.14.4.15 2006/02/04 18:28:16 hjlipp Exp $ - * ===================================================================== */ #include "gigaset.h" @@ -24,7 +22,7 @@ static int if_lock(struct cardstate *cs, int *arg) { int cmd = *arg; - dbg(DEBUG_IF, "%u: if_lock (%d)", cs->minor_index, cmd); + gig_dbg(DEBUG_IF, "%u: if_lock (%d)", cs->minor_index, cmd); if (cmd > 1) return -EINVAL; @@ -35,7 +33,7 @@ static int if_lock(struct cardstate *cs, int *arg) } if (!cmd && atomic_read(&cs->mstate) == MS_LOCKED - && atomic_read(&cs->connected)) { + && cs->connected) { cs->ops->set_modem_ctrl(cs, 0, TIOCM_DTR|TIOCM_RTS); cs->ops->baud_rate(cs, B115200); cs->ops->set_line_ctrl(cs, CS8); @@ -44,12 +42,12 @@ static int if_lock(struct cardstate *cs, int *arg) cs->waiting = 1; if (!gigaset_add_event(cs, &cs->at_state, EV_IF_LOCK, - NULL, cmd, NULL)) { + NULL, cmd, NULL)) { cs->waiting = 0; return -ENOMEM; } - dbg(DEBUG_CMD, "scheduling IF_LOCK"); + gig_dbg(DEBUG_CMD, "scheduling IF_LOCK"); gigaset_schedule_event(cs); wait_event(cs->waitqueue, !cs->waiting); @@ -68,7 +66,7 @@ static int if_version(struct cardstate *cs, unsigned arg[4]) static const unsigned compat[4] = GIG_COMPAT; unsigned cmd = arg[0]; - dbg(DEBUG_IF, "%u: if_version (%d)", cs->minor_index, cmd); + gig_dbg(DEBUG_IF, "%u: if_version (%d)", cs->minor_index, cmd); switch (cmd) { case GIGVER_DRIVER: @@ -80,12 +78,12 @@ static int if_version(struct cardstate *cs, unsigned arg[4]) case GIGVER_FWBASE: cs->waiting = 1; if (!gigaset_add_event(cs, &cs->at_state, EV_IF_VER, - NULL, 0, arg)) { + NULL, 0, arg)) { cs->waiting = 0; return -ENOMEM; } - dbg(DEBUG_CMD, "scheduling IF_VER"); + gig_dbg(DEBUG_CMD, "scheduling IF_VER"); gigaset_schedule_event(cs); wait_event(cs->waitqueue, !cs->waiting); @@ -101,7 +99,7 @@ static int if_version(struct cardstate *cs, unsigned arg[4]) static int if_config(struct cardstate *cs, int *arg) { - dbg(DEBUG_IF, "%u: if_config (%d)", cs->minor_index, *arg); + gig_dbg(DEBUG_IF, "%u: if_config (%d)", cs->minor_index, *arg); if (*arg != 1) return -EINVAL; @@ -109,6 +107,11 @@ static int if_config(struct cardstate *cs, int *arg) if (atomic_read(&cs->mstate) != MS_LOCKED) return -EBUSY; + if (!cs->connected) { + err("not connected!"); + return -ENODEV; + } + *arg = 0; return gigaset_enterconfigmode(cs); } @@ -119,7 +122,7 @@ static int if_config(struct cardstate *cs, int *arg) static int if_open(struct tty_struct *tty, struct file *filp); static void if_close(struct tty_struct *tty, struct file *filp); static int if_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg); + unsigned int cmd, unsigned long arg); static int if_write_room(struct tty_struct *tty); static int if_chars_in_buffer(struct tty_struct *tty); static void if_throttle(struct tty_struct *tty); @@ -127,9 +130,9 @@ static void if_unthrottle(struct tty_struct *tty); static void if_set_termios(struct tty_struct *tty, struct termios *old); static int if_tiocmget(struct tty_struct *tty, struct file *file); static int if_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear); + unsigned int set, unsigned int clear); static int if_write(struct tty_struct *tty, - const unsigned char *buf, int count); + const unsigned char *buf, int count); static struct tty_operations if_ops = { .open = if_open, @@ -153,8 +156,8 @@ static int if_open(struct tty_struct *tty, struct file *filp) struct cardstate *cs; unsigned long flags; - dbg(DEBUG_IF, "%d+%d: %s()", tty->driver->minor_start, tty->index, - __FUNCTION__); + gig_dbg(DEBUG_IF, "%d+%d: %s()", + tty->driver->minor_start, tty->index, __func__); tty->driver_data = NULL; @@ -162,7 +165,7 @@ static int if_open(struct tty_struct *tty, struct file *filp) if (!cs) return -ENODEV; - if (down_interruptible(&cs->sem)) + if (mutex_lock_interruptible(&cs->mutex)) return -ERESTARTSYS; // FIXME -EINTR? tty->driver_data = cs; @@ -173,10 +176,9 @@ static int if_open(struct tty_struct *tty, struct file *filp) cs->tty = tty; spin_unlock_irqrestore(&cs->lock, flags); tty->low_latency = 1; //FIXME test - //FIXME } - up(&cs->sem); + mutex_unlock(&cs->mutex); return 0; } @@ -187,30 +189,29 @@ static void if_close(struct tty_struct *tty, struct file *filp) cs = (struct cardstate *) tty->driver_data; if (!cs) { - err("cs==NULL in %s", __FUNCTION__); + err("cs==NULL in %s", __func__); return; } - dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__); + gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__); - down(&cs->sem); + mutex_lock(&cs->mutex); if (!cs->open_count) - warn("%s: device not opened", __FUNCTION__); + warn("%s: device not opened", __func__); else { if (!--cs->open_count) { spin_lock_irqsave(&cs->lock, flags); cs->tty = NULL; spin_unlock_irqrestore(&cs->lock, flags); - //FIXME } } - up(&cs->sem); + mutex_unlock(&cs->mutex); } static int if_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) + unsigned int cmd, unsigned long arg) { struct cardstate *cs; int retval = -ENODEV; @@ -220,17 +221,17 @@ static int if_ioctl(struct tty_struct *tty, struct file *file, cs = (struct cardstate *) tty->driver_data; if (!cs) { - err("cs==NULL in %s", __FUNCTION__); + err("cs==NULL in %s", __func__); return -ENODEV; } - dbg(DEBUG_IF, "%u: %s(0x%x)", cs->minor_index, __FUNCTION__, cmd); + gig_dbg(DEBUG_IF, "%u: %s(0x%x)", cs->minor_index, __func__, cmd); - if (down_interruptible(&cs->sem)) + if (mutex_lock_interruptible(&cs->mutex)) return -ERESTARTSYS; // FIXME -EINTR? if (!cs->open_count) - warn("%s: device not opened", __FUNCTION__); + warn("%s: device not opened", __func__); else { retval = 0; switch (cmd) { @@ -250,37 +251,40 @@ static int if_ioctl(struct tty_struct *tty, struct file *file, break; case GIGASET_BRKCHARS: //FIXME test if MS_LOCKED - gigaset_dbg_buffer(DEBUG_IF, "GIGASET_BRKCHARS", - 6, (const unsigned char *) arg, 1); - if (!atomic_read(&cs->connected)) { - dbg(DEBUG_ANY, "can't communicate with unplugged device"); + if (!cs->connected) { + gig_dbg(DEBUG_ANY, + "can't communicate with unplugged device"); retval = -ENODEV; break; } retval = copy_from_user(&buf, - (const unsigned char __user *) arg, 6) - ? -EFAULT : 0; - if (retval >= 0) + (const unsigned char __user *) arg, 6) + ? -EFAULT : 0; + if (retval >= 0) { + gigaset_dbg_buffer(DEBUG_IF, "GIGASET_BRKCHARS", + 6, (const unsigned char *) arg); retval = cs->ops->brkchars(cs, buf); + } break; case GIGASET_VERSION: - retval = copy_from_user(version, (unsigned __user *) arg, - sizeof version) ? -EFAULT : 0; + retval = copy_from_user(version, + (unsigned __user *) arg, sizeof version) + ? -EFAULT : 0; if (retval >= 0) retval = if_version(cs, version); if (retval >= 0) - retval = copy_to_user((unsigned __user *) arg, version, - sizeof version) - ? -EFAULT : 0; + retval = copy_to_user((unsigned __user *) arg, + version, sizeof version) + ? -EFAULT : 0; break; - default: - dbg(DEBUG_ANY, "%s: arg not supported - 0x%04x", - __FUNCTION__, cmd); + default: + gig_dbg(DEBUG_ANY, "%s: arg not supported - 0x%04x", + __func__, cmd); retval = -ENOIOCTLCMD; } } - up(&cs->sem); + mutex_unlock(&cs->mutex); return retval; } @@ -292,25 +296,25 @@ static int if_tiocmget(struct tty_struct *tty, struct file *file) cs = (struct cardstate *) tty->driver_data; if (!cs) { - err("cs==NULL in %s", __FUNCTION__); + err("cs==NULL in %s", __func__); return -ENODEV; } - dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__); + gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__); - if (down_interruptible(&cs->sem)) + if (mutex_lock_interruptible(&cs->mutex)) return -ERESTARTSYS; // FIXME -EINTR? // FIXME read from device? retval = cs->control_state & (TIOCM_RTS|TIOCM_DTR); - up(&cs->sem); + mutex_unlock(&cs->mutex); return retval; } static int if_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) + unsigned int set, unsigned int clear) { struct cardstate *cs; int retval; @@ -318,18 +322,18 @@ static int if_tiocmset(struct tty_struct *tty, struct file *file, cs = (struct cardstate *) tty->driver_data; if (!cs) { - err("cs==NULL in %s", __FUNCTION__); + err("cs==NULL in %s", __func__); return -ENODEV; } - dbg(DEBUG_IF, - "%u: %s(0x%x, 0x%x)", cs->minor_index, __FUNCTION__, set, clear); + gig_dbg(DEBUG_IF, "%u: %s(0x%x, 0x%x)", + cs->minor_index, __func__, set, clear); - if (down_interruptible(&cs->sem)) + if (mutex_lock_interruptible(&cs->mutex)) return -ERESTARTSYS; // FIXME -EINTR? - if (!atomic_read(&cs->connected)) { - dbg(DEBUG_ANY, "can't communicate with unplugged device"); + if (!cs->connected) { + gig_dbg(DEBUG_ANY, "can't communicate with unplugged device"); retval = -ENODEV; } else { mc = (cs->control_state | set) & ~clear & (TIOCM_RTS|TIOCM_DTR); @@ -337,7 +341,7 @@ static int if_tiocmset(struct tty_struct *tty, struct file *file, cs->control_state = mc; } - up(&cs->sem); + mutex_unlock(&cs->mutex); return retval; } @@ -349,29 +353,29 @@ static int if_write(struct tty_struct *tty, const unsigned char *buf, int count) cs = (struct cardstate *) tty->driver_data; if (!cs) { - err("cs==NULL in %s", __FUNCTION__); + err("cs==NULL in %s", __func__); return -ENODEV; } - dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__); + gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__); - if (down_interruptible(&cs->sem)) + if (mutex_lock_interruptible(&cs->mutex)) return -ERESTARTSYS; // FIXME -EINTR? if (!cs->open_count) - warn("%s: device not opened", __FUNCTION__); + warn("%s: device not opened", __func__); else if (atomic_read(&cs->mstate) != MS_LOCKED) { warn("can't write to unlocked device"); retval = -EBUSY; - } else if (!atomic_read(&cs->connected)) { - dbg(DEBUG_ANY, "can't write to unplugged device"); + } else if (!cs->connected) { + gig_dbg(DEBUG_ANY, "can't write to unplugged device"); retval = -EBUSY; //FIXME } else { retval = cs->ops->write_cmd(cs, buf, count, - &cs->if_wake_tasklet); + &cs->if_wake_tasklet); } - up(&cs->sem); + mutex_unlock(&cs->mutex); return retval; } @@ -383,27 +387,27 @@ static int if_write_room(struct tty_struct *tty) cs = (struct cardstate *) tty->driver_data; if (!cs) { - err("cs==NULL in %s", __FUNCTION__); + err("cs==NULL in %s", __func__); return -ENODEV; } - dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__); + gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__); - if (down_interruptible(&cs->sem)) + if (mutex_lock_interruptible(&cs->mutex)) return -ERESTARTSYS; // FIXME -EINTR? if (!cs->open_count) - warn("%s: device not opened", __FUNCTION__); + warn("%s: device not opened", __func__); else if (atomic_read(&cs->mstate) != MS_LOCKED) { warn("can't write to unlocked device"); retval = -EBUSY; //FIXME - } else if (!atomic_read(&cs->connected)) { - dbg(DEBUG_ANY, "can't write to unplugged device"); + } else if (!cs->connected) { + gig_dbg(DEBUG_ANY, "can't write to unplugged device"); retval = -EBUSY; //FIXME } else retval = cs->ops->write_room(cs); - up(&cs->sem); + mutex_unlock(&cs->mutex); return retval; } @@ -415,27 +419,27 @@ static int if_chars_in_buffer(struct tty_struct *tty) cs = (struct cardstate *) tty->driver_data; if (!cs) { - err("cs==NULL in %s", __FUNCTION__); + err("cs==NULL in %s", __func__); return -ENODEV; } - dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__); + gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__); - if (down_interruptible(&cs->sem)) + if (mutex_lock_interruptible(&cs->mutex)) return -ERESTARTSYS; // FIXME -EINTR? if (!cs->open_count) - warn("%s: device not opened", __FUNCTION__); + warn("%s: device not opened", __func__); else if (atomic_read(&cs->mstate) != MS_LOCKED) { warn("can't write to unlocked device"); retval = -EBUSY; - } else if (!atomic_read(&cs->connected)) { - dbg(DEBUG_ANY, "can't write to unplugged device"); + } else if (!cs->connected) { + gig_dbg(DEBUG_ANY, "can't write to unplugged device"); retval = -EBUSY; //FIXME } else retval = cs->ops->chars_in_buffer(cs); - up(&cs->sem); + mutex_unlock(&cs->mutex); return retval; } @@ -446,21 +450,21 @@ static void if_throttle(struct tty_struct *tty) cs = (struct cardstate *) tty->driver_data; if (!cs) { - err("cs==NULL in %s", __FUNCTION__); + err("cs==NULL in %s", __func__); return; } - dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__); + gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__); - down(&cs->sem); + mutex_lock(&cs->mutex); if (!cs->open_count) - warn("%s: device not opened", __FUNCTION__); + warn("%s: device not opened", __func__); else { //FIXME } - up(&cs->sem); + mutex_unlock(&cs->mutex); } static void if_unthrottle(struct tty_struct *tty) @@ -469,21 +473,21 @@ static void if_unthrottle(struct tty_struct *tty) cs = (struct cardstate *) tty->driver_data; if (!cs) { - err("cs==NULL in %s", __FUNCTION__); + err("cs==NULL in %s", __func__); return; } - dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__); + gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__); - down(&cs->sem); + mutex_lock(&cs->mutex); if (!cs->open_count) - warn("%s: device not opened", __FUNCTION__); + warn("%s: device not opened", __func__); else { //FIXME } - up(&cs->sem); + mutex_unlock(&cs->mutex); } static void if_set_termios(struct tty_struct *tty, struct termios *old) @@ -496,21 +500,21 @@ static void if_set_termios(struct tty_struct *tty, struct termios *old) cs = (struct cardstate *) tty->driver_data; if (!cs) { - err("cs==NULL in %s", __FUNCTION__); + err("cs==NULL in %s", __func__); return; } - dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__); + gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__); - down(&cs->sem); + mutex_lock(&cs->mutex); if (!cs->open_count) { - warn("%s: device not opened", __FUNCTION__); + warn("%s: device not opened", __func__); goto out; } - if (!atomic_read(&cs->connected)) { - dbg(DEBUG_ANY, "can't communicate with unplugged device"); + if (!cs->connected) { + gig_dbg(DEBUG_ANY, "can't communicate with unplugged device"); goto out; } @@ -518,8 +522,8 @@ static void if_set_termios(struct tty_struct *tty, struct termios *old) iflag = tty->termios->c_iflag; cflag = tty->termios->c_cflag; old_cflag = old ? old->c_cflag : cflag; //FIXME? - dbg(DEBUG_IF, "%u: iflag %x cflag %x old %x", cs->minor_index, - iflag, cflag, old_cflag); + gig_dbg(DEBUG_IF, "%u: iflag %x cflag %x old %x", + cs->minor_index, iflag, cflag, old_cflag); /* get a local copy of the current port settings */ control_state = cs->control_state; @@ -531,14 +535,15 @@ static void if_set_termios(struct tty_struct *tty, struct termios *old) * Premature optimization is the root of all evil. */ - /* reassert DTR and (maybe) RTS on transition from B0 */ + /* reassert DTR and (maybe) RTS on transition from B0 */ if ((old_cflag & CBAUD) == B0) { new_state = control_state | TIOCM_DTR; /* don't set RTS if using hardware flow control */ if (!(old_cflag & CRTSCTS)) new_state |= TIOCM_RTS; - dbg(DEBUG_IF, "%u: from B0 - set DTR%s", cs->minor_index, - (new_state & TIOCM_RTS) ? " only" : "/RTS"); + gig_dbg(DEBUG_IF, "%u: from B0 - set DTR%s", + cs->minor_index, + (new_state & TIOCM_RTS) ? " only" : "/RTS"); cs->ops->set_modem_ctrl(cs, control_state, new_state); control_state = new_state; } @@ -547,7 +552,7 @@ static void if_set_termios(struct tty_struct *tty, struct termios *old) if ((cflag & CBAUD) == B0) { /* Drop RTS and DTR */ - dbg(DEBUG_IF, "%u: to B0 - drop DTR/RTS", cs->minor_index); + gig_dbg(DEBUG_IF, "%u: to B0 - drop DTR/RTS", cs->minor_index); new_state = control_state & ~(TIOCM_DTR | TIOCM_RTS); cs->ops->set_modem_ctrl(cs, control_state, new_state); control_state = new_state; @@ -567,15 +572,17 @@ static void if_set_termios(struct tty_struct *tty, struct termios *old) * Just do what we have seen with SniffUSB on Win98. */ /* Drop DTR/RTS if no flow control otherwise assert */ - dbg(DEBUG_IF, "%u: control_state %x", cs->minor_index, control_state); + gig_dbg(DEBUG_IF, "%u: control_state %x", + cs->minor_index, control_state); 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) { - dbg(DEBUG_IF, "%u: new_state %x", cs->minor_index, new_state); - gigaset_set_modem_ctrl(cs, control_state, new_state); // FIXME: mct_u232.c sets the old state here. is this a bug? + gig_dbg(DEBUG_IF, "%u: new_state %x", + cs->minor_index, new_state); + gigaset_set_modem_ctrl(cs, control_state, new_state); control_state = new_state; } #endif @@ -584,7 +591,7 @@ static void if_set_termios(struct tty_struct *tty, struct termios *old) cs->control_state = control_state; out: - up(&cs->sem); + mutex_unlock(&cs->mutex); } @@ -600,7 +607,7 @@ static void if_wake(unsigned long data) if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) { - dbg(DEBUG_IF, "write wakeup call"); + gig_dbg(DEBUG_IF, "write wakeup call"); tty->ldisc.write_wakeup(tty); } @@ -635,14 +642,14 @@ void gigaset_if_free(struct cardstate *cs) } void gigaset_if_receive(struct cardstate *cs, - unsigned char *buffer, size_t len) + unsigned char *buffer, size_t len) { unsigned long flags; struct tty_struct *tty; spin_lock_irqsave(&cs->lock, flags); if ((tty = cs->tty) == NULL) - dbg(DEBUG_ANY, "receive on closed device"); + gig_dbg(DEBUG_ANY, "receive on closed device"); else { tty_buffer_request_room(tty, len); tty_insert_flip_string(tty, buffer, len); @@ -655,13 +662,13 @@ EXPORT_SYMBOL_GPL(gigaset_if_receive); /* gigaset_if_initdriver * Initialize tty interface. * parameters: - * drv Driver - * procname Name of the driver (e.g. for /proc/tty/drivers) - * devname Name of the device files (prefix without minor number) - * devfsname Devfs name of the device files without %d + * drv Driver + * procname Name of the driver (e.g. for /proc/tty/drivers) + * devname Name of the device files (prefix without minor number) + * devfsname Devfs name of the device files without %d */ void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname, - const char *devname, const char *devfsname) + const char *devname, const char *devfsname) { unsigned minors = drv->minors; int ret; @@ -696,7 +703,7 @@ void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname, warn("failed to register tty driver (error %d)", ret); goto error; } - dbg(DEBUG_IF, "tty driver initialized"); + gig_dbg(DEBUG_IF, "tty driver initialized"); drv->have_tty = 1; return; diff --git a/drivers/isdn/gigaset/isocdata.c b/drivers/isdn/gigaset/isocdata.c index 5744eb91b315..45f017ed6e8c 100644 --- a/drivers/isdn/gigaset/isocdata.c +++ b/drivers/isdn/gigaset/isocdata.c @@ -10,10 +10,6 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * ===================================================================== - * ToDo: ... - * ===================================================================== - * Version: $Id: isocdata.c,v 1.2.2.5 2005/11/13 23:05:19 hjlipp Exp $ - * ===================================================================== */ #include "gigaset.h" @@ -87,14 +83,14 @@ static inline int isowbuf_startwrite(struct isowbuf_t *iwb) { if (!atomic_dec_and_test(&iwb->writesem)) { atomic_inc(&iwb->writesem); - dbg(DEBUG_ISO, - "%s: couldn't acquire iso write semaphore", __func__); + gig_dbg(DEBUG_ISO, "%s: couldn't acquire iso write semaphore", + __func__); return 0; } #ifdef CONFIG_GIGASET_DEBUG - dbg(DEBUG_ISO, - "%s: acquired iso write semaphore, data[write]=%02x, nbits=%d", - __func__, iwb->data[atomic_read(&iwb->write)], iwb->wbits); + gig_dbg(DEBUG_ISO, + "%s: acquired iso write semaphore, data[write]=%02x, nbits=%d", + __func__, iwb->data[atomic_read(&iwb->write)], iwb->wbits); #endif return 1; } @@ -147,7 +143,7 @@ static inline void isowbuf_putflag(struct isowbuf_t *iwb) /* recover the idle flag byte */ write = atomic_read(&iwb->write); iwb->idle = iwb->data[write]; - dbg(DEBUG_ISO, "idle fill byte %02x", iwb->idle); + gig_dbg(DEBUG_ISO, "idle fill byte %02x", iwb->idle); /* mask extraneous bits in buffer */ iwb->data[write] &= (1 << iwb->wbits) - 1; } @@ -166,15 +162,14 @@ int gigaset_isowbuf_getbytes(struct isowbuf_t *iwb, int size) read = atomic_read(&iwb->nextread); write = atomic_read(&iwb->write); if (likely(read == write)) { - //dbg(DEBUG_STREAM, "%s: send buffer empty", __func__); /* return idle frame */ return read < BAS_OUTBUFPAD ? - BAS_OUTBUFSIZE : read - BAS_OUTBUFPAD; + BAS_OUTBUFSIZE : read - BAS_OUTBUFPAD; } limit = read + size; - dbg(DEBUG_STREAM, - "%s: read=%d write=%d limit=%d", __func__, read, write, limit); + gig_dbg(DEBUG_STREAM, "%s: read=%d write=%d limit=%d", + __func__, read, write, limit); #ifdef CONFIG_GIGASET_DEBUG if (unlikely(size < 0 || size > BAS_OUTBUFPAD)) { err("invalid size %d", size); @@ -196,11 +191,12 @@ int gigaset_isowbuf_getbytes(struct isowbuf_t *iwb, int size) return -EBUSY; /* write position could have changed */ if (limit >= (write = atomic_read(&iwb->write))) { - pbyte = iwb->data[write]; /* save partial byte */ + pbyte = iwb->data[write]; /* save + partial byte */ limit = write + BAS_OUTBUFPAD; - dbg(DEBUG_STREAM, - "%s: filling %d->%d with %02x", - __func__, write, limit, iwb->idle); + gig_dbg(DEBUG_STREAM, + "%s: filling %d->%d with %02x", + __func__, write, limit, iwb->idle); if (write + BAS_OUTBUFPAD < BAS_OUTBUFSIZE) memset(iwb->data + write, iwb->idle, BAS_OUTBUFPAD); @@ -211,9 +207,11 @@ int gigaset_isowbuf_getbytes(struct isowbuf_t *iwb, int size) - write); limit = 0; } - dbg(DEBUG_STREAM, "%s: restoring %02x at %d", - __func__, pbyte, limit); - iwb->data[limit] = pbyte; /* restore partial byte */ + gig_dbg(DEBUG_STREAM, + "%s: restoring %02x at %d", + __func__, pbyte, limit); + iwb->data[limit] = pbyte; /* restore + partial byte */ atomic_set(&iwb->write, limit); } isowbuf_donewrite(iwb); @@ -242,19 +240,17 @@ int gigaset_isowbuf_getbytes(struct isowbuf_t *iwb, int size) * write hex bytes to syslog for debugging */ static inline void dump_bytes(enum debuglevel level, const char *tag, - unsigned char *bytes, int count) + unsigned char *bytes, int count) { #ifdef CONFIG_GIGASET_DEBUG unsigned char c; static char dbgline[3 * 32 + 1]; static const char hexdigit[] = "0123456789abcdef"; int i = 0; - IFNULLRET(tag); - IFNULLRET(bytes); while (count-- > 0) { if (i > sizeof(dbgline) - 4) { dbgline[i] = '\0'; - dbg(level, "%s:%s", tag, dbgline); + gig_dbg(level, "%s:%s", tag, dbgline); i = 0; } c = *bytes++; @@ -264,7 +260,7 @@ static inline void dump_bytes(enum debuglevel level, const char *tag, dbgline[i++] = hexdigit[c & 0x0f]; } dbgline[i] = '\0'; - dbg(level, "%s:%s", tag, dbgline); + gig_dbg(level, "%s:%s", tag, dbgline); #endif } @@ -380,7 +376,7 @@ static u16 stufftab[5 * 256] = { */ static inline int hdlc_bitstuff_byte(struct isowbuf_t *iwb, unsigned char cin, - int ones) + int ones) { u16 stuff; int shiftinc, newones; @@ -422,7 +418,7 @@ static inline int hdlc_bitstuff_byte(struct isowbuf_t *iwb, unsigned char cin, */ static inline int hdlc_buildframe(struct isowbuf_t *iwb, - unsigned char *in, int count) + unsigned char *in, int count) { int ones; u16 fcs; @@ -431,8 +427,8 @@ static inline int hdlc_buildframe(struct isowbuf_t *iwb, if (isowbuf_freebytes(iwb) < count + count / 5 + 6 || !isowbuf_startwrite(iwb)) { - dbg(DEBUG_ISO, "%s: %d bytes free -> -EAGAIN", - __func__, isowbuf_freebytes(iwb)); + gig_dbg(DEBUG_ISO, "%s: %d bytes free -> -EAGAIN", + __func__, isowbuf_freebytes(iwb)); return -EAGAIN; } @@ -484,11 +480,11 @@ static inline int trans_buildframe(struct isowbuf_t *iwb, if (isowbuf_freebytes(iwb) < count || !isowbuf_startwrite(iwb)) { - dbg(DEBUG_ISO, "can't put %d bytes", count); + gig_dbg(DEBUG_ISO, "can't put %d bytes", count); return -EAGAIN; } - dbg(DEBUG_STREAM, "put %d bytes", count); + gig_dbg(DEBUG_STREAM, "put %d bytes", count); write = atomic_read(&iwb->write); do { c = gigaset_invtab[*in++]; @@ -508,11 +504,13 @@ int gigaset_isoc_buildframe(struct bc_state *bcs, unsigned char *in, int len) switch (bcs->proto2) { case ISDN_PROTO_L2_HDLC: result = hdlc_buildframe(bcs->hw.bas->isooutbuf, in, len); - dbg(DEBUG_ISO, "%s: %d bytes HDLC -> %d", __func__, len, result); + gig_dbg(DEBUG_ISO, "%s: %d bytes HDLC -> %d", + __func__, len, result); break; default: /* assume transparent */ result = trans_buildframe(bcs->hw.bas->isooutbuf, in, len); - dbg(DEBUG_ISO, "%s: %d bytes trans -> %d", __func__, len, result); + gig_dbg(DEBUG_ISO, "%s: %d bytes trans -> %d", + __func__, len, result); } return result; } @@ -528,13 +526,13 @@ static inline void hdlc_putbyte(unsigned char c, struct bc_state *bcs) return; } if (unlikely(bcs->skb->len == SBUFSIZE)) { - warn("received oversized packet discarded"); + dev_warn(bcs->cs->dev, "received oversized packet discarded\n"); bcs->hw.bas->giants++; dev_kfree_skb_any(bcs->skb); bcs->skb = NULL; return; } - *gigaset_skb_put_quick(bcs->skb, 1) = c; + *__skb_put(bcs->skb, 1) = c; } /* hdlc_flush @@ -549,7 +547,7 @@ static inline void hdlc_flush(struct bc_state *bcs) if ((bcs->skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL) skb_reserve(bcs->skb, HW_HDR_LEN); else - err("could not allocate skb"); + dev_err(bcs->cs->dev, "could not allocate skb\n"); } /* reset packet state */ @@ -571,23 +569,25 @@ static inline void hdlc_done(struct bc_state *bcs) if ((procskb = bcs->skb) == NULL) { /* previous error */ - dbg(DEBUG_ISO, "%s: skb=NULL", __func__); + gig_dbg(DEBUG_ISO, "%s: skb=NULL", __func__); gigaset_rcv_error(NULL, bcs->cs, bcs); } else if (procskb->len < 2) { - notice("received short frame (%d octets)", procskb->len); + dev_notice(bcs->cs->dev, "received short frame (%d octets)\n", + procskb->len); bcs->hw.bas->runts++; gigaset_rcv_error(procskb, bcs->cs, bcs); } else if (bcs->fcs != PPP_GOODFCS) { - notice("frame check error (0x%04x)", bcs->fcs); + dev_notice(bcs->cs->dev, "frame check error (0x%04x)\n", + bcs->fcs); bcs->hw.bas->fcserrs++; gigaset_rcv_error(procskb, bcs->cs, bcs); } else { procskb->len -= 2; /* subtract FCS */ procskb->tail -= 2; - dbg(DEBUG_ISO, - "%s: good frame (%d octets)", __func__, procskb->len); + gig_dbg(DEBUG_ISO, "%s: good frame (%d octets)", + __func__, procskb->len); dump_bytes(DEBUG_STREAM, - "rcv data", procskb->data, procskb->len); + "rcv data", procskb->data, procskb->len); bcs->hw.bas->goodbytes += procskb->len; gigaset_rcv_skb(procskb, bcs->cs, bcs); } @@ -595,7 +595,7 @@ static inline void hdlc_done(struct bc_state *bcs) if ((bcs->skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL) skb_reserve(bcs->skb, HW_HDR_LEN); else - err("could not allocate skb"); + dev_err(bcs->cs->dev, "could not allocate skb\n"); bcs->fcs = PPP_INITFCS; } @@ -610,14 +610,14 @@ static inline void hdlc_frag(struct bc_state *bcs, unsigned inbits) return; } - notice("received partial byte (%d bits)", inbits); + dev_notice(bcs->cs->dev, "received partial byte (%d bits)\n", inbits); bcs->hw.bas->alignerrs++; gigaset_rcv_error(bcs->skb, bcs->cs, bcs); if ((bcs->skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL) skb_reserve(bcs->skb, HW_HDR_LEN); else - err("could not allocate skb"); + dev_err(bcs->cs->dev, "could not allocate skb\n"); bcs->fcs = PPP_INITFCS; } @@ -659,16 +659,12 @@ static unsigned char bitcounts[256] = { * bcs receiving B channel structure */ static inline void hdlc_unpack(unsigned char *src, unsigned count, - struct bc_state *bcs) + struct bc_state *bcs) { - struct bas_bc_state *ubc; + struct bas_bc_state *ubc = bcs->hw.bas; int inputstate; unsigned seqlen, inbyte, inbits; - IFNULLRET(bcs); - ubc = bcs->hw.bas; - IFNULLRET(ubc); - /* load previous state: * inputstate = set of flag bits: * - INS_flag_hunt: no complete opening flag received since connection setup or last abort @@ -856,7 +852,7 @@ static inline void hdlc_unpack(unsigned char *src, unsigned count, * bcs receiving B channel structure */ static inline void trans_receive(unsigned char *src, unsigned count, - struct bc_state *bcs) + struct bc_state *bcs) { struct sk_buff *skb; int dobytes; @@ -870,7 +866,7 @@ static inline void trans_receive(unsigned char *src, unsigned count, if (unlikely((skb = bcs->skb) == NULL)) { bcs->skb = skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN); if (!skb) { - err("could not allocate skb"); + dev_err(bcs->cs->dev, "could not allocate skb\n"); return; } skb_reserve(skb, HW_HDR_LEN); @@ -888,7 +884,8 @@ static inline void trans_receive(unsigned char *src, unsigned count, gigaset_rcv_skb(skb, bcs->cs, bcs); bcs->skb = skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN); if (!skb) { - err("could not allocate skb"); + dev_err(bcs->cs->dev, + "could not allocate skb\n"); return; } skb_reserve(bcs->skb, HW_HDR_LEN); @@ -921,8 +918,8 @@ static void cmd_loop(unsigned char *src, int numbytes, struct inbuf_t *inbuf) case '\r': case '\n': /* end of line */ - dbg(DEBUG_TRANSCMD, "%s: End of Command (%d Bytes)", - __func__, cbytes); + gig_dbg(DEBUG_TRANSCMD, "%s: End of Command (%d Bytes)", + __func__, cbytes); cs->cbytes = cbytes; gigaset_handle_modem_response(cs); cbytes = 0; @@ -932,7 +929,7 @@ static void cmd_loop(unsigned char *src, int numbytes, struct inbuf_t *inbuf) if (cbytes < MAX_RESP_SIZE - 1) cbytes++; else - warn("response too large"); + dev_warn(cs->dev, "response too large\n"); } } @@ -951,27 +948,27 @@ void gigaset_isoc_input(struct inbuf_t *inbuf) head = atomic_read(&inbuf->head); while (head != (tail = atomic_read(&inbuf->tail))) { - dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail); + gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail); if (head > tail) tail = RBUFSIZE; src = inbuf->data + head; numbytes = tail - head; - dbg(DEBUG_INTR, "processing %u bytes", numbytes); + gig_dbg(DEBUG_INTR, "processing %u bytes", numbytes); if (atomic_read(&cs->mstate) == MS_LOCKED) { gigaset_dbg_buffer(DEBUG_LOCKCMD, "received response", - numbytes, src, 0); + numbytes, src); gigaset_if_receive(inbuf->cs, src, numbytes); } else { gigaset_dbg_buffer(DEBUG_CMD, "received response", - numbytes, src, 0); + numbytes, src); cmd_loop(src, numbytes, inbuf); } head += numbytes; if (head == RBUFSIZE) head = 0; - dbg(DEBUG_INTR, "setting head to %u", head); + gig_dbg(DEBUG_INTR, "setting head to %u", head); atomic_set(&inbuf->head, head); } } @@ -992,18 +989,18 @@ void gigaset_isoc_input(struct inbuf_t *inbuf) */ int gigaset_isoc_send_skb(struct bc_state *bcs, struct sk_buff *skb) { - int len; - - IFNULLRETVAL(bcs, -EFAULT); - IFNULLRETVAL(skb, -EFAULT); - len = skb->len; + int len = skb->len; + unsigned long flags; skb_queue_tail(&bcs->squeue, skb); - dbg(DEBUG_ISO, - "%s: skb queued, qlen=%d", __func__, skb_queue_len(&bcs->squeue)); + gig_dbg(DEBUG_ISO, "%s: skb queued, qlen=%d", + __func__, skb_queue_len(&bcs->squeue)); /* tasklet submits URB if necessary */ - tasklet_schedule(&bcs->hw.bas->sent_tasklet); + spin_lock_irqsave(&bcs->cs->lock, flags); + if (bcs->cs->connected) + tasklet_schedule(&bcs->hw.bas->sent_tasklet); + spin_unlock_irqrestore(&bcs->cs->lock, flags); return len; /* ok so far */ } diff --git a/drivers/isdn/gigaset/proc.c b/drivers/isdn/gigaset/proc.c index c6915fa2be6c..d267a636b53c 100644 --- a/drivers/isdn/gigaset/proc.c +++ b/drivers/isdn/gigaset/proc.c @@ -1,7 +1,7 @@ /* * Stuff used by all variants of the driver * - * Copyright (c) 2001 by Stefan Eilers , + * Copyright (c) 2001 by Stefan Eilers, * Hansjoerg Lipp , * Tilman Schmidt . * @@ -11,26 +11,29 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * ===================================================================== - * ToDo: ... - * ===================================================================== - * Version: $Id: proc.c,v 1.5.2.13 2006/02/04 18:28:16 hjlipp Exp $ - * ===================================================================== */ #include "gigaset.h" #include -static ssize_t show_cidmode(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t show_cidmode(struct device *dev, struct device_attribute *attr, + char *buf) { - struct usb_interface *intf = to_usb_interface(dev); - struct cardstate *cs = usb_get_intfdata(intf); - return sprintf(buf, "%d\n", atomic_read(&cs->cidmode)); // FIXME use scnprintf for 13607 bit architectures (if PAGE_SIZE==4096) + int ret; + unsigned long flags; + struct cardstate *cs = dev_get_drvdata(dev); + + spin_lock_irqsave(&cs->lock, flags); + ret = sprintf(buf, "%u\n", cs->cidmode); + spin_unlock_irqrestore(&cs->lock, flags); + + return ret; } -static ssize_t set_cidmode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t set_cidmode(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { - struct usb_interface *intf = to_usb_interface(dev); - struct cardstate *cs = usb_get_intfdata(intf); + struct cardstate *cs = dev_get_drvdata(dev); long int value; char *end; @@ -41,23 +44,23 @@ static ssize_t set_cidmode(struct device *dev, struct device_attribute *attr, co if (value < 0 || value > 1) return -EINVAL; - if (down_interruptible(&cs->sem)) + if (mutex_lock_interruptible(&cs->mutex)) return -ERESTARTSYS; // FIXME -EINTR? cs->waiting = 1; if (!gigaset_add_event(cs, &cs->at_state, EV_PROC_CIDMODE, - NULL, value, NULL)) { + NULL, value, NULL)) { cs->waiting = 0; - up(&cs->sem); + mutex_unlock(&cs->mutex); return -ENOMEM; } - dbg(DEBUG_CMD, "scheduling PROC_CIDMODE"); + gig_dbg(DEBUG_CMD, "scheduling PROC_CIDMODE"); gigaset_schedule_event(cs); wait_event(cs->waitqueue, !cs->waiting); - up(&cs->sem); + mutex_unlock(&cs->mutex); return count; } @@ -65,17 +68,15 @@ static ssize_t set_cidmode(struct device *dev, struct device_attribute *attr, co static DEVICE_ATTR(cidmode, S_IRUGO|S_IWUSR, show_cidmode, set_cidmode); /* free sysfs for device */ -void gigaset_free_dev_sysfs(struct usb_interface *interface) +void gigaset_free_dev_sysfs(struct cardstate *cs) { - dbg(DEBUG_INIT, "removing sysfs entries"); - device_remove_file(&interface->dev, &dev_attr_cidmode); + gig_dbg(DEBUG_INIT, "removing sysfs entries"); + device_remove_file(cs->dev, &dev_attr_cidmode); } -EXPORT_SYMBOL_GPL(gigaset_free_dev_sysfs); /* initialize sysfs for device */ -void gigaset_init_dev_sysfs(struct usb_interface *interface) +void gigaset_init_dev_sysfs(struct cardstate *cs) { - dbg(DEBUG_INIT, "setting up sysfs"); - device_create_file(&interface->dev, &dev_attr_cidmode); + gig_dbg(DEBUG_INIT, "setting up sysfs"); + device_create_file(cs->dev, &dev_attr_cidmode); } -EXPORT_SYMBOL_GPL(gigaset_init_dev_sysfs); diff --git a/drivers/isdn/gigaset/usb-gigaset.c b/drivers/isdn/gigaset/usb-gigaset.c index 323fc7349dec..bfb73fd5077e 100644 --- a/drivers/isdn/gigaset/usb-gigaset.c +++ b/drivers/isdn/gigaset/usb-gigaset.c @@ -1,7 +1,7 @@ /* * USB driver for Gigaset 307x directly or using M105 Data. * - * Copyright (c) 2001 by Stefan Eilers + * Copyright (c) 2001 by Stefan Eilers * and Hansjoerg Lipp . * * This driver was derived from the USB skeleton driver by @@ -13,10 +13,6 @@ * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * ===================================================================== - * ToDo: ... - * ===================================================================== - * Version: $Id: usb-gigaset.c,v 1.85.4.18 2006/02/04 18:28:16 hjlipp Exp $ - * ===================================================================== */ #include "gigaset.h" @@ -29,7 +25,7 @@ #include /* Version Information */ -#define DRIVER_AUTHOR "Hansjoerg Lipp , Stefan Eilers " +#define DRIVER_AUTHOR "Hansjoerg Lipp , Stefan Eilers" #define DRIVER_DESC "USB Driver for Gigaset 307x using M105" /* Module parameters */ @@ -62,10 +58,6 @@ static struct usb_device_id gigaset_table [] = { MODULE_DEVICE_TABLE(usb, gigaset_table); -/* Get a minor range for your devices from the usb maintainer */ -#define USB_SKEL_MINOR_BASE 200 - - /* * Control requests (empty fields: 00) * @@ -114,7 +106,7 @@ MODULE_DEVICE_TABLE(usb, gigaset_table); */ static int gigaset_probe(struct usb_interface *interface, - const struct usb_device_id *id); + const struct usb_device_id *id); static void gigaset_disconnect(struct usb_interface *interface); static struct gigaset_driver *driver = NULL; @@ -122,29 +114,29 @@ static struct cardstate *cardstate = NULL; /* usb specific object needed to register this driver with the usb subsystem */ static struct usb_driver gigaset_usb_driver = { - .name = GIGASET_MODULENAME, - .probe = gigaset_probe, - .disconnect = gigaset_disconnect, - .id_table = gigaset_table, + .name = GIGASET_MODULENAME, + .probe = gigaset_probe, + .disconnect = gigaset_disconnect, + .id_table = gigaset_table, }; struct usb_cardstate { - struct usb_device *udev; /* save off the usb device pointer */ - struct usb_interface *interface; /* the interface for this device */ - atomic_t busy; /* bulk output in progress */ + struct usb_device *udev; /* usb device pointer */ + struct usb_interface *interface; /* interface for this device */ + atomic_t busy; /* bulk output in progress */ - /* Output buffer for commands (M105: and data)*/ - unsigned char *bulk_out_buffer; /* the buffer to send data */ - int bulk_out_size; /* the size of the send buffer */ - __u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */ - struct urb *bulk_out_urb; /* the urb used to transmit data */ + /* Output buffer */ + unsigned char *bulk_out_buffer; + int bulk_out_size; + __u8 bulk_out_endpointAddr; + struct urb *bulk_out_urb; - /* Input buffer for command responses (M105: and data)*/ - int rcvbuf_size; /* the size of the receive buffer */ - struct urb *read_urb; /* the urb used to receive data */ - __u8 int_in_endpointAddr; /* the address of the bulk in endpoint */ + /* Input buffer */ + int rcvbuf_size; + struct urb *read_urb; + __u8 int_in_endpointAddr; - char bchars[6]; /* req. 0x19 */ + char bchars[6]; /* for request 0x19 */ }; struct usb_bc_state {}; @@ -157,19 +149,20 @@ static inline unsigned tiocm_to_gigaset(unsigned state) #ifdef CONFIG_GIGASET_UNDOCREQ /* WARNING: EXPERIMENTAL! */ static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state, - unsigned new_state) + unsigned new_state) { + struct usb_device *udev = cs->hw.usb->udev; unsigned mask, val; int r; mask = tiocm_to_gigaset(old_state ^ new_state); val = tiocm_to_gigaset(new_state); - dbg(DEBUG_USBREQ, "set flags 0x%02x with mask 0x%02x", val, mask); - r = usb_control_msg(cs->hw.usb->udev, - usb_sndctrlpipe(cs->hw.usb->udev, 0), 7, 0x41, - (val & 0xff) | ((mask & 0xff) << 8), 0, - NULL, 0, 2000 /*timeout??*/); // don't use this in an interrupt/BH + gig_dbg(DEBUG_USBREQ, "set flags 0x%02x with mask 0x%02x", val, mask); + // don't use this in an interrupt/BH + r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 7, 0x41, + (val & 0xff) | ((mask & 0xff) << 8), 0, + NULL, 0, 2000 /* timeout? */); if (r < 0) return r; //.. @@ -178,30 +171,29 @@ static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state, static int set_value(struct cardstate *cs, u8 req, u16 val) { + struct usb_device *udev = cs->hw.usb->udev; int r, r2; - dbg(DEBUG_USBREQ, "request %02x (%04x)", (unsigned)req, (unsigned)val); - r = usb_control_msg(cs->hw.usb->udev, - usb_sndctrlpipe(cs->hw.usb->udev, 0), 0x12, 0x41, - 0xf /*?*/, 0, - NULL, 0, 2000 /*?*/); /* no idea, what this does */ + gig_dbg(DEBUG_USBREQ, "request %02x (%04x)", + (unsigned)req, (unsigned)val); + r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x12, 0x41, + 0xf /*?*/, 0, NULL, 0, 2000 /*?*/); + /* no idea what this does */ if (r < 0) { - err("error %d on request 0x12", -r); + dev_err(&udev->dev, "error %d on request 0x12\n", -r); return r; } - r = usb_control_msg(cs->hw.usb->udev, - usb_sndctrlpipe(cs->hw.usb->udev, 0), req, 0x41, - val, 0, - NULL, 0, 2000 /*?*/); + r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), req, 0x41, + val, 0, NULL, 0, 2000 /*?*/); if (r < 0) - err("error %d on request 0x%02x", -r, (unsigned)req); + dev_err(&udev->dev, "error %d on request 0x%02x\n", + -r, (unsigned)req); - r2 = usb_control_msg(cs->hw.usb->udev, - usb_sndctrlpipe(cs->hw.usb->udev, 0), 0x19, 0x41, - 0, 0, cs->hw.usb->bchars, 6, 2000 /*?*/); + r2 = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x19, 0x41, + 0, 0, cs->hw.usb->bchars, 6, 2000 /*?*/); if (r2 < 0) - err("error %d on request 0x19", -r2); + dev_err(&udev->dev, "error %d on request 0x19\n", -r2); return r < 0 ? r : (r2 < 0 ? r2 : 0); } @@ -229,8 +221,8 @@ static int gigaset_baud_rate(struct cardstate *cs, unsigned cflag) case B115200: rate = 115200; break; default: rate = 9600; - err("unsupported baudrate request 0x%x," - " using default of B9600", cflag); + dev_err(cs->dev, "unsupported baudrate request 0x%x," + " using default of B9600\n", cflag); } val = 0x383fff / rate + 1; @@ -259,7 +251,7 @@ static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag) case CS8: val |= 8 << 8; break; default: - err("CSIZE was not CS5-CS8, using default of 8"); + dev_err(cs->dev, "CSIZE was not CS5-CS8, using default of 8\n"); val |= 8 << 8; break; } @@ -277,7 +269,7 @@ static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag) #else static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state, - unsigned new_state) + unsigned new_state) { return -EINVAL; } @@ -309,15 +301,12 @@ static int gigaset_close_bchannel(struct bc_state *bcs) return 0; } -//void send_ack_to_LL(void *data); static int write_modem(struct cardstate *cs); static int send_cb(struct cardstate *cs, struct cmdbuf_t *cb); -/* Handling of send queue. If there is already a skb opened, put data to - * the transfer buffer by calling "write_modem". Otherwise take a new skb out of the queue. - * This function will be called by the ISR via "transmit_chars" (USB: B-Channel Bulk callback handler - * via immediate task queue) or by writebuf_from_LL if the LL wants to transmit data. +/* Write tasklet handler: Continue sending current skb, or send command, or + * start sending an skb from the send queue. */ static void gigaset_modem_fill(unsigned long data) { @@ -327,10 +316,10 @@ static void gigaset_modem_fill(unsigned long data) unsigned long flags; int again; - dbg(DEBUG_OUTPUT, "modem_fill"); + gig_dbg(DEBUG_OUTPUT, "modem_fill"); if (atomic_read(&cs->hw.usb->busy)) { - dbg(DEBUG_OUTPUT, "modem_fill: busy"); + gig_dbg(DEBUG_OUTPUT, "modem_fill: busy"); return; } @@ -341,26 +330,27 @@ static void gigaset_modem_fill(unsigned long data) cb = cs->cmdbuf; spin_unlock_irqrestore(&cs->cmdlock, flags); if (cb) { /* commands to send? */ - dbg(DEBUG_OUTPUT, "modem_fill: cb"); + gig_dbg(DEBUG_OUTPUT, "modem_fill: cb"); if (send_cb(cs, cb) < 0) { - dbg(DEBUG_OUTPUT, - "modem_fill: send_cb failed"); - again = 1; /* no callback will be called! */ + gig_dbg(DEBUG_OUTPUT, + "modem_fill: send_cb failed"); + again = 1; /* no callback will be + called! */ } } else { /* skbs to send? */ bcs->tx_skb = skb_dequeue(&bcs->squeue); if (bcs->tx_skb) - dbg(DEBUG_INTR, - "Dequeued skb (Adr: %lx)!", - (unsigned long) bcs->tx_skb); + gig_dbg(DEBUG_INTR, + "Dequeued skb (Adr: %lx)!", + (unsigned long) bcs->tx_skb); } } if (bcs->tx_skb) { - dbg(DEBUG_OUTPUT, "modem_fill: tx_skb"); + gig_dbg(DEBUG_OUTPUT, "modem_fill: tx_skb"); if (write_modem(cs) < 0) { - dbg(DEBUG_OUTPUT, - "modem_fill: write_modem failed"); + gig_dbg(DEBUG_OUTPUT, + "modem_fill: write_modem failed"); // FIXME should we tell the LL? again = 1; /* no callback will be called! */ } @@ -371,88 +361,85 @@ static void gigaset_modem_fill(unsigned long data) /** * gigaset_read_int_callback * - * It is called if the data was received from the device. This is almost similiar to - * the interrupt service routine in the serial device. + * It is called if the data was received from the device. */ static void gigaset_read_int_callback(struct urb *urb, struct pt_regs *regs) { + struct inbuf_t *inbuf = urb->context; + struct cardstate *cs = inbuf->cs; int resubmit = 0; int r; - struct cardstate *cs; unsigned numbytes; unsigned char *src; - //unsigned long flags; - struct inbuf_t *inbuf; - - IFNULLRET(urb); - inbuf = (struct inbuf_t *) urb->context; - IFNULLRET(inbuf); - //spin_lock_irqsave(&inbuf->lock, flags); - cs = inbuf->cs; - IFNULLGOTO(cs, exit); - IFNULLGOTO(cardstate, exit); - - if (!atomic_read(&cs->connected)) { - err("%s: disconnected", __func__); - goto exit; - } + unsigned long flags; if (!urb->status) { + if (!cs->connected) { + err("%s: disconnected", __func__); /* should never happen */ + return; + } + numbytes = urb->actual_length; if (numbytes) { src = inbuf->rcvbuf; if (unlikely(*src)) - warn("%s: There was no leading 0, but 0x%02x!", - __func__, (unsigned) *src); + dev_warn(cs->dev, + "%s: There was no leading 0, but 0x%02x!\n", + __func__, (unsigned) *src); ++src; /* skip leading 0x00 */ --numbytes; if (gigaset_fill_inbuf(inbuf, src, numbytes)) { - dbg(DEBUG_INTR, "%s-->BH", __func__); + gig_dbg(DEBUG_INTR, "%s-->BH", __func__); gigaset_schedule_event(inbuf->cs); } } else - dbg(DEBUG_INTR, "Received zero block length"); + gig_dbg(DEBUG_INTR, "Received zero block length"); resubmit = 1; } else { /* The urb might have been killed. */ - dbg(DEBUG_ANY, "%s - nonzero read bulk status received: %d", - __func__, urb->status); - if (urb->status != -ENOENT) /* not killed */ + gig_dbg(DEBUG_ANY, "%s - nonzero read bulk status received: %d", + __func__, urb->status); + if (urb->status != -ENOENT) { /* not killed */ + if (!cs->connected) { + err("%s: disconnected", __func__); /* should never happen */ + return; + } resubmit = 1; + } } -exit: - //spin_unlock_irqrestore(&inbuf->lock, flags); + if (resubmit) { - r = usb_submit_urb(urb, SLAB_ATOMIC); + spin_lock_irqsave(&cs->lock, flags); + r = cs->connected ? usb_submit_urb(urb, SLAB_ATOMIC) : -ENODEV; + spin_unlock_irqrestore(&cs->lock, flags); if (r) - err("error %d when resubmitting urb.", -r); + dev_err(cs->dev, "error %d when resubmitting urb.\n", + -r); } } -/* This callback routine is called when data was transmitted to a B-Channel. - * Therefore it has to check if there is still data to transmit. This - * happens by calling modem_fill via task queue. - * - */ +/* This callback routine is called when data was transmitted to the device. */ static void gigaset_write_bulk_callback(struct urb *urb, struct pt_regs *regs) { - struct cardstate *cs = (struct cardstate *) urb->context; + struct cardstate *cs = urb->context; + unsigned long flags; - IFNULLRET(cs); -#ifdef CONFIG_GIGASET_DEBUG - if (!atomic_read(&cs->connected)) { - err("%s:not connected", __func__); - return; - } -#endif if (urb->status) - err("bulk transfer failed (status %d)", -urb->status); /* That's all we can do. Communication problems - are handeled by timeouts or network protocols */ + dev_err(cs->dev, "bulk transfer failed (status %d)\n", + -urb->status); + /* That's all we can do. Communication problems + are handled by timeouts or network protocols. */ - atomic_set(&cs->hw.usb->busy, 0); - tasklet_schedule(&cs->write_tasklet); + spin_lock_irqsave(&cs->lock, flags); + if (!cs->connected) { + err("%s: not connected", __func__); + } else { + atomic_set(&cs->hw.usb->busy, 0); + tasklet_schedule(&cs->write_tasklet); + } + spin_unlock_irqrestore(&cs->lock, flags); } static int send_cb(struct cardstate *cs, struct cmdbuf_t *cb) @@ -469,8 +456,8 @@ static int send_cb(struct cardstate *cs, struct cmdbuf_t *cb) spin_lock_irqsave(&cs->cmdlock, flags); cs->cmdbytes -= cs->curlen; - dbg(DEBUG_OUTPUT, "send_cb: sent %u bytes, %u left", - cs->curlen, cs->cmdbytes); + gig_dbg(DEBUG_OUTPUT, "send_cb: sent %u bytes, %u left", + cs->curlen, cs->cmdbytes); cs->cmdbuf = cb = cb->next; if (cb) { cb->prev = NULL; @@ -487,52 +474,51 @@ static int send_cb(struct cardstate *cs, struct cmdbuf_t *cb) } if (cb) { count = min(cb->len, ucs->bulk_out_size); + gig_dbg(DEBUG_OUTPUT, "send_cb: send %d bytes", count); + usb_fill_bulk_urb(ucs->bulk_out_urb, ucs->udev, - usb_sndbulkpipe(ucs->udev, - ucs->bulk_out_endpointAddr & 0x0f), - cb->buf + cb->offset, count, - gigaset_write_bulk_callback, cs); + usb_sndbulkpipe(ucs->udev, + ucs->bulk_out_endpointAddr & 0x0f), + cb->buf + cb->offset, count, + gigaset_write_bulk_callback, cs); cb->offset += count; cb->len -= count; atomic_set(&ucs->busy, 1); - dbg(DEBUG_OUTPUT, "send_cb: send %d bytes", count); - status = usb_submit_urb(ucs->bulk_out_urb, SLAB_ATOMIC); + spin_lock_irqsave(&cs->lock, flags); + status = cs->connected ? usb_submit_urb(ucs->bulk_out_urb, SLAB_ATOMIC) : -ENODEV; + spin_unlock_irqrestore(&cs->lock, flags); + if (status) { atomic_set(&ucs->busy, 0); - err("could not submit urb (error %d).", + err("could not submit urb (error %d)\n", -status); - cb->len = 0; /* skip urb => remove cb+wakeup in next loop cycle */ + cb->len = 0; /* skip urb => remove cb+wakeup + in next loop cycle */ } } - } while (cb && status); /* bei Fehler naechster Befehl //FIXME: ist das OK? */ + } while (cb && status); /* next command on error */ return status; } -/* Write string into transbuf and send it to modem. - */ +/* Send command to device. */ static int gigaset_write_cmd(struct cardstate *cs, const unsigned char *buf, - int len, struct tasklet_struct *wake_tasklet) + int len, struct tasklet_struct *wake_tasklet) { struct cmdbuf_t *cb; unsigned long flags; gigaset_dbg_buffer(atomic_read(&cs->mstate) != MS_LOCKED ? - DEBUG_TRANSCMD : DEBUG_LOCKCMD, - "CMD Transmit", len, buf, 0); - - if (!atomic_read(&cs->connected)) { - err("%s: not connected", __func__); - return -ENODEV; - } + DEBUG_TRANSCMD : DEBUG_LOCKCMD, + "CMD Transmit", len, buf); if (len <= 0) return 0; if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) { - err("%s: out of memory", __func__); + dev_err(cs->dev, "%s: out of memory\n", __func__); return -ENOMEM; } @@ -554,7 +540,10 @@ static int gigaset_write_cmd(struct cardstate *cs, const unsigned char *buf, cs->lastcmdbuf = cb; spin_unlock_irqrestore(&cs->cmdlock, flags); - tasklet_schedule(&cs->write_tasklet); + spin_lock_irqsave(&cs->lock, flags); + if (cs->connected) + tasklet_schedule(&cs->write_tasklet); + spin_unlock_irqrestore(&cs->lock, flags); return len; } @@ -578,11 +567,12 @@ static int gigaset_chars_in_buffer(struct cardstate *cs) static int gigaset_brkchars(struct cardstate *cs, const unsigned char buf[6]) { #ifdef CONFIG_GIGASET_UNDOCREQ - gigaset_dbg_buffer(DEBUG_USBREQ, "brkchars", 6, buf, 0); + struct usb_device *udev = cs->hw.usb->udev; + + gigaset_dbg_buffer(DEBUG_USBREQ, "brkchars", 6, buf); memcpy(cs->hw.usb->bchars, buf, 6); - return usb_control_msg(cs->hw.usb->udev, - usb_sndctrlpipe(cs->hw.usb->udev, 0), 0x19, 0x41, - 0, 0, &buf, 6, 2000); + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x19, 0x41, + 0, 0, &buf, 6, 2000); #else return -EINVAL; #endif @@ -604,7 +594,6 @@ static int gigaset_initbcshw(struct bc_state *bcs) if (!bcs->hw.usb) return 0; - //bcs->hw.usb->trans_flg = READY_TO_TRNSMIT; /* B-Channel ready to transmit */ return 1; } @@ -614,7 +603,6 @@ static void gigaset_reinitbcshw(struct bc_state *bcs) static void gigaset_freecshw(struct cardstate *cs) { - //FIXME tasklet_kill(&cs->write_tasklet); kfree(cs->hw.usb); } @@ -639,33 +627,21 @@ static int gigaset_initcshw(struct cardstate *cs) //ucs->urb_cmd_out = NULL; ucs->read_urb = NULL; tasklet_init(&cs->write_tasklet, - &gigaset_modem_fill, (unsigned long) cs); + &gigaset_modem_fill, (unsigned long) cs); return 1; } -/* Writes the data of the current open skb into the modem. - * We have to protect against multiple calls until the - * callback handler () is called , due to the fact that we - * are just allowed to send data once to an endpoint. Therefore - * we using "trans_flg" to synchonize ... - */ +/* Send data from current skb to the device. */ static int write_modem(struct cardstate *cs) { - int ret; + int ret = 0; int count; struct bc_state *bcs = &cs->bcs[0]; /* only one channel */ struct usb_cardstate *ucs = cs->hw.usb; - //unsigned long flags; + unsigned long flags; - IFNULLRETVAL(bcs->tx_skb, -EINVAL); - - dbg(DEBUG_WRITE, "len: %d...", bcs->tx_skb->len); - - ret = -ENODEV; - IFNULLGOTO(ucs->bulk_out_buffer, error); - IFNULLGOTO(ucs->bulk_out_urb, error); - ret = 0; + gig_dbg(DEBUG_WRITE, "len: %d...", bcs->tx_skb->len); if (!bcs->tx_skb->len) { dev_kfree_skb_any(bcs->tx_skb); @@ -679,40 +655,42 @@ static int write_modem(struct cardstate *cs) count = min(bcs->tx_skb->len, (unsigned) ucs->bulk_out_size); memcpy(ucs->bulk_out_buffer, bcs->tx_skb->data, count); skb_pull(bcs->tx_skb, count); - - usb_fill_bulk_urb(ucs->bulk_out_urb, ucs->udev, - usb_sndbulkpipe(ucs->udev, - ucs->bulk_out_endpointAddr & 0x0f), - ucs->bulk_out_buffer, count, - gigaset_write_bulk_callback, cs); atomic_set(&ucs->busy, 1); - dbg(DEBUG_OUTPUT, "write_modem: send %d bytes", count); + gig_dbg(DEBUG_OUTPUT, "write_modem: send %d bytes", count); + + spin_lock_irqsave(&cs->lock, flags); + if (cs->connected) { + usb_fill_bulk_urb(ucs->bulk_out_urb, ucs->udev, + usb_sndbulkpipe(ucs->udev, + ucs->bulk_out_endpointAddr & 0x0f), + ucs->bulk_out_buffer, count, + gigaset_write_bulk_callback, cs); + ret = usb_submit_urb(ucs->bulk_out_urb, SLAB_ATOMIC); + } else { + ret = -ENODEV; + } + spin_unlock_irqrestore(&cs->lock, flags); - ret = usb_submit_urb(ucs->bulk_out_urb, SLAB_ATOMIC); if (ret) { - err("could not submit urb (error %d).", -ret); + err("could not submit urb (error %d)\n", -ret); atomic_set(&ucs->busy, 0); } + if (!bcs->tx_skb->len) { /* skb sent completely */ gigaset_skb_sent(bcs, bcs->tx_skb); //FIXME also, when ret<0? - dbg(DEBUG_INTR, - "kfree skb (Adr: %lx)!", (unsigned long) bcs->tx_skb); + gig_dbg(DEBUG_INTR, "kfree skb (Adr: %lx)!", + (unsigned long) bcs->tx_skb); dev_kfree_skb_any(bcs->tx_skb); bcs->tx_skb = NULL; } return ret; -error: - dev_kfree_skb_any(bcs->tx_skb); - bcs->tx_skb = NULL; - return ret; - } static int gigaset_probe(struct usb_interface *interface, - const struct usb_device_id *id) + const struct usb_device_id *id) { int retval; struct usb_device *udev = interface_to_usbdev(interface); @@ -720,16 +698,14 @@ static int gigaset_probe(struct usb_interface *interface, struct usb_host_interface *hostif; struct cardstate *cs = NULL; struct usb_cardstate *ucs = NULL; - //struct usb_interface_descriptor *iface_desc; struct usb_endpoint_descriptor *endpoint; - //isdn_ctrl command; int buffer_size; int alt; - //unsigned long flags; - info("%s: Check if device matches .. (Vendor: 0x%x, Product: 0x%x)", - __func__, le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct)); + gig_dbg(DEBUG_ANY, + "%s: Check if device matches .. (Vendor: 0x%x, Product: 0x%x)", + __func__, le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); retval = -ENODEV; //FIXME @@ -744,7 +720,7 @@ static int gigaset_probe(struct usb_interface *interface, ifnum = hostif->desc.bInterfaceNumber; // FIXME ? if (alt != 0 || ifnum != 0) { - warn("ifnum %d, alt %d", ifnum, alt); + dev_warn(&udev->dev, "ifnum %d, alt %d\n", ifnum, alt); return -ENODEV; } @@ -752,42 +728,29 @@ static int gigaset_probe(struct usb_interface *interface, * */ if (hostif->desc.bInterfaceClass != 255) { - info("%s: Device matched, but iface_desc[%d]->bInterfaceClass==%d !", - __func__, ifnum, hostif->desc.bInterfaceClass); + dev_info(&udev->dev, + "%s: Device matched but iface_desc[%d]->bInterfaceClass==%d!\n", + __func__, ifnum, hostif->desc.bInterfaceClass); return -ENODEV; } - info("%s: Device matched ... !", __func__); + dev_info(&udev->dev, "%s: Device matched ... !\n", __func__); cs = gigaset_getunassignedcs(driver); if (!cs) { - warn("No free cardstate!"); + dev_warn(&udev->dev, "no free cardstate\n"); return -ENODEV; } ucs = cs->hw.usb; -#if 0 - if (usb_set_configuration(udev, udev->config[0].desc.bConfigurationValue) < 0) { - warn("set_configuration failed"); - goto error; - } + /* save off device structure ptrs for later use */ + usb_get_dev(udev); + ucs->udev = udev; + ucs->interface = interface; + cs->dev = &interface->dev; - - if (usb_set_interface(udev, ifnum/*==0*/, alt/*==0*/) < 0) { - warn("usb_set_interface failed, device %d interface %d altsetting %d", - udev->devnum, ifnum, alt); - goto error; - } -#endif - - /* set up the endpoint information */ - /* check out the endpoints */ - /* We will get 2 endpoints: One for sending commands to the device (bulk out) and one to - * poll messages from the device(int in). - * Therefore we will have an almost similiar situation as with our serial port handler. - * If an connection will be established, we will have to create data in/out pipes - * dynamically... - */ + /* save address of controller structure */ + usb_set_intfdata(interface, cs); // dev_set_drvdata(&interface->dev, cs); endpoint = &hostif->endpoint[0].desc; @@ -796,14 +759,14 @@ static int gigaset_probe(struct usb_interface *interface, ucs->bulk_out_endpointAddr = endpoint->bEndpointAddress; ucs->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL); if (!ucs->bulk_out_buffer) { - err("Couldn't allocate bulk_out_buffer"); + dev_err(cs->dev, "Couldn't allocate bulk_out_buffer\n"); retval = -ENOMEM; goto error; } ucs->bulk_out_urb = usb_alloc_urb(0, SLAB_KERNEL); if (!ucs->bulk_out_urb) { - err("Couldn't allocate bulk_out_buffer"); + dev_err(cs->dev, "Couldn't allocate bulk_out_urb\n"); retval = -ENOMEM; goto error; } @@ -811,12 +774,10 @@ static int gigaset_probe(struct usb_interface *interface, endpoint = &hostif->endpoint[1].desc; atomic_set(&ucs->busy, 0); - ucs->udev = udev; - ucs->interface = interface; ucs->read_urb = usb_alloc_urb(0, SLAB_KERNEL); if (!ucs->read_urb) { - err("No free urbs available"); + dev_err(cs->dev, "No free urbs available\n"); retval = -ENOMEM; goto error; } @@ -825,38 +786,33 @@ static int gigaset_probe(struct usb_interface *interface, ucs->int_in_endpointAddr = endpoint->bEndpointAddress; cs->inbuf[0].rcvbuf = kmalloc(buffer_size, GFP_KERNEL); if (!cs->inbuf[0].rcvbuf) { - err("Couldn't allocate rcvbuf"); + dev_err(cs->dev, "Couldn't allocate rcvbuf\n"); retval = -ENOMEM; goto error; } /* Fill the interrupt urb and send it to the core */ usb_fill_int_urb(ucs->read_urb, udev, - usb_rcvintpipe(udev, - endpoint->bEndpointAddress & 0x0f), - cs->inbuf[0].rcvbuf, buffer_size, - gigaset_read_int_callback, - cs->inbuf + 0, endpoint->bInterval); + usb_rcvintpipe(udev, + endpoint->bEndpointAddress & 0x0f), + cs->inbuf[0].rcvbuf, buffer_size, + gigaset_read_int_callback, + cs->inbuf + 0, endpoint->bInterval); retval = usb_submit_urb(ucs->read_urb, SLAB_KERNEL); if (retval) { - err("Could not submit URB!"); + dev_err(cs->dev, "Could not submit URB (error %d)\n", -retval); goto error; } /* tell common part that the device is ready */ if (startmode == SM_LOCKED) atomic_set(&cs->mstate, MS_LOCKED); + if (!gigaset_start(cs)) { tasklet_kill(&cs->write_tasklet); retval = -ENODEV; //FIXME goto error; } - - /* save address of controller structure */ - usb_set_intfdata(interface, cs); - - /* set up device sysfs */ - gigaset_init_dev_sysfs(interface); return 0; error: @@ -868,48 +824,45 @@ error: kfree(cs->inbuf[0].rcvbuf); if (ucs->read_urb != NULL) usb_free_urb(ucs->read_urb); + usb_set_intfdata(interface, NULL); ucs->read_urb = ucs->bulk_out_urb = NULL; cs->inbuf[0].rcvbuf = ucs->bulk_out_buffer = NULL; + usb_put_dev(ucs->udev); + ucs->udev = NULL; + ucs->interface = NULL; gigaset_unassign(cs); return retval; } -/** - * skel_disconnect - */ static void gigaset_disconnect(struct usb_interface *interface) { struct cardstate *cs; struct usb_cardstate *ucs; cs = usb_get_intfdata(interface); - - /* clear device sysfs */ - gigaset_free_dev_sysfs(interface); - - usb_set_intfdata(interface, NULL); ucs = cs->hw.usb; usb_kill_urb(ucs->read_urb); - //info("GigaSet USB device #%d will be disconnected", minor); gigaset_stop(cs); + usb_set_intfdata(interface, NULL); tasklet_kill(&cs->write_tasklet); - usb_kill_urb(ucs->bulk_out_urb); /* FIXME: nur, wenn noetig */ - //usb_kill_urb(ucs->urb_cmd_out); /* FIXME: nur, wenn noetig */ + usb_kill_urb(ucs->bulk_out_urb); /* FIXME: only if active? */ kfree(ucs->bulk_out_buffer); if (ucs->bulk_out_urb != NULL) usb_free_urb(ucs->bulk_out_urb); - //if(ucs->urb_cmd_out != NULL) - // usb_free_urb(ucs->urb_cmd_out); kfree(cs->inbuf[0].rcvbuf); if (ucs->read_urb != NULL) usb_free_urb(ucs->read_urb); - ucs->read_urb = ucs->bulk_out_urb/*=ucs->urb_cmd_out*/=NULL; + ucs->read_urb = ucs->bulk_out_urb = NULL; cs->inbuf[0].rcvbuf = ucs->bulk_out_buffer = NULL; + usb_put_dev(ucs->udev); + ucs->interface = NULL; + ucs->udev = NULL; + cs->dev = NULL; gigaset_unassign(cs); } @@ -942,9 +895,9 @@ static int __init usb_gigaset_init(void) /* allocate memory for our driver state and intialize it */ if ((driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS, - GIGASET_MODULENAME, GIGASET_DEVNAME, - GIGASET_DEVFSNAME, &ops, - THIS_MODULE)) == NULL) + GIGASET_MODULENAME, GIGASET_DEVNAME, + GIGASET_DEVFSNAME, &ops, + THIS_MODULE)) == NULL) goto error; /* allocate memory for our device state and intialize it */ @@ -981,8 +934,8 @@ error: if (cardstate) static void __exit usb_gigaset_exit(void) { gigaset_blockdriver(driver); /* => probe will fail - * => no gigaset_start any more - */ + * => no gigaset_start any more + */ gigaset_shutdown(cardstate); /* from now on, no isdn callback should be possible */ diff --git a/drivers/isdn/hardware/avm/avm_cs.c b/drivers/isdn/hardware/avm/avm_cs.c index 2a2b03ff096b..7bbfd85ab793 100644 --- a/drivers/isdn/hardware/avm/avm_cs.c +++ b/drivers/isdn/hardware/avm/avm_cs.c @@ -51,8 +51,8 @@ MODULE_LICENSE("GPL"); handler. */ -static void avmcs_config(dev_link_t *link); -static void avmcs_release(dev_link_t *link); +static int avmcs_config(struct pcmcia_device *link); +static void avmcs_release(struct pcmcia_device *link); /* The attach() and detach() entry points are used to create and destroy @@ -65,10 +65,10 @@ static void avmcs_detach(struct pcmcia_device *p_dev); /* A linked list of "instances" of the skeleton device. Each actual PCMCIA card corresponds to one device instance, and is described - by one dev_link_t structure (defined in ds.h). + by one struct pcmcia_device structure (defined in ds.h). You may not want to use a linked list for this -- for example, the - memory card driver uses an array of dev_link_t pointers, where minor + memory card driver uses an array of struct pcmcia_device pointers, where minor device numbers are used to derive the corresponding array index. */ @@ -78,7 +78,7 @@ static void avmcs_detach(struct pcmcia_device *p_dev); example, ethernet cards, modems). In other cases, there may be many actual or logical devices (SCSI adapters, memory cards with multiple partitions). The dev_node_t structures need to be kept - in a linked list starting at the 'dev' field of a dev_link_t + in a linked list starting at the 'dev' field of a struct pcmcia_device structure. We allocate them in the card's private data structure, because they generally can't be allocated dynamically. */ @@ -99,54 +99,38 @@ typedef struct local_info_t { ======================================================================*/ -static int avmcs_attach(struct pcmcia_device *p_dev) +static int avmcs_probe(struct pcmcia_device *p_dev) { - dev_link_t *link; local_info_t *local; - /* Initialize the dev_link_t structure */ - link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL); - if (!link) - goto err; - memset(link, 0, sizeof(struct dev_link_t)); - /* The io structure describes IO port mapping */ - link->io.NumPorts1 = 16; - link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; - link->io.NumPorts2 = 0; + p_dev->io.NumPorts1 = 16; + p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + p_dev->io.NumPorts2 = 0; /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED; + p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED; + + p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID; - link->irq.IRQInfo1 = IRQ_LEVEL_ID; - /* General socket configuration */ - link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; - link->conf.IntType = INT_MEMORY_AND_IO; - link->conf.ConfigIndex = 1; - link->conf.Present = PRESENT_OPTION; + p_dev->conf.Attributes = CONF_ENABLE_IRQ; + p_dev->conf.IntType = INT_MEMORY_AND_IO; + p_dev->conf.ConfigIndex = 1; + p_dev->conf.Present = PRESENT_OPTION; /* Allocate space for private device-specific data */ local = kmalloc(sizeof(local_info_t), GFP_KERNEL); if (!local) - goto err_kfree; + goto err; memset(local, 0, sizeof(local_info_t)); - link->priv = local; + p_dev->priv = local; - link->handle = p_dev; - p_dev->instance = link; + return avmcs_config(p_dev); - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - avmcs_config(link); - - return 0; - - err_kfree: - kfree(link); err: - return -EINVAL; + return -ENOMEM; } /* avmcs_attach */ /*====================================================================== @@ -158,15 +142,10 @@ static int avmcs_attach(struct pcmcia_device *p_dev) ======================================================================*/ -static void avmcs_detach(struct pcmcia_device *p_dev) +static void avmcs_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); - - if (link->state & DEV_CONFIG) avmcs_release(link); - - kfree(link->priv); - kfree(link); + kfree(link->priv); } /* avmcs_detach */ /*====================================================================== @@ -177,7 +156,7 @@ static void avmcs_detach(struct pcmcia_device *p_dev) ======================================================================*/ -static int get_tuple(client_handle_t handle, tuple_t *tuple, +static int get_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { int i = pcmcia_get_tuple_data(handle, tuple); @@ -185,7 +164,7 @@ static int get_tuple(client_handle_t handle, tuple_t *tuple, return pcmcia_parse_tuple(handle, tuple, parse); } -static int first_tuple(client_handle_t handle, tuple_t *tuple, +static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { int i = pcmcia_get_first_tuple(handle, tuple); @@ -193,7 +172,7 @@ static int first_tuple(client_handle_t handle, tuple_t *tuple, return get_tuple(handle, tuple, parse); } -static int next_tuple(client_handle_t handle, tuple_t *tuple, +static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { int i = pcmcia_get_next_tuple(handle, tuple); @@ -201,9 +180,8 @@ static int next_tuple(client_handle_t handle, tuple_t *tuple, return get_tuple(handle, tuple, parse); } -static void avmcs_config(dev_link_t *link) +static int avmcs_config(struct pcmcia_device *link) { - client_handle_t handle; tuple_t tuple; cisparse_t parse; cistpl_cftable_entry_t *cf = &parse.cftable_entry; @@ -213,8 +191,7 @@ static void avmcs_config(dev_link_t *link) char devname[128]; int cardtype; int (*addcard)(unsigned int port, unsigned irq); - - handle = link->handle; + dev = link->priv; /* @@ -223,25 +200,21 @@ static void avmcs_config(dev_link_t *link) */ do { tuple.DesiredTuple = CISTPL_CONFIG; - i = pcmcia_get_first_tuple(handle, &tuple); + i = pcmcia_get_first_tuple(link, &tuple); if (i != CS_SUCCESS) break; tuple.TupleData = buf; tuple.TupleDataMax = 64; tuple.TupleOffset = 0; - i = pcmcia_get_tuple_data(handle, &tuple); + i = pcmcia_get_tuple_data(link, &tuple); if (i != CS_SUCCESS) break; - i = pcmcia_parse_tuple(handle, &tuple, &parse); + i = pcmcia_parse_tuple(link, &tuple, &parse); if (i != CS_SUCCESS) break; link->conf.ConfigBase = parse.config.base; } while (0); if (i != CS_SUCCESS) { - cs_error(link->handle, ParseTuple, i); - link->state &= ~DEV_CONFIG_PENDING; - return; + cs_error(link, ParseTuple, i); + return -ENODEV; } - - /* Configure card */ - link->state |= DEV_CONFIG; do { @@ -252,7 +225,7 @@ static void avmcs_config(dev_link_t *link) tuple.DesiredTuple = CISTPL_VERS_1; devname[0] = 0; - if( !first_tuple(handle, &tuple, &parse) && parse.version_1.ns > 1 ) { + if( !first_tuple(link, &tuple, &parse) && parse.version_1.ns > 1 ) { strlcpy(devname,parse.version_1.str + parse.version_1.ofs[1], sizeof(devname)); } @@ -263,7 +236,7 @@ static void avmcs_config(dev_link_t *link) tuple.TupleOffset = 0; tuple.TupleDataMax = 255; tuple.Attributes = 0; tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - i = first_tuple(handle, &tuple, &parse); + i = first_tuple(link, &tuple, &parse); while (i == CS_SUCCESS) { if (cf->io.nwin > 0) { link->conf.ConfigIndex = cf->index; @@ -273,36 +246,36 @@ static void avmcs_config(dev_link_t *link) printk(KERN_INFO "avm_cs: testing i/o %#x-%#x\n", link->io.BasePort1, link->io.BasePort1+link->io.NumPorts1-1); - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) goto found_port; } - i = next_tuple(handle, &tuple, &parse); + i = next_tuple(link, &tuple, &parse); } found_port: if (i != CS_SUCCESS) { - cs_error(link->handle, RequestIO, i); + cs_error(link, RequestIO, i); break; } - + /* * allocate an interrupt line */ - i = pcmcia_request_irq(link->handle, &link->irq); + i = pcmcia_request_irq(link, &link->irq); if (i != CS_SUCCESS) { - cs_error(link->handle, RequestIRQ, i); - pcmcia_release_io(link->handle, &link->io); + cs_error(link, RequestIRQ, i); + /* undo */ + pcmcia_disable_device(link); break; } - + /* * configure the PCMCIA socket */ - i = pcmcia_request_configuration(link->handle, &link->conf); + i = pcmcia_request_configuration(link, &link->conf); if (i != CS_SUCCESS) { - cs_error(link->handle, RequestConfiguration, i); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); + cs_error(link, RequestConfiguration, i); + pcmcia_disable_device(link); break; } @@ -331,13 +304,12 @@ found_port: dev->node.major = 64; dev->node.minor = 0; - link->dev = &dev->node; - - link->state &= ~DEV_CONFIG_PENDING; + link->dev_node = &dev->node; + /* If any step failed, release any partially configured state */ if (i != 0) { avmcs_release(link); - return; + return -ENODEV; } @@ -351,9 +323,10 @@ found_port: printk(KERN_ERR "avm_cs: failed to add AVM-%s-Controller at i/o %#x, irq %d\n", dev->node.dev_name, link->io.BasePort1, link->irq.AssignedIRQ); avmcs_release(link); - return; + return -ENODEV; } dev->node.minor = i; + return 0; } /* avmcs_config */ @@ -365,56 +338,12 @@ found_port: ======================================================================*/ -static void avmcs_release(dev_link_t *link) +static void avmcs_release(struct pcmcia_device *link) { - b1pcmcia_delcard(link->io.BasePort1, link->irq.AssignedIRQ); - - /* Unlink the device chain */ - link->dev = NULL; - - /* Don't bother checking to see if these succeed or not */ - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - link->state &= ~DEV_CONFIG; + b1pcmcia_delcard(link->io.BasePort1, link->irq.AssignedIRQ); + pcmcia_disable_device(link); } /* avmcs_release */ -static int avmcs_suspend(struct pcmcia_device *dev) -{ - dev_link_t *link = dev_to_instance(dev); - - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) - pcmcia_release_configuration(link->handle); - - return 0; -} - -static int avmcs_resume(struct pcmcia_device *dev) -{ - dev_link_t *link = dev_to_instance(dev); - - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) - pcmcia_request_configuration(link->handle, &link->conf); - - return 0; -} - -/*====================================================================== - - The card status event handler. Mostly, this schedules other - stuff to run after an event is received. A CARD_REMOVAL event - also sets some flags to discourage the net drivers from trying - to talk to the card any more. - - When a CARD_REMOVAL event is received, we immediately set a flag - to block future accesses to this device. All the functions that - actually access the device should check this flag to make sure - the card is still present. - -======================================================================*/ - static struct pcmcia_device_id avmcs_ids[] = { PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN-Controller B1", 0x95d42008, 0x845dc335), @@ -429,11 +358,9 @@ static struct pcmcia_driver avmcs_driver = { .drv = { .name = "avm_cs", }, - .probe = avmcs_attach, + .probe = avmcs_probe, .remove = avmcs_detach, .id_table = avmcs_ids, - .suspend= avmcs_suspend, - .resume = avmcs_resume, }; static int __init avmcs_init(void) diff --git a/drivers/isdn/hisax/avma1_cs.c b/drivers/isdn/hisax/avma1_cs.c index 969da40c4248..ac28e3278ad9 100644 --- a/drivers/isdn/hisax/avma1_cs.c +++ b/drivers/isdn/hisax/avma1_cs.c @@ -67,8 +67,8 @@ module_param(isdnprot, int, 0); handler. */ -static void avma1cs_config(dev_link_t *link); -static void avma1cs_release(dev_link_t *link); +static int avma1cs_config(struct pcmcia_device *link); +static void avma1cs_release(struct pcmcia_device *link); /* The attach() and detach() entry points are used to create and destroy @@ -82,10 +82,10 @@ static void avma1cs_detach(struct pcmcia_device *p_dev); /* A linked list of "instances" of the skeleton device. Each actual PCMCIA card corresponds to one device instance, and is described - by one dev_link_t structure (defined in ds.h). + by one struct pcmcia_device structure (defined in ds.h). You may not want to use a linked list for this -- for example, the - memory card driver uses an array of dev_link_t pointers, where minor + memory card driver uses an array of struct pcmcia_device pointers, where minor device numbers are used to derive the corresponding array index. */ @@ -95,7 +95,7 @@ static void avma1cs_detach(struct pcmcia_device *p_dev); example, ethernet cards, modems). In other cases, there may be many actual or logical devices (SCSI adapters, memory cards with multiple partitions). The dev_node_t structures need to be kept - in a linked list starting at the 'dev' field of a dev_link_t + in a linked list starting at the 'dev' field of a struct pcmcia_device structure. We allocate them in the card's private data structure, because they generally can't be allocated dynamically. */ @@ -116,55 +116,40 @@ typedef struct local_info_t { ======================================================================*/ -static int avma1cs_attach(struct pcmcia_device *p_dev) +static int avma1cs_probe(struct pcmcia_device *p_dev) { - dev_link_t *link; local_info_t *local; DEBUG(0, "avma1cs_attach()\n"); - /* Initialize the dev_link_t structure */ - link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL); - if (!link) - return -ENOMEM; - memset(link, 0, sizeof(struct dev_link_t)); - /* Allocate space for private device-specific data */ local = kmalloc(sizeof(local_info_t), GFP_KERNEL); - if (!local) { - kfree(link); + if (!local) return -ENOMEM; - } + memset(local, 0, sizeof(local_info_t)); - link->priv = local; + p_dev->priv = local; /* The io structure describes IO port mapping */ - link->io.NumPorts1 = 16; - link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; - link->io.NumPorts2 = 16; - link->io.Attributes2 = IO_DATA_PATH_WIDTH_16; - link->io.IOAddrLines = 5; + p_dev->io.NumPorts1 = 16; + p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + p_dev->io.NumPorts2 = 16; + p_dev->io.Attributes2 = IO_DATA_PATH_WIDTH_16; + p_dev->io.IOAddrLines = 5; /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; - link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED; + p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED; - link->irq.IRQInfo1 = IRQ_LEVEL_ID; + p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID; /* General socket configuration */ - link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; - link->conf.IntType = INT_MEMORY_AND_IO; - link->conf.ConfigIndex = 1; - link->conf.Present = PRESENT_OPTION; + p_dev->conf.Attributes = CONF_ENABLE_IRQ; + p_dev->conf.IntType = INT_MEMORY_AND_IO; + p_dev->conf.ConfigIndex = 1; + p_dev->conf.Present = PRESENT_OPTION; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - avma1cs_config(link); - - return 0; + return avma1cs_config(p_dev); } /* avma1cs_attach */ /*====================================================================== @@ -176,17 +161,11 @@ static int avma1cs_attach(struct pcmcia_device *p_dev) ======================================================================*/ -static void avma1cs_detach(struct pcmcia_device *p_dev) +static void avma1cs_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); - - DEBUG(0, "avma1cs_detach(0x%p)\n", link); - - if (link->state & DEV_CONFIG) - avma1cs_release(link); - - kfree(link->priv); - kfree(link); + DEBUG(0, "avma1cs_detach(0x%p)\n", link); + avma1cs_release(link); + kfree(link->priv); } /* avma1cs_detach */ /*====================================================================== @@ -197,7 +176,7 @@ static void avma1cs_detach(struct pcmcia_device *p_dev) ======================================================================*/ -static int get_tuple(client_handle_t handle, tuple_t *tuple, +static int get_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { int i = pcmcia_get_tuple_data(handle, tuple); @@ -205,7 +184,7 @@ static int get_tuple(client_handle_t handle, tuple_t *tuple, return pcmcia_parse_tuple(handle, tuple, parse); } -static int first_tuple(client_handle_t handle, tuple_t *tuple, +static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { int i = pcmcia_get_first_tuple(handle, tuple); @@ -213,7 +192,7 @@ static int first_tuple(client_handle_t handle, tuple_t *tuple, return get_tuple(handle, tuple, parse); } -static int next_tuple(client_handle_t handle, tuple_t *tuple, +static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { int i = pcmcia_get_next_tuple(handle, tuple); @@ -221,9 +200,8 @@ static int next_tuple(client_handle_t handle, tuple_t *tuple, return get_tuple(handle, tuple, parse); } -static void avma1cs_config(dev_link_t *link) +static int avma1cs_config(struct pcmcia_device *link) { - client_handle_t handle; tuple_t tuple; cisparse_t parse; cistpl_cftable_entry_t *cf = &parse.cftable_entry; @@ -233,8 +211,7 @@ static void avma1cs_config(dev_link_t *link) char devname[128]; IsdnCard_t icard; int busy = 0; - - handle = link->handle; + dev = link->priv; DEBUG(0, "avma1cs_config(0x%p)\n", link); @@ -245,25 +222,21 @@ static void avma1cs_config(dev_link_t *link) */ do { tuple.DesiredTuple = CISTPL_CONFIG; - i = pcmcia_get_first_tuple(handle, &tuple); + i = pcmcia_get_first_tuple(link, &tuple); if (i != CS_SUCCESS) break; tuple.TupleData = buf; tuple.TupleDataMax = 64; tuple.TupleOffset = 0; - i = pcmcia_get_tuple_data(handle, &tuple); + i = pcmcia_get_tuple_data(link, &tuple); if (i != CS_SUCCESS) break; - i = pcmcia_parse_tuple(handle, &tuple, &parse); + i = pcmcia_parse_tuple(link, &tuple, &parse); if (i != CS_SUCCESS) break; link->conf.ConfigBase = parse.config.base; } while (0); if (i != CS_SUCCESS) { - cs_error(link->handle, ParseTuple, i); - link->state &= ~DEV_CONFIG_PENDING; - return; + cs_error(link, ParseTuple, i); + return -ENODEV; } - - /* Configure card */ - link->state |= DEV_CONFIG; do { @@ -274,7 +247,7 @@ static void avma1cs_config(dev_link_t *link) tuple.DesiredTuple = CISTPL_VERS_1; devname[0] = 0; - if( !first_tuple(handle, &tuple, &parse) && parse.version_1.ns > 1 ) { + if( !first_tuple(link, &tuple, &parse) && parse.version_1.ns > 1 ) { strlcpy(devname,parse.version_1.str + parse.version_1.ofs[1], sizeof(devname)); } @@ -285,7 +258,7 @@ static void avma1cs_config(dev_link_t *link) tuple.TupleOffset = 0; tuple.TupleDataMax = 255; tuple.Attributes = 0; tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - i = first_tuple(handle, &tuple, &parse); + i = first_tuple(link, &tuple, &parse); while (i == CS_SUCCESS) { if (cf->io.nwin > 0) { link->conf.ConfigIndex = cf->index; @@ -295,36 +268,36 @@ static void avma1cs_config(dev_link_t *link) printk(KERN_INFO "avma1_cs: testing i/o %#x-%#x\n", link->io.BasePort1, link->io.BasePort1+link->io.NumPorts1 - 1); - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) goto found_port; } - i = next_tuple(handle, &tuple, &parse); + i = next_tuple(link, &tuple, &parse); } found_port: if (i != CS_SUCCESS) { - cs_error(link->handle, RequestIO, i); + cs_error(link, RequestIO, i); break; } /* * allocate an interrupt line */ - i = pcmcia_request_irq(link->handle, &link->irq); + i = pcmcia_request_irq(link, &link->irq); if (i != CS_SUCCESS) { - cs_error(link->handle, RequestIRQ, i); - pcmcia_release_io(link->handle, &link->io); + cs_error(link, RequestIRQ, i); + /* undo */ + pcmcia_disable_device(link); break; } - + /* * configure the PCMCIA socket */ - i = pcmcia_request_configuration(link->handle, &link->conf); + i = pcmcia_request_configuration(link, &link->conf); if (i != CS_SUCCESS) { - cs_error(link->handle, RequestConfiguration, i); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); + cs_error(link, RequestConfiguration, i); + pcmcia_disable_device(link); break; } @@ -336,13 +309,12 @@ found_port: strcpy(dev->node.dev_name, "A1"); dev->node.major = 45; dev->node.minor = 0; - link->dev = &dev->node; - - link->state &= ~DEV_CONFIG_PENDING; + link->dev_node = &dev->node; + /* If any step failed, release any partially configured state */ if (i != 0) { avma1cs_release(link); - return; + return -ENODEV; } printk(KERN_NOTICE "avma1_cs: checking at i/o %#x, irq %d\n", @@ -357,10 +329,11 @@ found_port: if (i < 0) { printk(KERN_ERR "avma1_cs: failed to initialize AVM A1 PCMCIA %d at i/o %#x\n", i, link->io.BasePort1); avma1cs_release(link); - return; + return -ENODEV; } dev->node.minor = i; + return 0; } /* avma1cs_config */ /*====================================================================== @@ -371,47 +344,18 @@ found_port: ======================================================================*/ -static void avma1cs_release(dev_link_t *link) +static void avma1cs_release(struct pcmcia_device *link) { - local_info_t *local = link->priv; + local_info_t *local = link->priv; - DEBUG(0, "avma1cs_release(0x%p)\n", link); + DEBUG(0, "avma1cs_release(0x%p)\n", link); - /* no unregister function with hisax */ - HiSax_closecard(local->node.minor); + /* now unregister function with hisax */ + HiSax_closecard(local->node.minor); - /* Unlink the device chain */ - link->dev = NULL; - - /* Don't bother checking to see if these succeed or not */ - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - link->state &= ~DEV_CONFIG; + pcmcia_disable_device(link); } /* avma1cs_release */ -static int avma1cs_suspend(struct pcmcia_device *dev) -{ - dev_link_t *link = dev_to_instance(dev); - - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) - pcmcia_release_configuration(link->handle); - - return 0; -} - -static int avma1cs_resume(struct pcmcia_device *dev) -{ - dev_link_t *link = dev_to_instance(dev); - - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) - pcmcia_request_configuration(link->handle, &link->conf); - - return 0; -} - static struct pcmcia_device_id avma1cs_ids[] = { PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN A", 0x95d42008, 0xadc9d4bb), @@ -425,13 +369,11 @@ static struct pcmcia_driver avma1cs_driver = { .drv = { .name = "avma1_cs", }, - .probe = avma1cs_attach, + .probe = avma1cs_probe, .remove = avma1cs_detach, .id_table = avma1cs_ids, - .suspend = avma1cs_suspend, - .resume = avma1cs_resume, }; - + /*====================================================================*/ static int __init init_avma1_cs(void) diff --git a/drivers/isdn/hisax/elsa_cs.c b/drivers/isdn/hisax/elsa_cs.c index 062fb8f0739f..e18e75be8ed3 100644 --- a/drivers/isdn/hisax/elsa_cs.c +++ b/drivers/isdn/hisax/elsa_cs.c @@ -94,8 +94,8 @@ module_param(protocol, int, 0); handler. */ -static void elsa_cs_config(dev_link_t *link); -static void elsa_cs_release(dev_link_t *link); +static int elsa_cs_config(struct pcmcia_device *link); +static void elsa_cs_release(struct pcmcia_device *link); /* The attach() and detach() entry points are used to create and destroy @@ -111,7 +111,7 @@ static void elsa_cs_detach(struct pcmcia_device *p_dev); example, ethernet cards, modems). In other cases, there may be many actual or logical devices (SCSI adapters, memory cards with multiple partitions). The dev_node_t structures need to be kept - in a linked list starting at the 'dev' field of a dev_link_t + in a linked list starting at the 'dev' field of a struct pcmcia_device structure. We allocate them in the card's private data structure, because they generally shouldn't be allocated dynamically. In this case, we also provide a flag to indicate if a device is @@ -121,7 +121,7 @@ static void elsa_cs_detach(struct pcmcia_device *p_dev); */ typedef struct local_info_t { - dev_link_t link; + struct pcmcia_device *p_dev; dev_node_t node; int busy; int cardnr; @@ -139,9 +139,8 @@ typedef struct local_info_t { ======================================================================*/ -static int elsa_cs_attach(struct pcmcia_device *p_dev) +static int elsa_cs_probe(struct pcmcia_device *link) { - dev_link_t *link; local_info_t *local; DEBUG(0, "elsa_cs_attach()\n"); @@ -150,8 +149,11 @@ static int elsa_cs_attach(struct pcmcia_device *p_dev) local = kmalloc(sizeof(local_info_t), GFP_KERNEL); if (!local) return -ENOMEM; memset(local, 0, sizeof(local_info_t)); + + local->p_dev = link; + link->priv = local; + local->cardnr = -1; - link = &local->link; link->priv = local; /* Interrupt setup */ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED; @@ -170,16 +172,9 @@ static int elsa_cs_attach(struct pcmcia_device *p_dev) link->io.IOAddrLines = 3; link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - elsa_cs_config(link); - - return 0; + return elsa_cs_config(link); } /* elsa_cs_attach */ /*====================================================================== @@ -191,20 +186,16 @@ static int elsa_cs_attach(struct pcmcia_device *p_dev) ======================================================================*/ -static void elsa_cs_detach(struct pcmcia_device *p_dev) +static void elsa_cs_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); - local_info_t *info = link->priv; + local_info_t *info = link->priv; - DEBUG(0, "elsa_cs_detach(0x%p)\n", link); + DEBUG(0, "elsa_cs_detach(0x%p)\n", link); - if (link->state & DEV_CONFIG) { - info->busy = 1; - elsa_cs_release(link); - } - - kfree(info); + info->busy = 1; + elsa_cs_release(link); + kfree(info); } /* elsa_cs_detach */ /*====================================================================== @@ -214,7 +205,7 @@ static void elsa_cs_detach(struct pcmcia_device *p_dev) device available to the system. ======================================================================*/ -static int get_tuple(client_handle_t handle, tuple_t *tuple, +static int get_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { int i = pcmcia_get_tuple_data(handle, tuple); @@ -222,7 +213,7 @@ static int get_tuple(client_handle_t handle, tuple_t *tuple, return pcmcia_parse_tuple(handle, tuple, parse); } -static int first_tuple(client_handle_t handle, tuple_t *tuple, +static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { int i = pcmcia_get_first_tuple(handle, tuple); @@ -230,7 +221,7 @@ static int first_tuple(client_handle_t handle, tuple_t *tuple, return get_tuple(handle, tuple, parse); } -static int next_tuple(client_handle_t handle, tuple_t *tuple, +static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { int i = pcmcia_get_next_tuple(handle, tuple); @@ -238,9 +229,8 @@ static int next_tuple(client_handle_t handle, tuple_t *tuple, return get_tuple(handle, tuple, parse); } -static void elsa_cs_config(dev_link_t *link) +static int elsa_cs_config(struct pcmcia_device *link) { - client_handle_t handle; tuple_t tuple; cisparse_t parse; local_info_t *dev; @@ -250,7 +240,6 @@ static void elsa_cs_config(dev_link_t *link) IsdnCard_t icard; DEBUG(0, "elsa_config(0x%p)\n", link); - handle = link->handle; dev = link->priv; /* @@ -262,7 +251,7 @@ static void elsa_cs_config(dev_link_t *link) tuple.TupleDataMax = 255; tuple.TupleOffset = 0; tuple.Attributes = 0; - i = first_tuple(handle, &tuple, &parse); + i = first_tuple(link, &tuple, &parse); if (i != CS_SUCCESS) { last_fn = ParseTuple; goto cs_failed; @@ -270,32 +259,29 @@ static void elsa_cs_config(dev_link_t *link) link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; - /* Configure card */ - link->state |= DEV_CONFIG; - tuple.TupleData = (cisdata_t *)buf; tuple.TupleOffset = 0; tuple.TupleDataMax = 255; tuple.Attributes = 0; tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - i = first_tuple(handle, &tuple, &parse); + i = first_tuple(link, &tuple, &parse); while (i == CS_SUCCESS) { if ( (cf->io.nwin > 0) && cf->io.win[0].base) { printk(KERN_INFO "(elsa_cs: looks like the 96 model)\n"); link->conf.ConfigIndex = cf->index; link->io.BasePort1 = cf->io.win[0].base; - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) break; } else { printk(KERN_INFO "(elsa_cs: looks like the 97 model)\n"); link->conf.ConfigIndex = cf->index; for (i = 0, j = 0x2f0; j > 0x100; j -= 0x10) { link->io.BasePort1 = j; - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) break; } break; } - i = next_tuple(handle, &tuple, &parse); + i = next_tuple(link, &tuple, &parse); } if (i != CS_SUCCESS) { @@ -303,14 +289,14 @@ static void elsa_cs_config(dev_link_t *link) goto cs_failed; } - i = pcmcia_request_irq(link->handle, &link->irq); + i = pcmcia_request_irq(link, &link->irq); if (i != CS_SUCCESS) { link->irq.AssignedIRQ = 0; last_fn = RequestIRQ; goto cs_failed; } - i = pcmcia_request_configuration(link->handle, &link->conf); + i = pcmcia_request_configuration(link, &link->conf); if (i != CS_SUCCESS) { last_fn = RequestConfiguration; goto cs_failed; @@ -321,14 +307,11 @@ static void elsa_cs_config(dev_link_t *link) sprintf(dev->node.dev_name, "elsa"); dev->node.major = dev->node.minor = 0x0; - link->dev = &dev->node; + link->dev_node = &dev->node; /* Finally, report what we've done */ - printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d", - dev->node.dev_name, link->conf.ConfigIndex, - link->conf.Vcc/10, link->conf.Vcc%10); - if (link->conf.Vpp1) - printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10); + printk(KERN_INFO "%s: index 0x%02x: ", + dev->node.dev_name, link->conf.ConfigIndex); if (link->conf.Attributes & CONF_ENABLE_IRQ) printk(", irq %d", link->irq.AssignedIRQ); if (link->io.NumPorts1) @@ -339,8 +322,6 @@ static void elsa_cs_config(dev_link_t *link) link->io.BasePort2+link->io.NumPorts2-1); printk("\n"); - link->state &= ~DEV_CONFIG_PENDING; - icard.para[0] = link->irq.AssignedIRQ; icard.para[1] = link->io.BasePort1; icard.protocol = protocol; @@ -354,10 +335,11 @@ static void elsa_cs_config(dev_link_t *link) } else ((local_info_t*)link->priv)->cardnr = i; - return; + return 0; cs_failed: - cs_error(link->handle, last_fn, i); + cs_error(link, last_fn, i); elsa_cs_release(link); + return -ENODEV; } /* elsa_cs_config */ /*====================================================================== @@ -368,7 +350,7 @@ cs_failed: ======================================================================*/ -static void elsa_cs_release(dev_link_t *link) +static void elsa_cs_release(struct pcmcia_device *link) { local_info_t *local = link->priv; @@ -380,39 +362,23 @@ static void elsa_cs_release(dev_link_t *link) HiSax_closecard(local->cardnr); } } - /* Unlink the device chain */ - link->dev = NULL; - /* Don't bother checking to see if these succeed or not */ - if (link->win) - pcmcia_release_window(link->win); - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - link->state &= ~DEV_CONFIG; + pcmcia_disable_device(link); } /* elsa_cs_release */ -static int elsa_suspend(struct pcmcia_device *p_dev) +static int elsa_suspend(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); local_info_t *dev = link->priv; - link->state |= DEV_SUSPEND; dev->busy = 1; - if (link->state & DEV_CONFIG) - pcmcia_release_configuration(link->handle); return 0; } -static int elsa_resume(struct pcmcia_device *p_dev) +static int elsa_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); local_info_t *dev = link->priv; - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) - pcmcia_request_configuration(link->handle, &link->conf); dev->busy = 0; return 0; @@ -430,7 +396,7 @@ static struct pcmcia_driver elsa_cs_driver = { .drv = { .name = "elsa_cs", }, - .probe = elsa_cs_attach, + .probe = elsa_cs_probe, .remove = elsa_cs_detach, .id_table = elsa_ids, .suspend = elsa_suspend, diff --git a/drivers/isdn/hisax/sedlbauer_cs.c b/drivers/isdn/hisax/sedlbauer_cs.c index 6f5213a18a8d..9bb18f3f7829 100644 --- a/drivers/isdn/hisax/sedlbauer_cs.c +++ b/drivers/isdn/hisax/sedlbauer_cs.c @@ -95,8 +95,8 @@ module_param(protocol, int, 0); event handler. */ -static void sedlbauer_config(dev_link_t *link); -static void sedlbauer_release(dev_link_t *link); +static int sedlbauer_config(struct pcmcia_device *link); +static void sedlbauer_release(struct pcmcia_device *link); /* The attach() and detach() entry points are used to create and destroy @@ -119,7 +119,7 @@ static void sedlbauer_detach(struct pcmcia_device *p_dev); example, ethernet cards, modems). In other cases, there may be many actual or logical devices (SCSI adapters, memory cards with multiple partitions). The dev_node_t structures need to be kept - in a linked list starting at the 'dev' field of a dev_link_t + in a linked list starting at the 'dev' field of a struct pcmcia_device structure. We allocate them in the card's private data structure, because they generally shouldn't be allocated dynamically. @@ -130,7 +130,7 @@ static void sedlbauer_detach(struct pcmcia_device *p_dev); */ typedef struct local_info_t { - dev_link_t link; + struct pcmcia_device *p_dev; dev_node_t node; int stop; int cardnr; @@ -148,11 +148,10 @@ typedef struct local_info_t { ======================================================================*/ -static int sedlbauer_attach(struct pcmcia_device *p_dev) +static int sedlbauer_probe(struct pcmcia_device *link) { local_info_t *local; - dev_link_t *link; - + DEBUG(0, "sedlbauer_attach()\n"); /* Allocate space for private device-specific data */ @@ -160,8 +159,10 @@ static int sedlbauer_attach(struct pcmcia_device *p_dev) if (!local) return -ENOMEM; memset(local, 0, sizeof(local_info_t)); local->cardnr = -1; - link = &local->link; link->priv = local; - + + local->p_dev = link; + link->priv = local; + /* Interrupt setup */ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; link->irq.IRQInfo1 = IRQ_LEVEL_ID; @@ -182,18 +183,10 @@ static int sedlbauer_attach(struct pcmcia_device *p_dev) link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; link->io.IOAddrLines = 3; - link->conf.Attributes = 0; - link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - sedlbauer_config(link); - - return 0; + return sedlbauer_config(link); } /* sedlbauer_attach */ /*====================================================================== @@ -205,19 +198,15 @@ static int sedlbauer_attach(struct pcmcia_device *p_dev) ======================================================================*/ -static void sedlbauer_detach(struct pcmcia_device *p_dev) +static void sedlbauer_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); + DEBUG(0, "sedlbauer_detach(0x%p)\n", link); - DEBUG(0, "sedlbauer_detach(0x%p)\n", link); + ((local_info_t *)link->priv)->stop = 1; + sedlbauer_release(link); - if (link->state & DEV_CONFIG) { - ((local_info_t *)link->priv)->stop = 1; - sedlbauer_release(link); - } - - /* This points to the parent local_info_t struct */ - kfree(link->priv); + /* This points to the parent local_info_t struct */ + kfree(link->priv); } /* sedlbauer_detach */ /*====================================================================== @@ -230,9 +219,8 @@ static void sedlbauer_detach(struct pcmcia_device *p_dev) #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) -static void sedlbauer_config(dev_link_t *link) +static int sedlbauer_config(struct pcmcia_device *link) { - client_handle_t handle = link->handle; local_info_t *dev = link->priv; tuple_t tuple; cisparse_t parse; @@ -254,18 +242,13 @@ static void sedlbauer_config(dev_link_t *link) tuple.TupleData = buf; tuple.TupleDataMax = sizeof(buf); tuple.TupleOffset = 0; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; - - /* Configure card */ - link->state |= DEV_CONFIG; - /* Look up the current Vcc */ - CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf)); - link->conf.Vcc = conf.Vcc; + CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(link, &conf)); /* In this loop, we scan the CIS for configuration table entries, @@ -280,12 +263,12 @@ static void sedlbauer_config(dev_link_t *link) will only use the CIS to fill in implementation-defined details. */ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); while (1) { cistpl_cftable_entry_t dflt = { 0 }; cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); - if (pcmcia_get_tuple_data(handle, &tuple) != 0 || - pcmcia_parse_tuple(handle, &tuple, &parse) != 0) + if (pcmcia_get_tuple_data(link, &tuple) != 0 || + pcmcia_parse_tuple(link, &tuple, &parse) != 0) goto next_entry; if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg; @@ -309,10 +292,10 @@ static void sedlbauer_config(dev_link_t *link) } if (cfg->vpp1.present & (1<conf.Vpp1 = link->conf.Vpp2 = + link->conf.Vpp = cfg->vpp1.param[CISTPL_POWER_VNOM]/10000; else if (dflt.vpp1.present & (1<conf.Vpp1 = link->conf.Vpp2 = + link->conf.Vpp = dflt.vpp1.param[CISTPL_POWER_VNOM]/10000; /* Do we need to allocate an interrupt? */ @@ -339,13 +322,13 @@ static void sedlbauer_config(dev_link_t *link) link->io.NumPorts2 = io->win[1].len; } /* This reserves IO space but doesn't actually enable it */ - if (pcmcia_request_io(link->handle, &link->io) != 0) + if (pcmcia_request_io(link, &link->io) != 0) goto next_entry; } /* Now set up a common memory window, if needed. There is room - in the dev_link_t structure for one memory window handle, + in the struct pcmcia_device structure for one memory window handle, but if the base addresses need to be saved, or if multiple windows are needed, the info should go in the private data structure for this device. @@ -366,7 +349,7 @@ static void sedlbauer_config(dev_link_t *link) req.Size = 0x1000; */ req.AccessSpeed = 0; - if (pcmcia_request_window(&link->handle, &req, &link->win) != 0) + if (pcmcia_request_window(&link, &req, &link->win) != 0) goto next_entry; map.Page = 0; map.CardOffset = mem->win[0].card_addr; if (pcmcia_map_mem_page(link->win, &map) != 0) @@ -374,29 +357,25 @@ static void sedlbauer_config(dev_link_t *link) } /* If we got this far, we're cool! */ break; - + next_entry: -/* new in dummy.cs 2001/01/28 MN - if (link->io.NumPorts1) - pcmcia_release_io(link->handle, &link->io); -*/ - CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple)); + CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple)); } - + /* Allocate an interrupt line. Note that this does not assign a handler to the interrupt, unless the 'Handler' member of the irq structure is initialized. */ if (link->conf.Attributes & CONF_ENABLE_IRQ) - CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); /* This actually configures the PCMCIA socket -- setting up the I/O windows and the interrupt mapping, and putting the card and host interface into "Memory and IO" mode. */ - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); /* At this point, the dev_node_t structure(s) need to be @@ -404,14 +383,13 @@ static void sedlbauer_config(dev_link_t *link) */ sprintf(dev->node.dev_name, "sedlbauer"); dev->node.major = dev->node.minor = 0; - link->dev = &dev->node; + link->dev_node = &dev->node; /* Finally, report what we've done */ - printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d", - dev->node.dev_name, link->conf.ConfigIndex, - link->conf.Vcc/10, link->conf.Vcc%10); - if (link->conf.Vpp1) - printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10); + printk(KERN_INFO "%s: index 0x%02x:", + dev->node.dev_name, link->conf.ConfigIndex); + if (link->conf.Vpp) + printk(", Vpp %d.%d", link->conf.Vpp/10, link->conf.Vpp%10); if (link->conf.Attributes & CONF_ENABLE_IRQ) printk(", irq %d", link->irq.AssignedIRQ); if (link->io.NumPorts1) @@ -424,8 +402,6 @@ static void sedlbauer_config(dev_link_t *link) printk(", mem 0x%06lx-0x%06lx", req.Base, req.Base+req.Size-1); printk("\n"); - - link->state &= ~DEV_CONFIG_PENDING; icard.para[0] = link->irq.AssignedIRQ; icard.para[1] = link->io.BasePort1; @@ -437,14 +413,16 @@ static void sedlbauer_config(dev_link_t *link) printk(KERN_ERR "sedlbauer_cs: failed to initialize SEDLBAUER PCMCIA %d at i/o %#x\n", last_ret, link->io.BasePort1); sedlbauer_release(link); + return -ENODEV; } else ((local_info_t*)link->priv)->cardnr = last_ret; - return; + return 0; cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); sedlbauer_release(link); + return -ENODEV; } /* sedlbauer_config */ @@ -456,7 +434,7 @@ cs_failed: ======================================================================*/ -static void sedlbauer_release(dev_link_t *link) +static void sedlbauer_release(struct pcmcia_device *link) { local_info_t *local = link->priv; DEBUG(0, "sedlbauer_release(0x%p)\n", link); @@ -467,46 +445,23 @@ static void sedlbauer_release(dev_link_t *link) HiSax_closecard(local->cardnr); } } - /* Unlink the device chain */ - link->dev = NULL; - /* - In a normal driver, additional code may be needed to release - other kernel data structures associated with this device. - */ - - /* Don't bother checking to see if these succeed or not */ - if (link->win) - pcmcia_release_window(link->win); - pcmcia_release_configuration(link->handle); - if (link->io.NumPorts1) - pcmcia_release_io(link->handle, &link->io); - if (link->irq.AssignedIRQ) - pcmcia_release_irq(link->handle, &link->irq); - link->state &= ~DEV_CONFIG; + pcmcia_disable_device(link); } /* sedlbauer_release */ -static int sedlbauer_suspend(struct pcmcia_device *p_dev) +static int sedlbauer_suspend(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); local_info_t *dev = link->priv; - link->state |= DEV_SUSPEND; dev->stop = 1; - if (link->state & DEV_CONFIG) - pcmcia_release_configuration(link->handle); return 0; } -static int sedlbauer_resume(struct pcmcia_device *p_dev) +static int sedlbauer_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); local_info_t *dev = link->priv; - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) - pcmcia_request_configuration(link->handle, &link->conf); dev->stop = 0; return 0; @@ -530,7 +485,7 @@ static struct pcmcia_driver sedlbauer_driver = { .drv = { .name = "sedlbauer_cs", }, - .probe = sedlbauer_attach, + .probe = sedlbauer_probe, .remove = sedlbauer_detach, .id_table = sedlbauer_ids, .suspend = sedlbauer_suspend, diff --git a/drivers/isdn/hisax/teles_cs.c b/drivers/isdn/hisax/teles_cs.c index 4e5c14c7240e..afcc2aeadb34 100644 --- a/drivers/isdn/hisax/teles_cs.c +++ b/drivers/isdn/hisax/teles_cs.c @@ -75,8 +75,8 @@ module_param(protocol, int, 0); handler. */ -static void teles_cs_config(dev_link_t *link); -static void teles_cs_release(dev_link_t *link); +static int teles_cs_config(struct pcmcia_device *link); +static void teles_cs_release(struct pcmcia_device *link); /* The attach() and detach() entry points are used to create and destroy @@ -89,10 +89,10 @@ static void teles_detach(struct pcmcia_device *p_dev); /* A linked list of "instances" of the teles_cs device. Each actual PCMCIA card corresponds to one device instance, and is described - by one dev_link_t structure (defined in ds.h). + by one struct pcmcia_device structure (defined in ds.h). You may not want to use a linked list for this -- for example, the - memory card driver uses an array of dev_link_t pointers, where minor + memory card driver uses an array of struct pcmcia_device pointers, where minor device numbers are used to derive the corresponding array index. */ @@ -102,7 +102,7 @@ static void teles_detach(struct pcmcia_device *p_dev); example, ethernet cards, modems). In other cases, there may be many actual or logical devices (SCSI adapters, memory cards with multiple partitions). The dev_node_t structures need to be kept - in a linked list starting at the 'dev' field of a dev_link_t + in a linked list starting at the 'dev' field of a struct pcmcia_device structure. We allocate them in the card's private data structure, because they generally shouldn't be allocated dynamically. In this case, we also provide a flag to indicate if a device is @@ -112,7 +112,7 @@ static void teles_detach(struct pcmcia_device *p_dev); */ typedef struct local_info_t { - dev_link_t link; + struct pcmcia_device *p_dev; dev_node_t node; int busy; int cardnr; @@ -130,9 +130,8 @@ typedef struct local_info_t { ======================================================================*/ -static int teles_attach(struct pcmcia_device *p_dev) +static int teles_probe(struct pcmcia_device *link) { - dev_link_t *link; local_info_t *local; DEBUG(0, "teles_attach()\n"); @@ -142,7 +141,9 @@ static int teles_attach(struct pcmcia_device *p_dev) if (!local) return -ENOMEM; memset(local, 0, sizeof(local_info_t)); local->cardnr = -1; - link = &local->link; link->priv = local; + + local->p_dev = link; + link->priv = local; /* Interrupt setup */ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED; @@ -161,16 +162,9 @@ static int teles_attach(struct pcmcia_device *p_dev) link->io.IOAddrLines = 5; link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - teles_cs_config(link); - - return 0; + return teles_cs_config(link); } /* teles_attach */ /*====================================================================== @@ -182,20 +176,16 @@ static int teles_attach(struct pcmcia_device *p_dev) ======================================================================*/ -static void teles_detach(struct pcmcia_device *p_dev) +static void teles_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); - local_info_t *info = link->priv; + local_info_t *info = link->priv; - DEBUG(0, "teles_detach(0x%p)\n", link); + DEBUG(0, "teles_detach(0x%p)\n", link); - if (link->state & DEV_CONFIG) { - info->busy = 1; - teles_cs_release(link); - } - - kfree(info); + info->busy = 1; + teles_cs_release(link); + kfree(info); } /* teles_detach */ /*====================================================================== @@ -205,7 +195,7 @@ static void teles_detach(struct pcmcia_device *p_dev) device available to the system. ======================================================================*/ -static int get_tuple(client_handle_t handle, tuple_t *tuple, +static int get_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { int i = pcmcia_get_tuple_data(handle, tuple); @@ -213,7 +203,7 @@ static int get_tuple(client_handle_t handle, tuple_t *tuple, return pcmcia_parse_tuple(handle, tuple, parse); } -static int first_tuple(client_handle_t handle, tuple_t *tuple, +static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { int i = pcmcia_get_first_tuple(handle, tuple); @@ -221,7 +211,7 @@ static int first_tuple(client_handle_t handle, tuple_t *tuple, return get_tuple(handle, tuple, parse); } -static int next_tuple(client_handle_t handle, tuple_t *tuple, +static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { int i = pcmcia_get_next_tuple(handle, tuple); @@ -229,9 +219,8 @@ static int next_tuple(client_handle_t handle, tuple_t *tuple, return get_tuple(handle, tuple, parse); } -static void teles_cs_config(dev_link_t *link) +static int teles_cs_config(struct pcmcia_device *link) { - client_handle_t handle; tuple_t tuple; cisparse_t parse; local_info_t *dev; @@ -241,7 +230,6 @@ static void teles_cs_config(dev_link_t *link) IsdnCard_t icard; DEBUG(0, "teles_config(0x%p)\n", link); - handle = link->handle; dev = link->priv; /* @@ -253,7 +241,7 @@ static void teles_cs_config(dev_link_t *link) tuple.TupleDataMax = 255; tuple.TupleOffset = 0; tuple.Attributes = 0; - i = first_tuple(handle, &tuple, &parse); + i = first_tuple(link, &tuple, &parse); if (i != CS_SUCCESS) { last_fn = ParseTuple; goto cs_failed; @@ -261,32 +249,29 @@ static void teles_cs_config(dev_link_t *link) link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; - /* Configure card */ - link->state |= DEV_CONFIG; - tuple.TupleData = (cisdata_t *)buf; tuple.TupleOffset = 0; tuple.TupleDataMax = 255; tuple.Attributes = 0; tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - i = first_tuple(handle, &tuple, &parse); + i = first_tuple(link, &tuple, &parse); while (i == CS_SUCCESS) { if ( (cf->io.nwin > 0) && cf->io.win[0].base) { printk(KERN_INFO "(teles_cs: looks like the 96 model)\n"); link->conf.ConfigIndex = cf->index; link->io.BasePort1 = cf->io.win[0].base; - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) break; } else { printk(KERN_INFO "(teles_cs: looks like the 97 model)\n"); link->conf.ConfigIndex = cf->index; for (i = 0, j = 0x2f0; j > 0x100; j -= 0x10) { link->io.BasePort1 = j; - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) break; } break; } - i = next_tuple(handle, &tuple, &parse); + i = next_tuple(link, &tuple, &parse); } if (i != CS_SUCCESS) { @@ -294,14 +279,14 @@ static void teles_cs_config(dev_link_t *link) goto cs_failed; } - i = pcmcia_request_irq(link->handle, &link->irq); + i = pcmcia_request_irq(link, &link->irq); if (i != CS_SUCCESS) { link->irq.AssignedIRQ = 0; last_fn = RequestIRQ; goto cs_failed; } - i = pcmcia_request_configuration(link->handle, &link->conf); + i = pcmcia_request_configuration(link, &link->conf); if (i != CS_SUCCESS) { last_fn = RequestConfiguration; goto cs_failed; @@ -312,14 +297,11 @@ static void teles_cs_config(dev_link_t *link) sprintf(dev->node.dev_name, "teles"); dev->node.major = dev->node.minor = 0x0; - link->dev = &dev->node; + link->dev_node = &dev->node; /* Finally, report what we've done */ - printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d", - dev->node.dev_name, link->conf.ConfigIndex, - link->conf.Vcc/10, link->conf.Vcc%10); - if (link->conf.Vpp1) - printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10); + printk(KERN_INFO "%s: index 0x%02x:", + dev->node.dev_name, link->conf.ConfigIndex); if (link->conf.Attributes & CONF_ENABLE_IRQ) printk(", irq %d", link->irq.AssignedIRQ); if (link->io.NumPorts1) @@ -330,8 +312,6 @@ static void teles_cs_config(dev_link_t *link) link->io.BasePort2+link->io.NumPorts2-1); printk("\n"); - link->state &= ~DEV_CONFIG_PENDING; - icard.para[0] = link->irq.AssignedIRQ; icard.para[1] = link->io.BasePort1; icard.protocol = protocol; @@ -342,13 +322,16 @@ static void teles_cs_config(dev_link_t *link) printk(KERN_ERR "teles_cs: failed to initialize Teles PCMCIA %d at i/o %#x\n", i, link->io.BasePort1); teles_cs_release(link); - } else - ((local_info_t*)link->priv)->cardnr = i; + return -ENODEV; + } + + ((local_info_t*)link->priv)->cardnr = i; + return 0; - return; cs_failed: - cs_error(link->handle, last_fn, i); + cs_error(link, last_fn, i); teles_cs_release(link); + return -ENODEV; } /* teles_cs_config */ /*====================================================================== @@ -359,7 +342,7 @@ cs_failed: ======================================================================*/ -static void teles_cs_release(dev_link_t *link) +static void teles_cs_release(struct pcmcia_device *link) { local_info_t *local = link->priv; @@ -371,39 +354,23 @@ static void teles_cs_release(dev_link_t *link) HiSax_closecard(local->cardnr); } } - /* Unlink the device chain */ - link->dev = NULL; - /* Don't bother checking to see if these succeed or not */ - if (link->win) - pcmcia_release_window(link->win); - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - link->state &= ~DEV_CONFIG; + pcmcia_disable_device(link); } /* teles_cs_release */ -static int teles_suspend(struct pcmcia_device *p_dev) +static int teles_suspend(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); local_info_t *dev = link->priv; - link->state |= DEV_SUSPEND; dev->busy = 1; - if (link->state & DEV_CONFIG) - pcmcia_release_configuration(link->handle); return 0; } -static int teles_resume(struct pcmcia_device *p_dev) +static int teles_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); local_info_t *dev = link->priv; - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) - pcmcia_request_configuration(link->handle, &link->conf); dev->busy = 0; return 0; @@ -421,7 +388,7 @@ static struct pcmcia_driver teles_cs_driver = { .drv = { .name = "teles_cs", }, - .probe = teles_attach, + .probe = teles_probe, .remove = teles_detach, .id_table = teles_ids, .suspend = teles_suspend, diff --git a/drivers/isdn/sc/ioctl.c b/drivers/isdn/sc/ioctl.c index 94c9afb7017c..f4f71226a078 100644 --- a/drivers/isdn/sc/ioctl.c +++ b/drivers/isdn/sc/ioctl.c @@ -46,7 +46,8 @@ int sc_ioctl(int card, scs_ioctl *data) pr_debug("%s: SCIOCRESET: ioctl received\n", sc_adapter[card]->devicename); sc_adapter[card]->StartOnReset = 0; - return (reset(card)); + kfree(rcvmsg); + return reset(card); } case SCIOCLOAD: @@ -183,7 +184,7 @@ int sc_ioctl(int card, scs_ioctl *data) sc_adapter[card]->devicename); spid = kmalloc(SCIOC_SPIDSIZE, GFP_KERNEL); - if(!spid) { + if (!spid) { kfree(rcvmsg); return -ENOMEM; } @@ -195,10 +196,10 @@ int sc_ioctl(int card, scs_ioctl *data) if (!status) { pr_debug("%s: SCIOCGETSPID: command successful\n", sc_adapter[card]->devicename); - } - else { + } else { pr_debug("%s: SCIOCGETSPID: command failed (status = %d)\n", sc_adapter[card]->devicename, status); + kfree(spid); kfree(rcvmsg); return status; } diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig new file mode 100644 index 000000000000..3f5b64794542 --- /dev/null +++ b/drivers/leds/Kconfig @@ -0,0 +1,88 @@ + +menu "LED devices" + +config NEW_LEDS + bool "LED Support" + help + Say Y to enable Linux LED support. This is not related to standard + keyboard LEDs which are controlled via the input system. + +config LEDS_CLASS + tristate "LED Class Support" + depends NEW_LEDS + help + This option enables the led sysfs class in /sys/class/leds. You'll + need this to do anything useful with LEDs. If unsure, say N. + +comment "LED drivers" + +config LEDS_CORGI + tristate "LED Support for the Sharp SL-C7x0 series" + depends LEDS_CLASS && PXA_SHARP_C7xx + help + This option enables support for the LEDs on Sharp Zaurus + SL-C7x0 series (C700, C750, C760, C860). + +config LEDS_LOCOMO + tristate "LED Support for Locomo device" + depends LEDS_CLASS && SHARP_LOCOMO + help + This option enables support for the LEDs on Sharp Locomo. + Zaurus models SL-5500 and SL-5600. + +config LEDS_SPITZ + tristate "LED Support for the Sharp SL-Cxx00 series" + depends LEDS_CLASS && PXA_SHARP_Cxx00 + help + This option enables support for the LEDs on Sharp Zaurus + SL-Cxx00 series (C1000, C3000, C3100). + +config LEDS_IXP4XX + tristate "LED Support for GPIO connected LEDs on IXP4XX processors" + depends LEDS_CLASS && ARCH_IXP4XX + help + This option enables support for the LEDs connected to GPIO + outputs of the Intel IXP4XX processors. To be useful the + particular board must have LEDs and they must be connected + to the GPIO lines. If unsure, say Y. + +config LEDS_TOSA + tristate "LED Support for the Sharp SL-6000 series" + depends LEDS_CLASS && PXA_SHARPSL + help + This option enables support for the LEDs on Sharp Zaurus + SL-6000 series. + +config LEDS_S3C24XX + tristate "LED Support for Samsung S3C24XX GPIO LEDs" + depends on LEDS_CLASS && ARCH_S3C2410 + help + This option enables support for LEDs connected to GPIO lines + on Samsung S3C24XX series CPUs, such as the S3C2410 and S3C2440. + +comment "LED Triggers" + +config LEDS_TRIGGERS + bool "LED Trigger support" + depends NEW_LEDS + help + This option enables trigger support for the leds class. + These triggers allow kernel events to drive the LEDs and can + be configured via sysfs. If unsure, say Y. + +config LEDS_TRIGGER_TIMER + tristate "LED Timer Trigger" + depends LEDS_TRIGGERS + help + This allows LEDs to be controlled by a programmable timer + via sysfs. If unsure, say Y. + +config LEDS_TRIGGER_IDE_DISK + bool "LED IDE Disk Trigger" + depends LEDS_TRIGGERS && BLK_DEV_IDEDISK + help + This allows LEDs to be controlled by IDE disk activity. + If unsure, say Y. + +endmenu + diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile new file mode 100644 index 000000000000..40f042633bf5 --- /dev/null +++ b/drivers/leds/Makefile @@ -0,0 +1,17 @@ + +# LED Core +obj-$(CONFIG_NEW_LEDS) += led-core.o +obj-$(CONFIG_LEDS_CLASS) += led-class.o +obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o + +# LED Platform Drivers +obj-$(CONFIG_LEDS_CORGI) += leds-corgi.o +obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o +obj-$(CONFIG_LEDS_SPITZ) += leds-spitz.o +obj-$(CONFIG_LEDS_IXP4XX) += leds-ixp4xx-gpio.o +obj-$(CONFIG_LEDS_TOSA) += leds-tosa.o +obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o + +# LED Triggers +obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o +obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c new file mode 100644 index 000000000000..b0b5d05fadd6 --- /dev/null +++ b/drivers/leds/led-class.c @@ -0,0 +1,167 @@ +/* + * LED Class Core + * + * Copyright (C) 2005 John Lenz + * Copyright (C) 2005-2006 Richard Purdie + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "leds.h" + +static struct class *leds_class; + +static ssize_t led_brightness_show(struct class_device *dev, char *buf) +{ + struct led_classdev *led_cdev = class_get_devdata(dev); + ssize_t ret = 0; + + /* no lock needed for this */ + sprintf(buf, "%u\n", led_cdev->brightness); + ret = strlen(buf) + 1; + + return ret; +} + +static ssize_t led_brightness_store(struct class_device *dev, + const char *buf, size_t size) +{ + struct led_classdev *led_cdev = class_get_devdata(dev); + ssize_t ret = -EINVAL; + char *after; + unsigned long state = simple_strtoul(buf, &after, 10); + + if (after - buf > 0) { + ret = after - buf; + led_set_brightness(led_cdev, state); + } + + return ret; +} + +static CLASS_DEVICE_ATTR(brightness, 0644, led_brightness_show, + led_brightness_store); +#ifdef CONFIG_LEDS_TRIGGERS +static CLASS_DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store); +#endif + +/** + * led_classdev_suspend - suspend an led_classdev. + * @led_cdev: the led_classdev to suspend. + */ +void led_classdev_suspend(struct led_classdev *led_cdev) +{ + led_cdev->flags |= LED_SUSPENDED; + led_cdev->brightness_set(led_cdev, 0); +} +EXPORT_SYMBOL_GPL(led_classdev_suspend); + +/** + * led_classdev_resume - resume an led_classdev. + * @led_cdev: the led_classdev to resume. + */ +void led_classdev_resume(struct led_classdev *led_cdev) +{ + led_cdev->brightness_set(led_cdev, led_cdev->brightness); + led_cdev->flags &= ~LED_SUSPENDED; +} +EXPORT_SYMBOL_GPL(led_classdev_resume); + +/** + * led_classdev_register - register a new object of led_classdev class. + * @dev: The device to register. + * @led_cdev: the led_classdev structure for this device. + */ +int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) +{ + led_cdev->class_dev = class_device_create(leds_class, NULL, 0, + parent, "%s", led_cdev->name); + if (unlikely(IS_ERR(led_cdev->class_dev))) + return PTR_ERR(led_cdev->class_dev); + + class_set_devdata(led_cdev->class_dev, led_cdev); + + /* register the attributes */ + class_device_create_file(led_cdev->class_dev, + &class_device_attr_brightness); + + /* add to the list of leds */ + write_lock(&leds_list_lock); + list_add_tail(&led_cdev->node, &leds_list); + write_unlock(&leds_list_lock); + +#ifdef CONFIG_LEDS_TRIGGERS + rwlock_init(&led_cdev->trigger_lock); + + led_trigger_set_default(led_cdev); + + class_device_create_file(led_cdev->class_dev, + &class_device_attr_trigger); +#endif + + printk(KERN_INFO "Registered led device: %s\n", + led_cdev->class_dev->class_id); + + return 0; +} +EXPORT_SYMBOL_GPL(led_classdev_register); + +/** + * led_classdev_unregister - unregisters a object of led_properties class. + * @led_cdev: the led device to unreigister + * + * Unregisters a previously registered via led_classdev_register object. + */ +void led_classdev_unregister(struct led_classdev *led_cdev) +{ + class_device_remove_file(led_cdev->class_dev, + &class_device_attr_brightness); +#ifdef CONFIG_LEDS_TRIGGERS + class_device_remove_file(led_cdev->class_dev, + &class_device_attr_trigger); + write_lock(&led_cdev->trigger_lock); + if (led_cdev->trigger) + led_trigger_set(led_cdev, NULL); + write_unlock(&led_cdev->trigger_lock); +#endif + + class_device_unregister(led_cdev->class_dev); + + write_lock(&leds_list_lock); + list_del(&led_cdev->node); + write_unlock(&leds_list_lock); +} +EXPORT_SYMBOL_GPL(led_classdev_unregister); + +static int __init leds_init(void) +{ + leds_class = class_create(THIS_MODULE, "leds"); + if (IS_ERR(leds_class)) + return PTR_ERR(leds_class); + return 0; +} + +static void __exit leds_exit(void) +{ + class_destroy(leds_class); +} + +subsys_initcall(leds_init); +module_exit(leds_exit); + +MODULE_AUTHOR("John Lenz, Richard Purdie"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("LED Class Interface"); diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c new file mode 100644 index 000000000000..fe6541326c71 --- /dev/null +++ b/drivers/leds/led-core.c @@ -0,0 +1,25 @@ +/* + * LED Class Core + * + * Copyright 2005-2006 Openedhand Ltd. + * + * Author: Richard Purdie + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include "leds.h" + +rwlock_t leds_list_lock = RW_LOCK_UNLOCKED; +LIST_HEAD(leds_list); + +EXPORT_SYMBOL_GPL(leds_list); +EXPORT_SYMBOL_GPL(leds_list_lock); diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c new file mode 100644 index 000000000000..5e2cd8be1191 --- /dev/null +++ b/drivers/leds/led-triggers.c @@ -0,0 +1,239 @@ +/* + * LED Triggers Core + * + * Copyright 2005-2006 Openedhand Ltd. + * + * Author: Richard Purdie + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "leds.h" + +/* + * Nests outside led_cdev->trigger_lock + */ +static rwlock_t triggers_list_lock = RW_LOCK_UNLOCKED; +static LIST_HEAD(trigger_list); + +ssize_t led_trigger_store(struct class_device *dev, const char *buf, + size_t count) +{ + struct led_classdev *led_cdev = class_get_devdata(dev); + char trigger_name[TRIG_NAME_MAX]; + struct led_trigger *trig; + size_t len; + + trigger_name[sizeof(trigger_name) - 1] = '\0'; + strncpy(trigger_name, buf, sizeof(trigger_name) - 1); + len = strlen(trigger_name); + + if (len && trigger_name[len - 1] == '\n') + trigger_name[len - 1] = '\0'; + + if (!strcmp(trigger_name, "none")) { + write_lock(&led_cdev->trigger_lock); + led_trigger_set(led_cdev, NULL); + write_unlock(&led_cdev->trigger_lock); + return count; + } + + read_lock(&triggers_list_lock); + list_for_each_entry(trig, &trigger_list, next_trig) { + if (!strcmp(trigger_name, trig->name)) { + write_lock(&led_cdev->trigger_lock); + led_trigger_set(led_cdev, trig); + write_unlock(&led_cdev->trigger_lock); + + read_unlock(&triggers_list_lock); + return count; + } + } + read_unlock(&triggers_list_lock); + + return -EINVAL; +} + + +ssize_t led_trigger_show(struct class_device *dev, char *buf) +{ + struct led_classdev *led_cdev = class_get_devdata(dev); + struct led_trigger *trig; + int len = 0; + + read_lock(&triggers_list_lock); + read_lock(&led_cdev->trigger_lock); + + if (!led_cdev->trigger) + len += sprintf(buf+len, "[none] "); + else + len += sprintf(buf+len, "none "); + + list_for_each_entry(trig, &trigger_list, next_trig) { + if (led_cdev->trigger && !strcmp(led_cdev->trigger->name, + trig->name)) + len += sprintf(buf+len, "[%s] ", trig->name); + else + len += sprintf(buf+len, "%s ", trig->name); + } + read_unlock(&led_cdev->trigger_lock); + read_unlock(&triggers_list_lock); + + len += sprintf(len+buf, "\n"); + return len; +} + +void led_trigger_event(struct led_trigger *trigger, + enum led_brightness brightness) +{ + struct list_head *entry; + + if (!trigger) + return; + + read_lock(&trigger->leddev_list_lock); + list_for_each(entry, &trigger->led_cdevs) { + struct led_classdev *led_cdev; + + led_cdev = list_entry(entry, struct led_classdev, trig_list); + led_set_brightness(led_cdev, brightness); + } + read_unlock(&trigger->leddev_list_lock); +} + +/* Caller must ensure led_cdev->trigger_lock held */ +void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger) +{ + unsigned long flags; + + /* Remove any existing trigger */ + if (led_cdev->trigger) { + write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags); + list_del(&led_cdev->trig_list); + write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock, flags); + if (led_cdev->trigger->deactivate) + led_cdev->trigger->deactivate(led_cdev); + } + if (trigger) { + write_lock_irqsave(&trigger->leddev_list_lock, flags); + list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs); + write_unlock_irqrestore(&trigger->leddev_list_lock, flags); + if (trigger->activate) + trigger->activate(led_cdev); + } + led_cdev->trigger = trigger; +} + +void led_trigger_set_default(struct led_classdev *led_cdev) +{ + struct led_trigger *trig; + + if (!led_cdev->default_trigger) + return; + + read_lock(&triggers_list_lock); + write_lock(&led_cdev->trigger_lock); + list_for_each_entry(trig, &trigger_list, next_trig) { + if (!strcmp(led_cdev->default_trigger, trig->name)) + led_trigger_set(led_cdev, trig); + } + write_unlock(&led_cdev->trigger_lock); + read_unlock(&triggers_list_lock); +} + +int led_trigger_register(struct led_trigger *trigger) +{ + struct led_classdev *led_cdev; + + rwlock_init(&trigger->leddev_list_lock); + INIT_LIST_HEAD(&trigger->led_cdevs); + + /* Add to the list of led triggers */ + write_lock(&triggers_list_lock); + list_add_tail(&trigger->next_trig, &trigger_list); + write_unlock(&triggers_list_lock); + + /* Register with any LEDs that have this as a default trigger */ + read_lock(&leds_list_lock); + list_for_each_entry(led_cdev, &leds_list, node) { + write_lock(&led_cdev->trigger_lock); + if (!led_cdev->trigger && led_cdev->default_trigger && + !strcmp(led_cdev->default_trigger, trigger->name)) + led_trigger_set(led_cdev, trigger); + write_unlock(&led_cdev->trigger_lock); + } + read_unlock(&leds_list_lock); + + return 0; +} + +void led_trigger_register_simple(const char *name, struct led_trigger **tp) +{ + struct led_trigger *trigger; + + trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL); + + if (trigger) { + trigger->name = name; + led_trigger_register(trigger); + } + *tp = trigger; +} + +void led_trigger_unregister(struct led_trigger *trigger) +{ + struct led_classdev *led_cdev; + + /* Remove from the list of led triggers */ + write_lock(&triggers_list_lock); + list_del(&trigger->next_trig); + write_unlock(&triggers_list_lock); + + /* Remove anyone actively using this trigger */ + read_lock(&leds_list_lock); + list_for_each_entry(led_cdev, &leds_list, node) { + write_lock(&led_cdev->trigger_lock); + if (led_cdev->trigger == trigger) + led_trigger_set(led_cdev, NULL); + write_unlock(&led_cdev->trigger_lock); + } + read_unlock(&leds_list_lock); +} + +void led_trigger_unregister_simple(struct led_trigger *trigger) +{ + led_trigger_unregister(trigger); + kfree(trigger); +} + +/* Used by LED Class */ +EXPORT_SYMBOL_GPL(led_trigger_set); +EXPORT_SYMBOL_GPL(led_trigger_set_default); +EXPORT_SYMBOL_GPL(led_trigger_show); +EXPORT_SYMBOL_GPL(led_trigger_store); + +/* LED Trigger Interface */ +EXPORT_SYMBOL_GPL(led_trigger_register); +EXPORT_SYMBOL_GPL(led_trigger_unregister); + +/* Simple LED Tigger Interface */ +EXPORT_SYMBOL_GPL(led_trigger_register_simple); +EXPORT_SYMBOL_GPL(led_trigger_unregister_simple); +EXPORT_SYMBOL_GPL(led_trigger_event); + +MODULE_AUTHOR("Richard Purdie"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("LED Triggers Core"); diff --git a/drivers/leds/leds-corgi.c b/drivers/leds/leds-corgi.c new file mode 100644 index 000000000000..bb7d84df0121 --- /dev/null +++ b/drivers/leds/leds-corgi.c @@ -0,0 +1,121 @@ +/* + * LED Triggers Core + * + * Copyright 2005-2006 Openedhand Ltd. + * + * Author: Richard Purdie + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void corgiled_amber_set(struct led_classdev *led_cdev, enum led_brightness value) +{ + if (value) + GPSR0 = GPIO_bit(CORGI_GPIO_LED_ORANGE); + else + GPCR0 = GPIO_bit(CORGI_GPIO_LED_ORANGE); +} + +static void corgiled_green_set(struct led_classdev *led_cdev, enum led_brightness value) +{ + if (value) + set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_LED_GREEN); + else + reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_LED_GREEN); +} + +static struct led_classdev corgi_amber_led = { + .name = "corgi:amber", + .default_trigger = "sharpsl-charge", + .brightness_set = corgiled_amber_set, +}; + +static struct led_classdev corgi_green_led = { + .name = "corgi:green", + .default_trigger = "nand-disk", + .brightness_set = corgiled_green_set, +}; + +#ifdef CONFIG_PM +static int corgiled_suspend(struct platform_device *dev, pm_message_t state) +{ +#ifdef CONFIG_LEDS_TRIGGERS + if (corgi_amber_led.trigger && strcmp(corgi_amber_led.trigger->name, "sharpsl-charge")) +#endif + led_classdev_suspend(&corgi_amber_led); + led_classdev_suspend(&corgi_green_led); + return 0; +} + +static int corgiled_resume(struct platform_device *dev) +{ + led_classdev_resume(&corgi_amber_led); + led_classdev_resume(&corgi_green_led); + return 0; +} +#endif + +static int corgiled_probe(struct platform_device *pdev) +{ + int ret; + + ret = led_classdev_register(&pdev->dev, &corgi_amber_led); + if (ret < 0) + return ret; + + ret = led_classdev_register(&pdev->dev, &corgi_green_led); + if (ret < 0) + led_classdev_unregister(&corgi_amber_led); + + return ret; +} + +static int corgiled_remove(struct platform_device *pdev) +{ + led_classdev_unregister(&corgi_amber_led); + led_classdev_unregister(&corgi_green_led); + return 0; +} + +static struct platform_driver corgiled_driver = { + .probe = corgiled_probe, + .remove = corgiled_remove, +#ifdef CONFIG_PM + .suspend = corgiled_suspend, + .resume = corgiled_resume, +#endif + .driver = { + .name = "corgi-led", + }, +}; + +static int __init corgiled_init(void) +{ + return platform_driver_register(&corgiled_driver); +} + +static void __exit corgiled_exit(void) +{ + platform_driver_unregister(&corgiled_driver); +} + +module_init(corgiled_init); +module_exit(corgiled_exit); + +MODULE_AUTHOR("Richard Purdie "); +MODULE_DESCRIPTION("Corgi LED driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-ixp4xx-gpio.c b/drivers/leds/leds-ixp4xx-gpio.c new file mode 100644 index 000000000000..30ced150e4cf --- /dev/null +++ b/drivers/leds/leds-ixp4xx-gpio.c @@ -0,0 +1,215 @@ +/* + * IXP4XX GPIO driver LED driver + * + * Author: John Bowler + * + * Copyright (c) 2006 John Bowler + * + * Permission is hereby granted, free of charge, to any + * person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the + * Software without restriction, including without + * limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice + * shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF + * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT + * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +extern spinlock_t gpio_lock; + +/* Up to 16 gpio lines are possible. */ +#define GPIO_MAX 16 +static struct ixp4xxgpioled_device { + struct led_classdev ancestor; + int flags; +} ixp4xxgpioled_devices[GPIO_MAX]; + +void ixp4xxgpioled_brightness_set(struct led_classdev *pled, + enum led_brightness value) +{ + const struct ixp4xxgpioled_device *const ixp4xx_dev = + container_of(pled, struct ixp4xxgpioled_device, ancestor); + const u32 gpio_pin = ixp4xx_dev - ixp4xxgpioled_devices; + + if (gpio_pin < GPIO_MAX && ixp4xx_dev->ancestor.name != 0) { + /* Set or clear the 'gpio_pin' bit according to the style + * and the required setting (value > 0 == on) + */ + const int gpio_value = + (value > 0) == (ixp4xx_dev->flags != IXP4XX_GPIO_LOW) ? + IXP4XX_GPIO_HIGH : IXP4XX_GPIO_LOW; + + { + unsigned long flags; + spin_lock_irqsave(&gpio_lock, flags); + gpio_line_set(gpio_pin, gpio_value); + spin_unlock_irqrestore(&gpio_lock, flags); + } + } +} + +/* LEDs are described in resources, the following iterates over the valid + * LED resources. + */ +#define for_all_leds(i, pdev) \ + for (i=0; inum_resources; ++i) \ + if (pdev->resource[i].start < GPIO_MAX && \ + pdev->resource[i].name != 0) + +/* The following applies 'operation' to each LED from the given platform, + * the function always returns 0 to allow tail call elimination. + */ +static int apply_to_all_leds(struct platform_device *pdev, + void (*operation)(struct led_classdev *pled)) +{ + int i; + + for_all_leds(i, pdev) + operation(&ixp4xxgpioled_devices[pdev->resource[i].start].ancestor); + return 0; +} + +#ifdef CONFIG_PM +static int ixp4xxgpioled_suspend(struct platform_device *pdev, + pm_message_t state) +{ + return apply_to_all_leds(pdev, led_classdev_suspend); +} + +static int ixp4xxgpioled_resume(struct platform_device *pdev) +{ + return apply_to_all_leds(pdev, led_classdev_resume); +} +#endif + +static void ixp4xxgpioled_remove_one_led(struct led_classdev *pled) +{ + led_classdev_unregister(pled); + pled->name = 0; +} + +static int ixp4xxgpioled_remove(struct platform_device *pdev) +{ + return apply_to_all_leds(pdev, ixp4xxgpioled_remove_one_led); +} + +static int ixp4xxgpioled_probe(struct platform_device *pdev) +{ + /* The board level has to tell the driver where the + * LEDs are connected - there is no way to find out + * electrically. It must also say whether the GPIO + * lines are active high or active low. + * + * To do this read the num_resources (the number of + * LEDs) and the struct resource (the data for each + * LED). The name comes from the resource, and it + * isn't copied. + */ + int i; + + for_all_leds(i, pdev) { + const u8 gpio_pin = pdev->resource[i].start; + int rc; + + if (ixp4xxgpioled_devices[gpio_pin].ancestor.name == 0) { + unsigned long flags; + + spin_lock_irqsave(&gpio_lock, flags); + gpio_line_config(gpio_pin, IXP4XX_GPIO_OUT); + /* The config can, apparently, reset the state, + * I suspect the gpio line may be an input and + * the config may cause the line to be latched, + * so the setting depends on how the LED is + * connected to the line (which affects how it + * floats if not driven). + */ + gpio_line_set(gpio_pin, IXP4XX_GPIO_HIGH); + spin_unlock_irqrestore(&gpio_lock, flags); + + ixp4xxgpioled_devices[gpio_pin].flags = + pdev->resource[i].flags & IORESOURCE_BITS; + + ixp4xxgpioled_devices[gpio_pin].ancestor.name = + pdev->resource[i].name; + + /* This is how a board manufacturer makes the LED + * come on on reset - the GPIO line will be high, so + * make the LED light when the line is low... + */ + if (ixp4xxgpioled_devices[gpio_pin].flags != IXP4XX_GPIO_LOW) + ixp4xxgpioled_devices[gpio_pin].ancestor.brightness = 100; + else + ixp4xxgpioled_devices[gpio_pin].ancestor.brightness = 0; + + ixp4xxgpioled_devices[gpio_pin].ancestor.flags = 0; + + ixp4xxgpioled_devices[gpio_pin].ancestor.brightness_set = + ixp4xxgpioled_brightness_set; + + ixp4xxgpioled_devices[gpio_pin].ancestor.default_trigger = 0; + } + + rc = led_classdev_register(&pdev->dev, + &ixp4xxgpioled_devices[gpio_pin].ancestor); + if (rc < 0) { + ixp4xxgpioled_devices[gpio_pin].ancestor.name = 0; + ixp4xxgpioled_remove(pdev); + return rc; + } + } + + return 0; +} + +static struct platform_driver ixp4xxgpioled_driver = { + .probe = ixp4xxgpioled_probe, + .remove = ixp4xxgpioled_remove, +#ifdef CONFIG_PM + .suspend = ixp4xxgpioled_suspend, + .resume = ixp4xxgpioled_resume, +#endif + .driver = { + .name = "IXP4XX-GPIO-LED", + }, +}; + +static int __init ixp4xxgpioled_init(void) +{ + return platform_driver_register(&ixp4xxgpioled_driver); +} + +static void __exit ixp4xxgpioled_exit(void) +{ + platform_driver_unregister(&ixp4xxgpioled_driver); +} + +module_init(ixp4xxgpioled_init); +module_exit(ixp4xxgpioled_exit); + +MODULE_AUTHOR("John Bowler "); +MODULE_DESCRIPTION("IXP4XX GPIO LED driver"); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/leds/leds-locomo.c b/drivers/leds/leds-locomo.c new file mode 100644 index 000000000000..749a86c2adb6 --- /dev/null +++ b/drivers/leds/leds-locomo.c @@ -0,0 +1,95 @@ +/* + * linux/drivers/leds/locomo.c + * + * Copyright (C) 2005 John Lenz + * + * 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 +#include + +#include +#include + +static void locomoled_brightness_set(struct led_classdev *led_cdev, + enum led_brightness value, int offset) +{ + struct locomo_dev *locomo_dev = LOCOMO_DEV(led_cdev->class_dev->dev); + unsigned long flags; + + local_irq_save(flags); + if (value) + locomo_writel(LOCOMO_LPT_TOFH, locomo_dev->mapbase + offset); + else + locomo_writel(LOCOMO_LPT_TOFL, locomo_dev->mapbase + offset); + local_irq_restore(flags); +} + +static void locomoled_brightness_set0(struct led_classdev *led_cdev, + enum led_brightness value) +{ + locomoled_brightness_set(led_cdev, value, LOCOMO_LPT0); +} + +static void locomoled_brightness_set1(struct led_classdev *led_cdev, + enum led_brightness value) +{ + locomoled_brightness_set(led_cdev, value, LOCOMO_LPT1); +} + +static struct led_classdev locomo_led0 = { + .name = "locomo:amber", + .brightness_set = locomoled_brightness_set0, +}; + +static struct led_classdev locomo_led1 = { + .name = "locomo:green", + .brightness_set = locomoled_brightness_set1, +}; + +static int locomoled_probe(struct locomo_dev *ldev) +{ + int ret; + + ret = led_classdev_register(&ldev->dev, &locomo_led0); + if (ret < 0) + return ret; + + ret = led_classdev_register(&ldev->dev, &locomo_led1); + if (ret < 0) + led_classdev_unregister(&locomo_led0); + + return ret; +} + +static int locomoled_remove(struct locomo_dev *dev) +{ + led_classdev_unregister(&locomo_led0); + led_classdev_unregister(&locomo_led1); + return 0; +} + +static struct locomo_driver locomoled_driver = { + .drv = { + .name = "locomoled" + }, + .devid = LOCOMO_DEVID_LED, + .probe = locomoled_probe, + .remove = locomoled_remove, +}; + +static int __init locomoled_init(void) +{ + return locomo_driver_register(&locomoled_driver); +} +module_init(locomoled_init); + +MODULE_AUTHOR("John Lenz "); +MODULE_DESCRIPTION("Locomo LED driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-s3c24xx.c b/drivers/leds/leds-s3c24xx.c new file mode 100644 index 000000000000..650cf72dc675 --- /dev/null +++ b/drivers/leds/leds-s3c24xx.c @@ -0,0 +1,163 @@ +/* drivers/leds/leds-s3c24xx.c + * + * (c) 2006 Simtec Electronics + * http://armlinux.simtec.co.uk/ + * Ben Dooks + * + * S3C24XX - LEDs GPIO driver + * + * 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 +#include + +#include +#include +#include + +/* our context */ + +struct s3c24xx_gpio_led { + struct led_classdev cdev; + struct s3c24xx_led_platdata *pdata; +}; + +static inline struct s3c24xx_gpio_led *pdev_to_gpio(struct platform_device *dev) +{ + return platform_get_drvdata(dev); +} + +static inline struct s3c24xx_gpio_led *to_gpio(struct led_classdev *led_cdev) +{ + return container_of(led_cdev, struct s3c24xx_gpio_led, cdev); +} + +static void s3c24xx_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct s3c24xx_gpio_led *led = to_gpio(led_cdev); + struct s3c24xx_led_platdata *pd = led->pdata; + + /* there will be a sort delay between setting the output and + * going from output to input when using tristate. */ + + s3c2410_gpio_setpin(pd->gpio, (value ? 1 : 0) ^ + (pd->flags & S3C24XX_LEDF_ACTLOW)); + + if (pd->flags & S3C24XX_LEDF_TRISTATE) + s3c2410_gpio_cfgpin(pd->gpio, + value ? S3C2410_GPIO_OUTPUT : S3C2410_GPIO_INPUT); + +} + +static int s3c24xx_led_remove(struct platform_device *dev) +{ + struct s3c24xx_gpio_led *led = pdev_to_gpio(dev); + + led_classdev_unregister(&led->cdev); + kfree(led); + + return 0; +} + +static int s3c24xx_led_probe(struct platform_device *dev) +{ + struct s3c24xx_led_platdata *pdata = dev->dev.platform_data; + struct s3c24xx_gpio_led *led; + int ret; + + led = kzalloc(sizeof(struct s3c24xx_gpio_led), GFP_KERNEL); + if (led == NULL) { + dev_err(&dev->dev, "No memory for device\n"); + return -ENOMEM; + } + + platform_set_drvdata(dev, led); + + led->cdev.brightness_set = s3c24xx_led_set; + led->cdev.default_trigger = pdata->def_trigger; + led->cdev.name = pdata->name; + + led->pdata = pdata; + + /* no point in having a pull-up if we are always driving */ + + if (pdata->flags & S3C24XX_LEDF_TRISTATE) { + s3c2410_gpio_setpin(pdata->gpio, 0); + s3c2410_gpio_cfgpin(pdata->gpio, S3C2410_GPIO_INPUT); + } else { + s3c2410_gpio_pullup(pdata->gpio, 0); + s3c2410_gpio_setpin(pdata->gpio, 0); + s3c2410_gpio_cfgpin(pdata->gpio, S3C2410_GPIO_OUTPUT); + } + + /* register our new led device */ + + ret = led_classdev_register(&dev->dev, &led->cdev); + if (ret < 0) { + dev_err(&dev->dev, "led_classdev_register failed\n"); + goto exit_err1; + } + + return 0; + + exit_err1: + kfree(led); + return ret; +} + + +#ifdef CONFIG_PM +static int s3c24xx_led_suspend(struct platform_device *dev, pm_message_t state) +{ + struct s3c24xx_gpio_led *led = pdev_to_gpio(dev); + + led_classdev_suspend(&led->cdev); + return 0; +} + +static int s3c24xx_led_resume(struct platform_device *dev) +{ + struct s3c24xx_gpio_led *led = pdev_to_gpio(dev); + + led_classdev_resume(&led->cdev); + return 0; +} +#else +#define s3c24xx_led_suspend NULL +#define s3c24xx_led_resume NULL +#endif + +static struct platform_driver s3c24xx_led_driver = { + .probe = s3c24xx_led_probe, + .remove = s3c24xx_led_remove, + .suspend = s3c24xx_led_suspend, + .resume = s3c24xx_led_resume, + .driver = { + .name = "s3c24xx_led", + .owner = THIS_MODULE, + }, +}; + +static int __init s3c24xx_led_init(void) +{ + return platform_driver_register(&s3c24xx_led_driver); +} + +static void __exit s3c24xx_led_exit(void) +{ + platform_driver_unregister(&s3c24xx_led_driver); +} + +module_init(s3c24xx_led_init); +module_exit(s3c24xx_led_exit); + +MODULE_AUTHOR("Ben Dooks "); +MODULE_DESCRIPTION("S3C24XX LED driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-spitz.c b/drivers/leds/leds-spitz.c new file mode 100644 index 000000000000..65bbef4a5e09 --- /dev/null +++ b/drivers/leds/leds-spitz.c @@ -0,0 +1,125 @@ +/* + * LED Triggers Core + * + * Copyright 2005-2006 Openedhand Ltd. + * + * Author: Richard Purdie + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void spitzled_amber_set(struct led_classdev *led_cdev, enum led_brightness value) +{ + if (value) + set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_LED_ORANGE); + else + reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_LED_ORANGE); +} + +static void spitzled_green_set(struct led_classdev *led_cdev, enum led_brightness value) +{ + if (value) + set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_LED_GREEN); + else + reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_LED_GREEN); +} + +static struct led_classdev spitz_amber_led = { + .name = "spitz:amber", + .default_trigger = "sharpsl-charge", + .brightness_set = spitzled_amber_set, +}; + +static struct led_classdev spitz_green_led = { + .name = "spitz:green", + .default_trigger = "ide-disk", + .brightness_set = spitzled_green_set, +}; + +#ifdef CONFIG_PM +static int spitzled_suspend(struct platform_device *dev, pm_message_t state) +{ +#ifdef CONFIG_LEDS_TRIGGERS + if (spitz_amber_led.trigger && strcmp(spitz_amber_led.trigger->name, "sharpsl-charge")) +#endif + led_classdev_suspend(&spitz_amber_led); + led_classdev_suspend(&spitz_green_led); + return 0; +} + +static int spitzled_resume(struct platform_device *dev) +{ + led_classdev_resume(&spitz_amber_led); + led_classdev_resume(&spitz_green_led); + return 0; +} +#endif + +static int spitzled_probe(struct platform_device *pdev) +{ + int ret; + + if (machine_is_akita()) + spitz_green_led.default_trigger = "nand-disk"; + + ret = led_classdev_register(&pdev->dev, &spitz_amber_led); + if (ret < 0) + return ret; + + ret = led_classdev_register(&pdev->dev, &spitz_green_led); + if (ret < 0) + led_classdev_unregister(&spitz_amber_led); + + return ret; +} + +static int spitzled_remove(struct platform_device *pdev) +{ + led_classdev_unregister(&spitz_amber_led); + led_classdev_unregister(&spitz_green_led); + + return 0; +} + +static struct platform_driver spitzled_driver = { + .probe = spitzled_probe, + .remove = spitzled_remove, +#ifdef CONFIG_PM + .suspend = spitzled_suspend, + .resume = spitzled_resume, +#endif + .driver = { + .name = "spitz-led", + }, +}; + +static int __init spitzled_init(void) +{ + return platform_driver_register(&spitzled_driver); +} + +static void __exit spitzled_exit(void) +{ + platform_driver_unregister(&spitzled_driver); +} + +module_init(spitzled_init); +module_exit(spitzled_exit); + +MODULE_AUTHOR("Richard Purdie "); +MODULE_DESCRIPTION("Spitz LED driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-tosa.c b/drivers/leds/leds-tosa.c new file mode 100644 index 000000000000..c9e8cc1ec481 --- /dev/null +++ b/drivers/leds/leds-tosa.c @@ -0,0 +1,131 @@ +/* + * LED Triggers Core + * + * Copyright 2005 Dirk Opfer + * + * Author: Dirk Opfer + * based on spitz.c + * + * 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 +#include +#include +#include +#include +#include +#include + +static void tosaled_amber_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + if (value) + set_scoop_gpio(&tosascoop_jc_device.dev, + TOSA_SCOOP_JC_CHRG_ERR_LED); + else + reset_scoop_gpio(&tosascoop_jc_device.dev, + TOSA_SCOOP_JC_CHRG_ERR_LED); +} + +static void tosaled_green_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + if (value) + set_scoop_gpio(&tosascoop_jc_device.dev, + TOSA_SCOOP_JC_NOTE_LED); + else + reset_scoop_gpio(&tosascoop_jc_device.dev, + TOSA_SCOOP_JC_NOTE_LED); +} + +static struct led_classdev tosa_amber_led = { + .name = "tosa:amber", + .default_trigger = "sharpsl-charge", + .brightness_set = tosaled_amber_set, +}; + +static struct led_classdev tosa_green_led = { + .name = "tosa:green", + .default_trigger = "nand-disk", + .brightness_set = tosaled_green_set, +}; + +#ifdef CONFIG_PM +static int tosaled_suspend(struct platform_device *dev, pm_message_t state) +{ +#ifdef CONFIG_LEDS_TRIGGERS + if (tosa_amber_led.trigger && strcmp(tosa_amber_led.trigger->name, + "sharpsl-charge")) +#endif + led_classdev_suspend(&tosa_amber_led); + led_classdev_suspend(&tosa_green_led); + return 0; +} + +static int tosaled_resume(struct platform_device *dev) +{ + led_classdev_resume(&tosa_amber_led); + led_classdev_resume(&tosa_green_led); + return 0; +} +#else +#define tosaled_suspend NULL +#define tosaled_resume NULL +#endif + +static int tosaled_probe(struct platform_device *pdev) +{ + int ret; + + ret = led_classdev_register(&pdev->dev, &tosa_amber_led); + if (ret < 0) + return ret; + + ret = led_classdev_register(&pdev->dev, &tosa_green_led); + if (ret < 0) + led_classdev_unregister(&tosa_amber_led); + + return ret; +} + +static int tosaled_remove(struct platform_device *pdev) +{ + led_classdev_unregister(&tosa_amber_led); + led_classdev_unregister(&tosa_green_led); + + return 0; +} + +static struct platform_driver tosaled_driver = { + .probe = tosaled_probe, + .remove = tosaled_remove, + .suspend = tosaled_suspend, + .resume = tosaled_resume, + .driver = { + .name = "tosa-led", + }, +}; + +static int __init tosaled_init(void) +{ + return platform_driver_register(&tosaled_driver); +} + +static void __exit tosaled_exit(void) +{ + platform_driver_unregister(&tosaled_driver); +} + +module_init(tosaled_init); +module_exit(tosaled_exit); + +MODULE_AUTHOR("Dirk Opfer "); +MODULE_DESCRIPTION("Tosa LED driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h new file mode 100644 index 000000000000..a715c4ed93ff --- /dev/null +++ b/drivers/leds/leds.h @@ -0,0 +1,44 @@ +/* + * LED Core + * + * Copyright 2005 Openedhand Ltd. + * + * Author: Richard Purdie + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#ifndef __LEDS_H_INCLUDED +#define __LEDS_H_INCLUDED + +#include + +static inline void led_set_brightness(struct led_classdev *led_cdev, + enum led_brightness value) +{ + if (value > LED_FULL) + value = LED_FULL; + led_cdev->brightness = value; + if (!(led_cdev->flags & LED_SUSPENDED)) + led_cdev->brightness_set(led_cdev, value); +} + +extern rwlock_t leds_list_lock; +extern struct list_head leds_list; + +#ifdef CONFIG_LEDS_TRIGGERS +void led_trigger_set_default(struct led_classdev *led_cdev); +void led_trigger_set(struct led_classdev *led_cdev, + struct led_trigger *trigger); +#else +#define led_trigger_set_default(x) do {} while(0) +#define led_trigger_set(x, y) do {} while(0) +#endif + +ssize_t led_trigger_store(struct class_device *dev, const char *buf, + size_t count); +ssize_t led_trigger_show(struct class_device *dev, char *buf); + +#endif /* __LEDS_H_INCLUDED */ diff --git a/drivers/leds/ledtrig-ide-disk.c b/drivers/leds/ledtrig-ide-disk.c new file mode 100644 index 000000000000..fa651886ab4f --- /dev/null +++ b/drivers/leds/ledtrig-ide-disk.c @@ -0,0 +1,62 @@ +/* + * LED IDE-Disk Activity Trigger + * + * Copyright 2006 Openedhand Ltd. + * + * Author: Richard Purdie + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include + +static void ledtrig_ide_timerfunc(unsigned long data); + +DEFINE_LED_TRIGGER(ledtrig_ide); +static DEFINE_TIMER(ledtrig_ide_timer, ledtrig_ide_timerfunc, 0, 0); +static int ide_activity; +static int ide_lastactivity; + +void ledtrig_ide_activity(void) +{ + ide_activity++; + if (!timer_pending(&ledtrig_ide_timer)) + mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10)); +} +EXPORT_SYMBOL(ledtrig_ide_activity); + +static void ledtrig_ide_timerfunc(unsigned long data) +{ + if (ide_lastactivity != ide_activity) { + ide_lastactivity = ide_activity; + led_trigger_event(ledtrig_ide, LED_FULL); + mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10)); + } else { + led_trigger_event(ledtrig_ide, LED_OFF); + } +} + +static int __init ledtrig_ide_init(void) +{ + led_trigger_register_simple("ide-disk", &ledtrig_ide); + return 0; +} + +static void __exit ledtrig_ide_exit(void) +{ + led_trigger_unregister_simple(ledtrig_ide); +} + +module_init(ledtrig_ide_init); +module_exit(ledtrig_ide_exit); + +MODULE_AUTHOR("Richard Purdie "); +MODULE_DESCRIPTION("LED IDE Disk Activity Trigger"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/ledtrig-timer.c b/drivers/leds/ledtrig-timer.c new file mode 100644 index 000000000000..f484b5d6dbf8 --- /dev/null +++ b/drivers/leds/ledtrig-timer.c @@ -0,0 +1,170 @@ +/* + * LED Kernel Timer Trigger + * + * Copyright 2005-2006 Openedhand Ltd. + * + * Author: Richard Purdie + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "leds.h" + +struct timer_trig_data { + unsigned long delay_on; /* milliseconds on */ + unsigned long delay_off; /* milliseconds off */ + struct timer_list timer; +}; + +static void led_timer_function(unsigned long data) +{ + struct led_classdev *led_cdev = (struct led_classdev *) data; + struct timer_trig_data *timer_data = led_cdev->trigger_data; + unsigned long brightness = LED_OFF; + unsigned long delay = timer_data->delay_off; + + if (!timer_data->delay_on || !timer_data->delay_off) { + led_set_brightness(led_cdev, LED_OFF); + return; + } + + if (!led_cdev->brightness) { + brightness = LED_FULL; + delay = timer_data->delay_on; + } + + led_set_brightness(led_cdev, brightness); + + mod_timer(&timer_data->timer, jiffies + msecs_to_jiffies(delay)); +} + +static ssize_t led_delay_on_show(struct class_device *dev, char *buf) +{ + struct led_classdev *led_cdev = class_get_devdata(dev); + struct timer_trig_data *timer_data = led_cdev->trigger_data; + + sprintf(buf, "%lu\n", timer_data->delay_on); + + return strlen(buf) + 1; +} + +static ssize_t led_delay_on_store(struct class_device *dev, const char *buf, + size_t size) +{ + struct led_classdev *led_cdev = class_get_devdata(dev); + struct timer_trig_data *timer_data = led_cdev->trigger_data; + int ret = -EINVAL; + char *after; + unsigned long state = simple_strtoul(buf, &after, 10); + + if (after - buf > 0) { + timer_data->delay_on = state; + mod_timer(&timer_data->timer, jiffies + 1); + ret = after - buf; + } + + return ret; +} + +static ssize_t led_delay_off_show(struct class_device *dev, char *buf) +{ + struct led_classdev *led_cdev = class_get_devdata(dev); + struct timer_trig_data *timer_data = led_cdev->trigger_data; + + sprintf(buf, "%lu\n", timer_data->delay_off); + + return strlen(buf) + 1; +} + +static ssize_t led_delay_off_store(struct class_device *dev, const char *buf, + size_t size) +{ + struct led_classdev *led_cdev = class_get_devdata(dev); + struct timer_trig_data *timer_data = led_cdev->trigger_data; + int ret = -EINVAL; + char *after; + unsigned long state = simple_strtoul(buf, &after, 10); + + if (after - buf > 0) { + timer_data->delay_off = state; + mod_timer(&timer_data->timer, jiffies + 1); + ret = after - buf; + } + + return ret; +} + +static CLASS_DEVICE_ATTR(delay_on, 0644, led_delay_on_show, + led_delay_on_store); +static CLASS_DEVICE_ATTR(delay_off, 0644, led_delay_off_show, + led_delay_off_store); + +static void timer_trig_activate(struct led_classdev *led_cdev) +{ + struct timer_trig_data *timer_data; + + timer_data = kzalloc(sizeof(struct timer_trig_data), GFP_KERNEL); + if (!timer_data) + return; + + led_cdev->trigger_data = timer_data; + + init_timer(&timer_data->timer); + timer_data->timer.function = led_timer_function; + timer_data->timer.data = (unsigned long) led_cdev; + + class_device_create_file(led_cdev->class_dev, + &class_device_attr_delay_on); + class_device_create_file(led_cdev->class_dev, + &class_device_attr_delay_off); +} + +static void timer_trig_deactivate(struct led_classdev *led_cdev) +{ + struct timer_trig_data *timer_data = led_cdev->trigger_data; + + if (timer_data) { + class_device_remove_file(led_cdev->class_dev, + &class_device_attr_delay_on); + class_device_remove_file(led_cdev->class_dev, + &class_device_attr_delay_off); + del_timer_sync(&timer_data->timer); + kfree(timer_data); + } +} + +static struct led_trigger timer_led_trigger = { + .name = "timer", + .activate = timer_trig_activate, + .deactivate = timer_trig_deactivate, +}; + +static int __init timer_trig_init(void) +{ + return led_trigger_register(&timer_led_trigger); +} + +static void __exit timer_trig_exit(void) +{ + led_trigger_unregister(&timer_led_trigger); +} + +module_init(timer_trig_init); +module_exit(timer_trig_exit); + +MODULE_AUTHOR("Richard Purdie "); +MODULE_DESCRIPTION("Timer LED trigger"); +MODULE_LICENSE("GPL"); diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index fd2aae150ccc..ac25a48362ac 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -139,11 +139,12 @@ config MD_RAID5_RESHAPE is online. However it is still EXPERIMENTAL code. It should work, but please be sure that you have backups. - You will need a version of mdadm newer than 2.3.1. During the - early stage of reshape there is a critical section where live data - is being over-written. A crash during this time needs extra care - for recovery. The newer mdadm takes a copy of the data in the - critical section and will restore it, if necessary, after a crash. + You will need mdadm verion 2.4.1 or later to use this + feature safely. During the early stage of reshape there is + a critical section where live data is being over-written. A + crash during this time needs extra care for recovery. The + newer mdadm takes a copy of the data in the critical section + and will restore it, if necessary, after a crash. The mdadm usage is e.g. mdadm --grow /dev/md1 --raid-disks=6 diff --git a/drivers/md/dm-target.c b/drivers/md/dm-target.c index aecd9e0c2616..64fd8e79ea4c 100644 --- a/drivers/md/dm-target.c +++ b/drivers/md/dm-target.c @@ -78,8 +78,7 @@ void dm_put_target_type(struct target_type *t) if (--ti->use == 0) module_put(ti->tt.module); - if (ti->use < 0) - BUG(); + BUG_ON(ti->use < 0); up_read(&_lock); return; diff --git a/drivers/md/md.c b/drivers/md/md.c index 039e071c1007..1ed5152db450 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -215,13 +215,11 @@ static void mddev_put(mddev_t *mddev) return; if (!mddev->raid_disks && list_empty(&mddev->disks)) { list_del(&mddev->all_mddevs); - /* that blocks */ + spin_unlock(&all_mddevs_lock); blk_cleanup_queue(mddev->queue); - /* that also blocks */ kobject_unregister(&mddev->kobj); - /* result blows... */ - } - spin_unlock(&all_mddevs_lock); + } else + spin_unlock(&all_mddevs_lock); } static mddev_t * mddev_find(dev_t unit) diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 3cb0872a845d..6081941de1b3 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1135,8 +1135,19 @@ static int end_sync_write(struct bio *bio, unsigned int bytes_done, int error) mirror = i; break; } - if (!uptodate) + if (!uptodate) { + int sync_blocks = 0; + sector_t s = r1_bio->sector; + long sectors_to_go = r1_bio->sectors; + /* make sure these bits doesn't get cleared. */ + do { + bitmap_end_sync(mddev->bitmap, r1_bio->sector, + &sync_blocks, 1); + s += sync_blocks; + sectors_to_go -= sync_blocks; + } while (sectors_to_go > 0); md_error(mddev, conf->mirrors[mirror].rdev); + } update_head_pos(mirror, r1_bio); @@ -1547,8 +1558,7 @@ static int init_resync(conf_t *conf) int buffs; buffs = RESYNC_WINDOW / RESYNC_BLOCK_SIZE; - if (conf->r1buf_pool) - BUG(); + BUG_ON(conf->r1buf_pool); conf->r1buf_pool = mempool_create(buffs, r1buf_pool_alloc, r1buf_pool_free, conf->poolinfo); if (!conf->r1buf_pool) @@ -1721,8 +1731,7 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i !conf->fullsync && !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) break; - if (sync_blocks < (PAGE_SIZE>>9)) - BUG(); + BUG_ON(sync_blocks < (PAGE_SIZE>>9)); if (len > (sync_blocks<<9)) len = sync_blocks<<9; } diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index ab90a6d12020..617012bc107a 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -1117,8 +1117,7 @@ static int end_sync_read(struct bio *bio, unsigned int bytes_done, int error) for (i=0; icopies; i++) if (r10_bio->devs[i].bio == bio) break; - if (i == conf->copies) - BUG(); + BUG_ON(i == conf->copies); update_head_pos(i, r10_bio); d = r10_bio->devs[i].devnum; @@ -1518,8 +1517,7 @@ static int init_resync(conf_t *conf) int buffs; buffs = RESYNC_WINDOW / RESYNC_BLOCK_SIZE; - if (conf->r10buf_pool) - BUG(); + BUG_ON(conf->r10buf_pool); conf->r10buf_pool = mempool_create(buffs, r10buf_pool_alloc, r10buf_pool_free, conf); if (!conf->r10buf_pool) return -ENOMEM; diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index dae740adaf65..31843604049c 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -73,10 +73,8 @@ static void print_raid5_conf (raid5_conf_t *conf); static void __release_stripe(raid5_conf_t *conf, struct stripe_head *sh) { if (atomic_dec_and_test(&sh->count)) { - if (!list_empty(&sh->lru)) - BUG(); - if (atomic_read(&conf->active_stripes)==0) - BUG(); + BUG_ON(!list_empty(&sh->lru)); + BUG_ON(atomic_read(&conf->active_stripes)==0); if (test_bit(STRIPE_HANDLE, &sh->state)) { if (test_bit(STRIPE_DELAYED, &sh->state)) list_add_tail(&sh->lru, &conf->delayed_list); @@ -184,10 +182,8 @@ static void init_stripe(struct stripe_head *sh, sector_t sector, int pd_idx, int raid5_conf_t *conf = sh->raid_conf; int i; - if (atomic_read(&sh->count) != 0) - BUG(); - if (test_bit(STRIPE_HANDLE, &sh->state)) - BUG(); + BUG_ON(atomic_read(&sh->count) != 0); + BUG_ON(test_bit(STRIPE_HANDLE, &sh->state)); CHECK_DEVLOCK(); PRINTK("init_stripe called, stripe %llu\n", @@ -269,8 +265,7 @@ static struct stripe_head *get_active_stripe(raid5_conf_t *conf, sector_t sector init_stripe(sh, sector, pd_idx, disks); } else { if (atomic_read(&sh->count)) { - if (!list_empty(&sh->lru)) - BUG(); + BUG_ON(!list_empty(&sh->lru)); } else { if (!test_bit(STRIPE_HANDLE, &sh->state)) atomic_inc(&conf->active_stripes); @@ -465,8 +460,7 @@ static int drop_one_stripe(raid5_conf_t *conf) spin_unlock_irq(&conf->device_lock); if (!sh) return 0; - if (atomic_read(&sh->count)) - BUG(); + BUG_ON(atomic_read(&sh->count)); shrink_buffers(sh, conf->pool_size); kmem_cache_free(conf->slab_cache, sh); atomic_dec(&conf->active_stripes); @@ -882,8 +876,7 @@ static void compute_parity(struct stripe_head *sh, int method) ptr[0] = page_address(sh->dev[pd_idx].page); switch(method) { case READ_MODIFY_WRITE: - if (!test_bit(R5_UPTODATE, &sh->dev[pd_idx].flags)) - BUG(); + BUG_ON(!test_bit(R5_UPTODATE, &sh->dev[pd_idx].flags)); for (i=disks ; i-- ;) { if (i==pd_idx) continue; @@ -896,7 +889,7 @@ static void compute_parity(struct stripe_head *sh, int method) if (test_and_clear_bit(R5_Overlap, &sh->dev[i].flags)) wake_up(&conf->wait_for_overlap); - if (sh->dev[i].written) BUG(); + BUG_ON(sh->dev[i].written); sh->dev[i].written = chosen; check_xor(); } @@ -912,7 +905,7 @@ static void compute_parity(struct stripe_head *sh, int method) if (test_and_clear_bit(R5_Overlap, &sh->dev[i].flags)) wake_up(&conf->wait_for_overlap); - if (sh->dev[i].written) BUG(); + BUG_ON(sh->dev[i].written); sh->dev[i].written = chosen; } break; @@ -995,8 +988,7 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in if (*bip && (*bip)->bi_sector < bi->bi_sector + ((bi->bi_size)>>9)) goto overlap; - if (*bip && bi->bi_next && (*bip) != bi->bi_next) - BUG(); + BUG_ON(*bip && bi->bi_next && (*bip) != bi->bi_next); if (*bip) bi->bi_next = *bip; *bip = bi; @@ -1430,8 +1422,7 @@ static void handle_stripe(struct stripe_head *sh) set_bit(STRIPE_HANDLE, &sh->state); if (failed == 0) { char *pagea; - if (uptodate != disks) - BUG(); + BUG_ON(uptodate != disks); compute_parity(sh, CHECK_PARITY); uptodate--; pagea = page_address(sh->dev[sh->pd_idx].page); @@ -2096,8 +2087,7 @@ static void raid5d (mddev_t *mddev) list_del_init(first); atomic_inc(&sh->count); - if (atomic_read(&sh->count)!= 1) - BUG(); + BUG_ON(atomic_read(&sh->count)!= 1); spin_unlock_irq(&conf->device_lock); handled++; diff --git a/drivers/md/raid6main.c b/drivers/md/raid6main.c index 6df4930fddec..bc69355e0100 100644 --- a/drivers/md/raid6main.c +++ b/drivers/md/raid6main.c @@ -91,10 +91,8 @@ static void print_raid6_conf (raid6_conf_t *conf); static void __release_stripe(raid6_conf_t *conf, struct stripe_head *sh) { if (atomic_dec_and_test(&sh->count)) { - if (!list_empty(&sh->lru)) - BUG(); - if (atomic_read(&conf->active_stripes)==0) - BUG(); + BUG_ON(!list_empty(&sh->lru)); + BUG_ON(atomic_read(&conf->active_stripes)==0); if (test_bit(STRIPE_HANDLE, &sh->state)) { if (test_bit(STRIPE_DELAYED, &sh->state)) list_add_tail(&sh->lru, &conf->delayed_list); @@ -202,10 +200,8 @@ static void init_stripe(struct stripe_head *sh, sector_t sector, int pd_idx) raid6_conf_t *conf = sh->raid_conf; int disks = conf->raid_disks, i; - if (atomic_read(&sh->count) != 0) - BUG(); - if (test_bit(STRIPE_HANDLE, &sh->state)) - BUG(); + BUG_ON(atomic_read(&sh->count) != 0); + BUG_ON(test_bit(STRIPE_HANDLE, &sh->state)); CHECK_DEVLOCK(); PRINTK("init_stripe called, stripe %llu\n", @@ -284,13 +280,11 @@ static struct stripe_head *get_active_stripe(raid6_conf_t *conf, sector_t sector init_stripe(sh, sector, pd_idx); } else { if (atomic_read(&sh->count)) { - if (!list_empty(&sh->lru)) - BUG(); + BUG_ON(!list_empty(&sh->lru)); } else { if (!test_bit(STRIPE_HANDLE, &sh->state)) atomic_inc(&conf->active_stripes); - if (list_empty(&sh->lru)) - BUG(); + BUG_ON(list_empty(&sh->lru)); list_del_init(&sh->lru); } } @@ -353,8 +347,7 @@ static int drop_one_stripe(raid6_conf_t *conf) spin_unlock_irq(&conf->device_lock); if (!sh) return 0; - if (atomic_read(&sh->count)) - BUG(); + BUG_ON(atomic_read(&sh->count)); shrink_buffers(sh, conf->raid_disks); kmem_cache_free(conf->slab_cache, sh); atomic_dec(&conf->active_stripes); @@ -780,7 +773,7 @@ static void compute_parity(struct stripe_head *sh, int method) if (test_and_clear_bit(R5_Overlap, &sh->dev[i].flags)) wake_up(&conf->wait_for_overlap); - if (sh->dev[i].written) BUG(); + BUG_ON(sh->dev[i].written); sh->dev[i].written = chosen; } break; @@ -970,8 +963,7 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in if (*bip && (*bip)->bi_sector < bi->bi_sector + ((bi->bi_size)>>9)) goto overlap; - if (*bip && bi->bi_next && (*bip) != bi->bi_next) - BUG(); + BUG_ON(*bip && bi->bi_next && (*bip) != bi->bi_next); if (*bip) bi->bi_next = *bip; *bip = bi; @@ -1906,8 +1898,7 @@ static void raid6d (mddev_t *mddev) list_del_init(first); atomic_inc(&sh->count); - if (atomic_read(&sh->count)!= 1) - BUG(); + BUG_ON(atomic_read(&sh->count)!= 1); spin_unlock_irq(&conf->device_lock); handled++; @@ -2151,6 +2142,8 @@ static int run(mddev_t *mddev) } /* Ok, everything is just fine now */ + sysfs_create_group(&mddev->kobj, &raid6_attrs_group); + mddev->array_size = mddev->size * (mddev->raid_disks - 2); mddev->queue->unplug_fn = raid6_unplug_device; diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index baa9f58beffc..fffc711c260c 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -51,18 +51,18 @@ config VIDEO_TVEEPROM tristate config USB_DABUSB - tristate "DABUSB driver" - depends on USB - ---help--- - A Digital Audio Broadcasting (DAB) Receiver for USB and Linux - brought to you by the DAB-Team - . This driver can be taken - as an example for URB-based bulk, control, and isochronous - transactions. URB's are explained in - . + tristate "DABUSB driver" + depends on USB + ---help--- + A Digital Audio Broadcasting (DAB) Receiver for USB and Linux + brought to you by the DAB-Team + . This driver can be taken + as an example for URB-based bulk, control, and isochronous + transactions. URB's are explained in + . - To compile this driver as a module, choose M here: the - module will be called dabusb. + To compile this driver as a module, choose M here: the + module will be called dabusb. endmenu diff --git a/drivers/media/dvb/bt8xx/Kconfig b/drivers/media/dvb/bt8xx/Kconfig index 2337b41714e0..376ca48f1d1d 100644 --- a/drivers/media/dvb/bt8xx/Kconfig +++ b/drivers/media/dvb/bt8xx/Kconfig @@ -7,6 +7,7 @@ config DVB_BT8XX select DVB_CX24110 select DVB_OR51211 select DVB_LGDT330X + select FW_LOADER help Support for PCI cards based on the Bt8xx PCI bridge. Examples are the Nebula cards, the Pinnacle PCTV cards, the Twinhan DST cards, diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c index 09e96e9ddbdf..04578df3f249 100644 --- a/drivers/media/dvb/dvb-core/dmxdev.c +++ b/drivers/media/dvb/dvb-core/dmxdev.c @@ -141,12 +141,18 @@ static int dvb_dvr_open(struct inode *inode, struct file *file) } if ((file->f_flags & O_ACCMODE) == O_RDONLY) { - void *mem = vmalloc(DVR_BUFFER_SIZE); + void *mem; + if (!dvbdev->readers) { + mutex_unlock(&dmxdev->mutex); + return -EBUSY; + } + mem = vmalloc(DVR_BUFFER_SIZE); if (!mem) { mutex_unlock(&dmxdev->mutex); return -ENOMEM; } dvb_ringbuffer_init(&dmxdev->dvr_buffer, mem, DVR_BUFFER_SIZE); + dvbdev->readers--; } if ((file->f_flags & O_ACCMODE) == O_WRONLY) { @@ -184,6 +190,7 @@ static int dvb_dvr_release(struct inode *inode, struct file *file) dmxdev->dvr_orig_fe); } if ((file->f_flags & O_ACCMODE) == O_RDONLY) { + dvbdev->readers++; if (dmxdev->dvr_buffer.data) { void *mem = dmxdev->dvr_buffer.data; mb(); @@ -1029,8 +1036,7 @@ static struct file_operations dvb_dvr_fops = { static struct dvb_device dvbdev_dvr = { .priv = NULL, - .users = 1, - .writers = 1, + .readers = 1, .fops = &dvb_dvr_fops }; diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 2c3ea8f95dcd..4f8f257e6795 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -105,6 +105,7 @@ struct dvb_frontend_private { fe_status_t status; unsigned long tune_mode_flags; unsigned int delay; + unsigned int reinitialise; /* swzigzag values */ unsigned int state; @@ -121,6 +122,7 @@ struct dvb_frontend_private { unsigned int check_wrapped; }; +static void dvb_frontend_wakeup(struct dvb_frontend *fe); static void dvb_frontend_add_event(struct dvb_frontend *fe, fe_status_t status) { @@ -213,6 +215,15 @@ static void dvb_frontend_init(struct dvb_frontend *fe) fe->ops->init(fe); } +void dvb_frontend_reinitialise(struct dvb_frontend *fe) +{ + struct dvb_frontend_private *fepriv = fe->frontend_priv; + + fepriv->reinitialise = 1; + dvb_frontend_wakeup(fe); +} +EXPORT_SYMBOL(dvb_frontend_reinitialise); + static void dvb_frontend_swzigzag_update_delay(struct dvb_frontend_private *fepriv, int locked) { int q2; @@ -505,8 +516,8 @@ static int dvb_frontend_thread(void *data) fepriv->quality = 0; fepriv->delay = 3*HZ; fepriv->status = 0; - dvb_frontend_init(fe); fepriv->wakeup = 0; + fepriv->reinitialise = 1; while (1) { up(&fepriv->sem); /* is locked when we enter the thread... */ @@ -524,6 +535,11 @@ static int dvb_frontend_thread(void *data) if (down_interruptible(&fepriv->sem)) break; + if (fepriv->reinitialise) { + dvb_frontend_init(fe); + fepriv->reinitialise = 0; + } + /* do an iteration of the tuning loop */ if (fe->ops->tune) { /* have we been asked to retune? */ diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h index d5aee5ad67a0..5926a3b745c9 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -112,6 +112,8 @@ extern int dvb_register_frontend(struct dvb_adapter* dvb, extern int dvb_unregister_frontend(struct dvb_frontend* fe); +extern void dvb_frontend_reinitialise(struct dvb_frontend *fe); + extern void dvb_frontend_sleep_until(struct timeval *waketime, u32 add_usec); extern s32 timeval_usec_diff(struct timeval lasttime, struct timeval curtime); diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c index a14e737ec848..7edd6362b9cc 100644 --- a/drivers/media/dvb/dvb-usb/cxusb.c +++ b/drivers/media/dvb/dvb-usb/cxusb.c @@ -233,6 +233,45 @@ static struct dvb_usb_rc_key dvico_mce_rc_keys[] = { { 0xfe, 0x4e, KEY_POWER }, }; +static struct dvb_usb_rc_key dvico_portable_rc_keys[] = { + { 0xfc, 0x02, KEY_SETUP }, /* Profile */ + { 0xfc, 0x43, KEY_POWER2 }, + { 0xfc, 0x06, KEY_EPG }, + { 0xfc, 0x5a, KEY_BACK }, + { 0xfc, 0x05, KEY_MENU }, + { 0xfc, 0x47, KEY_INFO }, + { 0xfc, 0x01, KEY_TAB }, + { 0xfc, 0x42, KEY_PREVIOUSSONG },/* Replay */ + { 0xfc, 0x49, KEY_VOLUMEUP }, + { 0xfc, 0x09, KEY_VOLUMEDOWN }, + { 0xfc, 0x54, KEY_CHANNELUP }, + { 0xfc, 0x0b, KEY_CHANNELDOWN }, + { 0xfc, 0x16, KEY_CAMERA }, + { 0xfc, 0x40, KEY_TUNER }, /* ATV/DTV */ + { 0xfc, 0x45, KEY_OPEN }, + { 0xfc, 0x19, KEY_1 }, + { 0xfc, 0x18, KEY_2 }, + { 0xfc, 0x1b, KEY_3 }, + { 0xfc, 0x1a, KEY_4 }, + { 0xfc, 0x58, KEY_5 }, + { 0xfc, 0x59, KEY_6 }, + { 0xfc, 0x15, KEY_7 }, + { 0xfc, 0x14, KEY_8 }, + { 0xfc, 0x17, KEY_9 }, + { 0xfc, 0x44, KEY_ANGLE }, /* Aspect */ + { 0xfc, 0x55, KEY_0 }, + { 0xfc, 0x07, KEY_ZOOM }, + { 0xfc, 0x0a, KEY_REWIND }, + { 0xfc, 0x08, KEY_PLAYPAUSE }, + { 0xfc, 0x4b, KEY_FASTFORWARD }, + { 0xfc, 0x5b, KEY_MUTE }, + { 0xfc, 0x04, KEY_STOP }, + { 0xfc, 0x56, KEY_RECORD }, + { 0xfc, 0x57, KEY_POWER }, + { 0xfc, 0x41, KEY_UNKNOWN }, /* INPUT */ + { 0xfc, 0x00, KEY_UNKNOWN }, /* HD */ +}; + static int cxusb_dee1601_demod_init(struct dvb_frontend* fe) { static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x28 }; @@ -511,6 +550,11 @@ static struct dvb_usb_properties cxusb_bluebird_lgh064f_properties = { .i2c_algo = &cxusb_i2c_algo, + .rc_interval = 100, + .rc_key_map = dvico_portable_rc_keys, + .rc_key_map_size = ARRAY_SIZE(dvico_portable_rc_keys), + .rc_query = cxusb_rc_query, + .generic_bulk_ctrl_endpoint = 0x01, /* parameter for the MPEG2-data transfer */ .urb = { @@ -600,6 +644,11 @@ static struct dvb_usb_properties cxusb_bluebird_lgz201_properties = { .i2c_algo = &cxusb_i2c_algo, + .rc_interval = 100, + .rc_key_map = dvico_portable_rc_keys, + .rc_key_map_size = ARRAY_SIZE(dvico_portable_rc_keys), + .rc_query = cxusb_rc_query, + .generic_bulk_ctrl_endpoint = 0x01, /* parameter for the MPEG2-data transfer */ .urb = { @@ -640,6 +689,11 @@ static struct dvb_usb_properties cxusb_bluebird_dtt7579_properties = { .i2c_algo = &cxusb_i2c_algo, + .rc_interval = 100, + .rc_key_map = dvico_portable_rc_keys, + .rc_key_map_size = ARRAY_SIZE(dvico_portable_rc_keys), + .rc_query = cxusb_rc_query, + .generic_bulk_ctrl_endpoint = 0x01, /* parameter for the MPEG2-data transfer */ .urb = { diff --git a/drivers/media/dvb/dvb-usb/dtt200u.c b/drivers/media/dvb/dvb-usb/dtt200u.c index 12ebaf8bddca..70afcfd141ca 100644 --- a/drivers/media/dvb/dvb-usb/dtt200u.c +++ b/drivers/media/dvb/dvb-usb/dtt200u.c @@ -94,12 +94,14 @@ static int dtt200u_frontend_attach(struct dvb_usb_device *d) static struct dvb_usb_properties dtt200u_properties; static struct dvb_usb_properties wt220u_properties; +static struct dvb_usb_properties wt220u_zl0353_properties; static int dtt200u_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { if (dvb_usb_device_init(intf,&dtt200u_properties,THIS_MODULE,NULL) == 0 || - dvb_usb_device_init(intf,&wt220u_properties,THIS_MODULE,NULL) == 0) + dvb_usb_device_init(intf,&wt220u_properties,THIS_MODULE,NULL) == 0 || + dvb_usb_device_init(intf,&wt220u_zl0353_properties,THIS_MODULE,NULL) == 0) return 0; return -ENODEV; @@ -110,6 +112,8 @@ static struct usb_device_id dtt200u_usb_table [] = { { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_DTT200U_WARM) }, { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_COLD) }, { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_WARM) }, + { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_ZL0353_COLD) }, + { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_ZL0353_WARM) }, { 0 }, }; MODULE_DEVICE_TABLE(usb, dtt200u_usb_table); @@ -196,6 +200,47 @@ static struct dvb_usb_properties wt220u_properties = { } }; +static struct dvb_usb_properties wt220u_zl0353_properties = { + .caps = DVB_USB_HAS_PID_FILTER | DVB_USB_NEED_PID_FILTERING, + .pid_filter_count = 15, + + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-wt220u-zl0353-01.fw", + + .power_ctrl = dtt200u_power_ctrl, + .streaming_ctrl = dtt200u_streaming_ctrl, + .pid_filter = dtt200u_pid_filter, + .frontend_attach = dtt200u_frontend_attach, + + .rc_interval = 300, + .rc_key_map = dtt200u_rc_keys, + .rc_key_map_size = ARRAY_SIZE(dtt200u_rc_keys), + .rc_query = dtt200u_rc_query, + + .generic_bulk_ctrl_endpoint = 0x01, + + /* parameter for the MPEG2-data transfer */ + .urb = { + .type = DVB_USB_BULK, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + + .num_device_descs = 1, + .devices = { + { .name = "WideView WT-220U PenType Receiver (based on ZL353)", + .cold_ids = { &dtt200u_usb_table[4], NULL }, + .warm_ids = { &dtt200u_usb_table[5], NULL }, + }, + { NULL }, + } +}; + /* usb specific object needed to register this driver with the usb subsystem */ static struct usb_driver dtt200u_usb_driver = { .name = "dvb_usb_dtt200u", diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 4a1b9e77e339..cb239049b098 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -83,6 +83,8 @@ #define USB_PID_DTT200U_WARM 0x0301 #define USB_PID_WT220U_COLD 0x0222 #define USB_PID_WT220U_WARM 0x0221 +#define USB_PID_WT220U_ZL0353_COLD 0x022a +#define USB_PID_WT220U_ZL0353_WARM 0x022b #define USB_PID_WINTV_NOVA_T_USB2_COLD 0x9300 #define USB_PID_WINTV_NOVA_T_USB2_WARM 0x9301 #define USB_PID_NEBULA_DIGITV 0x0201 diff --git a/drivers/media/dvb/dvb-usb/vp702x-fe.c b/drivers/media/dvb/dvb-usb/vp702x-fe.c index b6d95e1c9c52..2a89f8c5da99 100644 --- a/drivers/media/dvb/dvb-usb/vp702x-fe.c +++ b/drivers/media/dvb/dvb-usb/vp702x-fe.c @@ -147,8 +147,9 @@ static int vp702x_fe_set_frontend(struct dvb_frontend* fe, cmd[4] = (sr >> 4) & 0xff; cmd[5] = (sr << 4) & 0xf0; - deb_fe("setting frontend to: %u -> %u (%x) LNB-based GHz, symbolrate: %d -> %Lu (%Lx)\n", - fep->frequency,freq,freq, fep->u.qpsk.symbol_rate, sr, sr); + deb_fe("setting frontend to: %u -> %u (%x) LNB-based GHz, symbolrate: %d -> %lu (%lx)\n", + fep->frequency,freq,freq, fep->u.qpsk.symbol_rate, + (unsigned long) sr, (unsigned long) sr); /* if (fep->inversion == INVERSION_ON) cmd[6] |= 0x80; */ diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index 94233168d241..37d5e0af1683 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -181,6 +181,11 @@ config DVB_OR51211 help An ATSC 8VSB tuner module. Say Y when you want to support this frontend. + This driver needs external firmware. Please use the command + "/Documentation/dvb/get_dvb_firmware or51211" to + download it, and then copy it to /usr/lib/hotplug/firmware + or /lib/firmware (depending on configuration of firmware hotplug). + config DVB_OR51132 tristate "Oren OR51132 based" depends on DVB_CORE @@ -189,6 +194,13 @@ config DVB_OR51132 An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. + This driver needs external firmware. Please use the commands + "/Documentation/dvb/get_dvb_firmware or51132_vsb" and/or + "/Documentation/dvb/get_dvb_firmware or51132_qam" to + download firmwares for 8VSB and QAM64/256, respectively. Copy them to + /usr/lib/hotplug/firmware or /lib/firmware (depending on + configuration of firmware hotplug). + config DVB_BCM3510 tristate "Broadcom BCM3510" depends on DVB_CORE diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c index 8e8df7b4ca0e..b83dafa4e12c 100644 --- a/drivers/media/dvb/frontends/tda1004x.c +++ b/drivers/media/dvb/frontends/tda1004x.c @@ -52,7 +52,6 @@ struct tda1004x_state { struct dvb_frontend frontend; /* private demod data */ - u8 initialised; enum tda1004x_demod demod_type; }; @@ -594,9 +593,6 @@ static int tda10045_init(struct dvb_frontend* fe) dprintk("%s\n", __FUNCTION__); - if (state->initialised) - return 0; - if (tda10045_fwupload(fe)) { printk("tda1004x: firmware upload failed\n"); return -EIO; @@ -626,7 +622,6 @@ static int tda10045_init(struct dvb_frontend* fe) tda1004x_write_mask(state, 0x1f, 0x01, state->config->invert_oclk); - state->initialised = 1; return 0; } @@ -635,9 +630,6 @@ static int tda10046_init(struct dvb_frontend* fe) struct tda1004x_state* state = fe->demodulator_priv; dprintk("%s\n", __FUNCTION__); - if (state->initialised) - return 0; - if (tda10046_fwupload(fe)) { printk("tda1004x: firmware upload failed\n"); return -EIO; @@ -697,7 +689,6 @@ static int tda10046_init(struct dvb_frontend* fe) // tda1004x_write_mask(state, 0x50, 0x80, 0x80); // handle out of guard echoes tda1004x_write_mask(state, 0x3a, 0x80, state->config->invert_oclk << 7); - state->initialised = 1; return 0; } @@ -1207,7 +1198,6 @@ static int tda1004x_sleep(struct dvb_frontend* fe) tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 1); break; } - state->initialised = 0; return 0; } @@ -1271,7 +1261,6 @@ struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, state->config = config; state->i2c = i2c; memcpy(&state->ops, &tda10045_ops, sizeof(struct dvb_frontend_ops)); - state->initialised = 0; state->demod_type = TDA1004X_DEMOD_TDA10045; /* check if the demod is there */ @@ -1330,7 +1319,6 @@ struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, state->config = config; state->i2c = i2c; memcpy(&state->ops, &tda10046_ops, sizeof(struct dvb_frontend_ops)); - state->initialised = 0; state->demod_type = TDA1004X_DEMOD_TDA10046; /* check if the demod is there */ diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c index 840efec32cb6..d028245c8eed 100644 --- a/drivers/media/dvb/ttpci/av7110.c +++ b/drivers/media/dvb/ttpci/av7110.c @@ -87,6 +87,7 @@ static int volume = 255; static int budgetpatch; static int wss_cfg_4_3 = 0x4008; static int wss_cfg_16_9 = 0x0007; +static int tv_standard; module_param_named(debug, av7110_debug, int, 0644); MODULE_PARM_DESC(debug, "debug level (bitmask, default 0)"); @@ -109,6 +110,8 @@ module_param(wss_cfg_4_3, int, 0444); MODULE_PARM_DESC(wss_cfg_4_3, "WSS 4:3 - default 0x4008 - bit 15: disable, 14: burst mode, 13..0: wss data"); module_param(wss_cfg_16_9, int, 0444); MODULE_PARM_DESC(wss_cfg_16_9, "WSS 16:9 - default 0x0007 - bit 15: disable, 14: burst mode, 13..0: wss data"); +module_param(tv_standard, int, 0444); +MODULE_PARM_DESC(tv_standard, "TV standard: 0 PAL (default), 1 NTSC"); static void restart_feeds(struct av7110 *av7110); @@ -2123,7 +2126,7 @@ static int frontend_init(struct av7110 *av7110) read_pwm(av7110)); break; case 0x0003: - /* Haupauge DVB-C 2.1 VES1820/ALPS TDBE2 */ + /* Hauppauge DVB-C 2.1 VES1820/ALPS TDBE2 */ av7110->fe = ves1820_attach(&alps_tdbe2_config, &av7110->i2c_adap, read_pwm(av7110)); break; @@ -2543,6 +2546,9 @@ static int __devinit av7110_attach(struct saa7146_dev* dev, av7110->osdwin = 1; mutex_init(&av7110->osd_mutex); + /* TV standard */ + av7110->vidmode = tv_standard == 1 ? VIDEO_MODE_NTSC : VIDEO_MODE_PAL; + /* ARM "watchdog" */ init_waitqueue_head(&av7110->arm_wait); av7110->arm_thread = NULL; diff --git a/drivers/media/dvb/ttpci/av7110_av.c b/drivers/media/dvb/ttpci/av7110_av.c index 400facec7407..2eff09f638d3 100644 --- a/drivers/media/dvb/ttpci/av7110_av.c +++ b/drivers/media/dvb/ttpci/av7110_av.c @@ -1479,8 +1479,6 @@ int av7110_av_init(struct av7110 *av7110) void (*play[])(u8 *, int, void *) = { play_audio_cb, play_video_cb }; int i, ret; - av7110->vidmode = VIDEO_MODE_PAL; - for (i = 0; i < 2; i++) { struct ipack *ipack = av7110->ipack + i; diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c index 9dd4745f5312..8efe3ce5f66c 100644 --- a/drivers/media/dvb/ttpci/budget-av.c +++ b/drivers/media/dvb/ttpci/budget-av.c @@ -60,11 +60,11 @@ struct budget_av { struct dvb_ca_en50221 ca; }; -/* GPIO CI Connections: - * 0 - Vcc/Reset (Reset is controlled by capacitor) - * 1 - Attribute Memory - * 2 - Card Enable (Active Low) - * 3 - Card Detect +/* GPIO Connections: + * 0 - Vcc/Reset (Reset is controlled by capacitor). Resets the frontend *AS WELL*! + * 1 - CI memory select 0=>IO memory, 1=>Attribute Memory + * 2 - CI Card Enable (Active Low) + * 3 - CI Card Detect */ /**************************************************************************** @@ -214,6 +214,9 @@ static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) while (--timeout > 0 && ciintf_read_attribute_mem(ca, slot, 0) != 0x1d) msleep(100); + /* reinitialise the frontend */ + dvb_frontend_reinitialise(budget_av->budget.dvb_frontend); + if (timeout <= 0) { printk(KERN_ERR "budget-av: cam reset failed (timeout).\n"); diff --git a/drivers/media/dvb/ttpci/budget-core.c b/drivers/media/dvb/ttpci/budget-core.c index 633e68c341c8..ea2066d461fc 100644 --- a/drivers/media/dvb/ttpci/budget-core.c +++ b/drivers/media/dvb/ttpci/budget-core.c @@ -39,9 +39,21 @@ #include "budget.h" #include "ttpci-eeprom.h" +#define TS_WIDTH (2 * TS_SIZE) +#define TS_WIDTH_ACTIVY TS_SIZE +#define TS_HEIGHT_MASK 0xf00 +#define TS_HEIGHT_MASK_ACTIVY 0xc00 +#define TS_MIN_BUFSIZE_K 188 +#define TS_MAX_BUFSIZE_K 1410 +#define TS_MAX_BUFSIZE_K_ACTIVY 564 +#define BUFFER_WARNING_WAIT (30*HZ) + int budget_debug; +static int dma_buffer_size = TS_MIN_BUFSIZE_K; module_param_named(debug, budget_debug, int, 0644); +module_param_named(bufsize, dma_buffer_size, int, 0444); MODULE_PARM_DESC(debug, "Turn on/off budget debugging (default:off)."); +MODULE_PARM_DESC(bufsize, "DMA buffer size in KB, default: 188, min: 188, max: 1410 (Activy: 564)"); /**************************************************************************** * TT budget / WinTV Nova @@ -70,11 +82,10 @@ static int start_ts_capture(struct budget *budget) saa7146_write(dev, MC1, MASK_20); // DMA3 off - memset(budget->grabbing, 0x00, TS_HEIGHT * TS_WIDTH); + memset(budget->grabbing, 0x00, budget->buffer_size); saa7146_write(dev, PCI_BT_V1, 0x001c0000 | (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000)); - budget->tsf = 0xff; budget->ttbp = 0; /* @@ -115,16 +126,12 @@ static int start_ts_capture(struct budget *budget) saa7146_write(dev, BASE_ODD3, 0); saa7146_write(dev, BASE_EVEN3, 0); - saa7146_write(dev, PROT_ADDR3, TS_WIDTH * TS_HEIGHT); + saa7146_write(dev, PROT_ADDR3, budget->buffer_size); saa7146_write(dev, BASE_PAGE3, budget->pt.dma | ME1 | 0x90); - if (budget->card->type == BUDGET_FS_ACTIVY) { - saa7146_write(dev, PITCH3, TS_WIDTH / 2); - saa7146_write(dev, NUM_LINE_BYTE3, ((TS_HEIGHT * 2) << 16) | (TS_WIDTH / 2)); - } else { - saa7146_write(dev, PITCH3, TS_WIDTH); - saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT << 16) | TS_WIDTH); - } + saa7146_write(dev, PITCH3, budget->buffer_width); + saa7146_write(dev, NUM_LINE_BYTE3, + (budget->buffer_height << 16) | budget->buffer_width); saa7146_write(dev, MC2, (MASK_04 | MASK_20)); @@ -141,11 +148,12 @@ static void vpeirq(unsigned long data) u8 *mem = (u8 *) (budget->grabbing); u32 olddma = budget->ttbp; u32 newdma = saa7146_read(budget->dev, PCI_VDP3); + u32 count; /* nearest lower position divisible by 188 */ newdma -= newdma % 188; - if (newdma >= TS_BUFLEN) + if (newdma >= budget->buffer_size) return; budget->ttbp = newdma; @@ -154,11 +162,24 @@ static void vpeirq(unsigned long data) return; if (newdma > olddma) { /* no wraparound, dump olddma..newdma */ - dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, (newdma - olddma) / 188); + count = newdma - olddma; + dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188); } else { /* wraparound, dump olddma..buflen and 0..newdma */ - dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, (TS_BUFLEN - olddma) / 188); + count = budget->buffer_size - olddma; + dvb_dmx_swfilter_packets(&budget->demux, mem + olddma, count / 188); + count += newdma; dvb_dmx_swfilter_packets(&budget->demux, mem, newdma / 188); } + + if (count > budget->buffer_warning_threshold) + budget->buffer_warnings++; + + if (budget->buffer_warnings && time_after(jiffies, budget->buffer_warning_time)) { + printk("%s %s: used %d times >80%% of buffer (%u bytes now)\n", + budget->dev->name, __FUNCTION__, budget->buffer_warnings, count); + budget->buffer_warning_time = jiffies + BUFFER_WARNING_WAIT; + budget->buffer_warnings = 0; + } } @@ -341,9 +362,10 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, struct saa7146_pci_extension_data *info, struct module *owner) { - int length = TS_WIDTH * TS_HEIGHT; int ret = 0; struct budget_info *bi = info->ext_priv; + int max_bufsize; + int height_mask; memset(budget, 0, sizeof(struct budget)); @@ -352,6 +374,32 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, budget->card = bi; budget->dev = (struct saa7146_dev *) dev; + if (budget->card->type == BUDGET_FS_ACTIVY) { + budget->buffer_width = TS_WIDTH_ACTIVY; + max_bufsize = TS_MAX_BUFSIZE_K_ACTIVY; + height_mask = TS_HEIGHT_MASK_ACTIVY; + } else { + budget->buffer_width = TS_WIDTH; + max_bufsize = TS_MAX_BUFSIZE_K; + height_mask = TS_HEIGHT_MASK; + } + + if (dma_buffer_size < TS_MIN_BUFSIZE_K) + dma_buffer_size = TS_MIN_BUFSIZE_K; + else if (dma_buffer_size > max_bufsize) + dma_buffer_size = max_bufsize; + + budget->buffer_height = dma_buffer_size * 1024 / budget->buffer_width; + budget->buffer_height &= height_mask; + budget->buffer_size = budget->buffer_height * budget->buffer_width; + budget->buffer_warning_threshold = budget->buffer_size * 80/100; + budget->buffer_warnings = 0; + budget->buffer_warning_time = jiffies; + + dprintk(2, "%s: width = %d, height = %d\n", + budget->dev->name, budget->buffer_width, budget->buffer_height); + printk("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size); + dvb_register_adapter(&budget->dvb_adapter, budget->card->name, owner); /* set dd1 stream a & b */ @@ -392,7 +440,7 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, ttpci_eeprom_parse_mac(&budget->i2c_adap, budget->dvb_adapter.proposed_mac); if (NULL == - (budget->grabbing = saa7146_vmalloc_build_pgtable(dev->pci, length, &budget->pt))) { + (budget->grabbing = saa7146_vmalloc_build_pgtable(dev->pci, budget->buffer_size, &budget->pt))) { ret = -ENOMEM; goto err; } diff --git a/drivers/media/dvb/ttpci/budget-patch.c b/drivers/media/dvb/ttpci/budget-patch.c index 9fc9185a8426..1b3aaac5e763 100644 --- a/drivers/media/dvb/ttpci/budget-patch.c +++ b/drivers/media/dvb/ttpci/budget-patch.c @@ -577,20 +577,6 @@ static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_exte saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO); // Set RPS1 Address register to point to RPS code (r108 p42) saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle); - // Set Source Line Counter Threshold, using BRS (rCC p43) - // It generates HS event every TS_HEIGHT lines - // this is related to TS_WIDTH set in register - // NUM_LINE_BYTE3 in budget-core.c. If NUM_LINE_BYTE - // low 16 bits are set to TS_WIDTH bytes (TS_WIDTH=2*188 - //,then RPS_THRESH1 - // should be set to trigger every TS_HEIGHT (512) lines. - // - saa7146_write(dev, RPS_THRESH1, (TS_HEIGHT*1) | MASK_12 ); - - // saa7146_write(dev, RPS_THRESH0, ((TS_HEIGHT/2)<<16) |MASK_28| (TS_HEIGHT/2) |MASK_12 ); - // Enable RPS1 (rFC p33) - saa7146_write(dev, MC1, (MASK_13 | MASK_29)); - if (!(budget = kmalloc (sizeof(struct budget_patch), GFP_KERNEL))) return -ENOMEM; @@ -602,6 +588,20 @@ static int budget_patch_attach (struct saa7146_dev* dev, struct saa7146_pci_exte return err; } + // Set Source Line Counter Threshold, using BRS (rCC p43) + // It generates HS event every TS_HEIGHT lines + // this is related to TS_WIDTH set in register + // NUM_LINE_BYTE3 in budget-core.c. If NUM_LINE_BYTE + // low 16 bits are set to TS_WIDTH bytes (TS_WIDTH=2*188 + //,then RPS_THRESH1 + // should be set to trigger every TS_HEIGHT (512) lines. + // + saa7146_write(dev, RPS_THRESH1, budget->buffer_height | MASK_12 ); + + // saa7146_write(dev, RPS_THRESH0, ((TS_HEIGHT/2)<<16) |MASK_28| (TS_HEIGHT/2) |MASK_12 ); + // Enable RPS1 (rFC p33) + saa7146_write(dev, MC1, (MASK_13 | MASK_29)); + dev->ext_priv = budget; diff --git a/drivers/media/dvb/ttpci/budget.h b/drivers/media/dvb/ttpci/budget.h index 4ac0f4d08025..ecea3a13030e 100644 --- a/drivers/media/dvb/ttpci/budget.h +++ b/drivers/media/dvb/ttpci/budget.h @@ -58,7 +58,13 @@ struct budget { int ci_present; int video_port; - u8 tsf; + u32 buffer_width; + u32 buffer_height; + u32 buffer_size; + u32 buffer_warning_threshold; + u32 buffer_warnings; + unsigned long buffer_warning_time; + u32 ttbp; int feeding; @@ -79,11 +85,6 @@ static struct saa7146_pci_extension_data x_var = { \ .ext_priv = &x_var ## _info, \ .ext = &budget_extension }; -#define TS_WIDTH (376) -#define TS_HEIGHT (512) -#define TS_BUFLEN (TS_WIDTH*TS_HEIGHT) -#define TS_MAX_PACKETS (TS_BUFLEN/TS_SIZE) - #define BUDGET_TT 0 #define BUDGET_TT_HW_DISEQC 1 #define BUDGET_PATCH 3 diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index f31a19890b15..85888a8a93c9 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -300,7 +300,7 @@ config VIDEO_OVCAMCHIP camera drivers. To compile this driver as a module, choose M here: the - module will be called ovcamchip + module will be called ovcamchip. config VIDEO_M32R_AR tristate "AR devices" @@ -316,6 +316,13 @@ config VIDEO_M32R_AR_M64278 Say Y here to use the Renesas M64278E-800 camera module, which supports VGA(640x480 pixcels) size of images. +# +# Encoder / Decoder module configuration +# + +menu "Encoders and Decoders" + depends on VIDEO_DEV + config VIDEO_MSP3400 tristate "Micronas MSP34xx audio decoders" depends on VIDEO_DEV && I2C @@ -323,7 +330,7 @@ config VIDEO_MSP3400 Support for the Micronas MSP34xx series of audio decoders. To compile this driver as a module, choose M here: the - module will be called msp3400 + module will be called msp3400. config VIDEO_CS53L32A tristate "Cirrus Logic CS53L32A audio ADC" @@ -333,17 +340,27 @@ config VIDEO_CS53L32A stereo A/D converter. To compile this driver as a module, choose M here: the - module will be called cs53l32a + module will be called cs53l32a. config VIDEO_WM8775 - tristate "Wolfson Microelectronics WM8775 audio ADC" + tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer" depends on VIDEO_DEV && I2C && EXPERIMENTAL ---help--- - Support for the Wolfson Microelectronics WM8775 - high performance stereo A/D Converter. + Support for the Wolfson Microelectronics WM8775 high + performance stereo A/D Converter with a 4 channel input mixer. To compile this driver as a module, choose M here: the - module will be called wm8775 + module will be called wm8775. + +config VIDEO_WM8739 + tristate "Wolfson Microelectronics WM8739 stereo audio ADC" + depends on VIDEO_DEV && I2C && EXPERIMENTAL + ---help--- + Support for the Wolfson Microelectronics WM8739 + stereo A/D Converter. + + To compile this driver as a module, choose M here: the + module will be called wm8739. source "drivers/media/video/cx25840/Kconfig" @@ -354,7 +371,7 @@ config VIDEO_SAA711X Support for the Philips SAA7113/4/5 video decoders. To compile this driver as a module, choose M here: the - module will be called saa7115 + module will be called saa7115. config VIDEO_SAA7127 tristate "Philips SAA7127/9 digital video encoders" @@ -363,7 +380,32 @@ config VIDEO_SAA7127 Support for the Philips SAA7127/9 digital video encoders. To compile this driver as a module, choose M here: the - module will be called saa7127 + module will be called saa7127. + +config VIDEO_UPD64031A + tristate "NEC Electronics uPD64031A Ghost Reduction" + depends on VIDEO_DEV && I2C && EXPERIMENTAL + ---help--- + Support for the NEC Electronics uPD64031A Ghost Reduction + video chip. It is most often found in NTSC TV cards made for + Japan and is used to reduce the 'ghosting' effect that can + be present in analog TV broadcasts. + + To compile this driver as a module, choose M here: the + module will be called upd64031a. + +config VIDEO_UPD64083 + tristate "NEC Electronics uPD64083 3-Dimensional Y/C separation" + depends on VIDEO_DEV && I2C && EXPERIMENTAL + ---help--- + Support for the NEC Electronics uPD64083 3-Dimensional Y/C + separation video chip. It is used to improve the quality of + the colors of a composite signal. + + To compile this driver as a module, choose M here: the + module will be called upd64083. + +endmenu # encoder / decoder chips # # USB Multimedia device configuration @@ -374,20 +416,6 @@ menu "V4L USB devices" source "drivers/media/video/em28xx/Kconfig" -config USB_VICAM - tristate "USB 3com HomeConnect (aka vicam) support (EXPERIMENTAL)" - depends on USB && VIDEO_DEV && EXPERIMENTAL - ---help--- - Say Y here if you have 3com homeconnect camera (vicam). - - This driver uses the Video For Linux API. You must say Y or M to - "Video For Linux" (under Multimedia Devices) to use this driver. - Information on this API and pointers to "v4l" programs may be found - at . - - To compile this driver as a module, choose M here: the - module will be called vicam. - config USB_DSBR tristate "D-Link USB FM radio support (EXPERIMENTAL)" depends on USB && VIDEO_DEV && EXPERIMENTAL @@ -397,79 +425,20 @@ config USB_DSBR you must connect the line out connector to a sound card or a set of speakers. - This driver uses the Video For Linux API. You must enable - (Y or M in config) Video For Linux (under Character Devices) - to use this driver. Information on this API and pointers to - "v4l" programs may be found at - . - To compile this driver as a module, choose M here: the module will be called dsbr100. -config USB_ET61X251 - tristate "USB ET61X[12]51 PC Camera Controller support" - depends on USB && VIDEO_DEV - ---help--- - Say Y here if you want support for cameras based on Etoms ET61X151 - or ET61X251 PC Camera Controllers. +source "drivers/media/video/usbvideo/Kconfig" - See for more informations. - - This driver uses the Video For Linux API. You must say Y or M to - "Video For Linux" to use this driver. - - To compile this driver as a module, choose M here: the - module will be called et61x251. - -config USB_IBMCAM - tristate "USB IBM (Xirlink) C-it Camera support" - depends on USB && VIDEO_DEV - ---help--- - Say Y here if you want to connect a IBM "C-It" camera, also known as - "Xirlink PC Camera" to your computer's USB port. For more - information, read . - - This driver uses the Video For Linux API. You must enable - (Y or M in config) Video For Linux (under Character Devices) - to use this driver. Information on this API and pointers to - "v4l" programs may be found at - . - - To compile this driver as a module, choose M here: the - module will be called ibmcam. - - This camera has several configuration options which - can be specified when you load the module. Read - to learn more. - -config USB_KONICAWC - tristate "USB Konica Webcam support" - depends on USB && VIDEO_DEV - ---help--- - Say Y here if you want support for webcams based on a Konica - chipset. This is known to work with the Intel YC76 webcam. - - This driver uses the Video For Linux API. You must enable - (Y or M in config) Video For Linux (under Character Devices) - to use this driver. Information on this API and pointers to - "v4l" programs may be found at - . - - To compile this driver as a module, choose M here: the - module will be called konicawc. +source "drivers/media/video/et61x251/Kconfig" config USB_OV511 tristate "USB OV511 Camera support" depends on USB && VIDEO_DEV ---help--- Say Y here if you want to connect this type of camera to your - computer's USB port. See for more - information and for a list of supported cameras. - - This driver uses the Video For Linux API. You must say Y or M to - "Video For Linux" (under Character Devices) to use this driver. - Information on this API and pointers to "v4l" programs may be found - at . + computer's USB port. See + for more information and for a list of supported cameras. To compile this driver as a module, choose M here: the module will be called ov511. @@ -479,31 +448,13 @@ config USB_SE401 depends on USB && VIDEO_DEV ---help--- Say Y here if you want to connect this type of camera to your - computer's USB port. See for more - information and for a list of supported cameras. - - This driver uses the Video For Linux API. You must say Y or M to - "Video For Linux" (under Multimedia Devices) to use this driver. - Information on this API and pointers to "v4l" programs may be found - at . + computer's USB port. See + for more information and for a list of supported cameras. To compile this driver as a module, choose M here: the module will be called se401. -config USB_SN9C102 - tristate "USB SN9C10x PC Camera Controller support" - depends on USB && VIDEO_DEV - ---help--- - Say Y here if you want support for cameras based on SONiX SN9C101, - SN9C102 or SN9C103 PC Camera Controllers. - - See for more informations. - - This driver uses the Video For Linux API. You must say Y or M to - "Video For Linux" to use this driver. - - To compile this driver as a module, choose M here: the - module will be called sn9c102. +source "drivers/media/video/sn9c102/Kconfig" config USB_STV680 tristate "USB STV680 (Pencam) Camera support" @@ -511,20 +462,16 @@ config USB_STV680 ---help--- Say Y here if you want to connect this type of camera to your computer's USB port. This includes the Pencam line of cameras. - See for more information and for - a list of supported cameras. - - This driver uses the Video For Linux API. You must say Y or M to - "Video For Linux" (under Multimedia Devices) to use this driver. - Information on this API and pointers to "v4l" programs may be found - at . + See for more information + and for a list of supported cameras. To compile this driver as a module, choose M here: the module will be called stv680. config USB_W9968CF tristate "USB W996[87]CF JPEG Dual Mode Camera support" - depends on USB && VIDEO_DEV && I2C && VIDEO_OVCAMCHIP + depends on USB && VIDEO_DEV && I2C + select VIDEO_OVCAMCHIP ---help--- Say Y here if you want support for cameras based on OV681 or Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips. @@ -534,64 +481,14 @@ config USB_W9968CF resolutions and framerates, but cannot be included in the official Linux kernel for performance purposes. - See for more informations. - - This driver uses the Video For Linux and the I2C APIs. It needs the - OmniVision Camera Chip support as well. You must say Y or M to - "Video For Linux", "I2C Support" and "OmniVision Camera Chip - support" to use this driver. + See for more info. To compile this driver as a module, choose M here: the module will be called w9968cf. -config USB_ZC0301 - tristate "USB ZC0301 Image Processor and Control Chip support" - depends on USB && VIDEO_DEV - ---help--- - Say Y here if you want support for cameras based on the ZC0301 - Image Processor and Control Chip. +source "drivers/media/video/zc0301/Kconfig" - See for more informations. - - This driver uses the Video For Linux API. You must say Y or M to - "Video For Linux" to use this driver. - - To compile this driver as a module, choose M here: the - module will be called zc0301. - -config USB_PWC - tristate "USB Philips Cameras" - depends on USB && VIDEO_DEV - ---help--- - Say Y or M here if you want to use one of these Philips & OEM - webcams: - * Philips PCA645, PCA646 - * Philips PCVC675, PCVC680, PCVC690 - * Philips PCVC720/40, PCVC730, PCVC740, PCVC750 - * Askey VC010 - * Logitech QuickCam Pro 3000, 4000, 'Zoom', 'Notebook Pro' - and 'Orbit'/'Sphere' - * Samsung MPC-C10, MPC-C30 - * Creative Webcam 5, Pro Ex - * SOTEC Afina Eye - * Visionite VCS-UC300, VCS-UM100 - - The PCA635, PCVC665 and PCVC720/20 are not supported by this driver - and never will be, but the 665 and 720/20 are supported by other - drivers. - - See for more information and - installation instructions. - - The built-in microphone is enabled by selecting USB Audio support. - - This driver uses the Video For Linux API. You must say Y or M to - "Video For Linux" (under Character Devices) to use this driver. - Information on this API and pointers to "v4l" programs may be found - at . - - To compile this driver as a module, choose M here: the - module will be called pwc. +source "drivers/media/video/pwc/Kconfig" endmenu # V4L USB devices diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 1c0e72e5a593..b3ea2d63db9b 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_VIDEO_EM28XX) += tvp5150.o obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o obj-$(CONFIG_VIDEO_WM8775) += wm8775.o +obj-$(CONFIG_VIDEO_WM8739) += wm8739.o obj-$(CONFIG_VIDEO_OVCAMCHIP) += ovcamchip/ obj-$(CONFIG_VIDEO_CPIA2) += cpia2/ obj-$(CONFIG_VIDEO_MXB) += saa7111.o tda9840.o tea6415c.o tea6420.o mxb.o @@ -64,9 +65,8 @@ obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o obj-$(CONFIG_VIDEO_CX25840) += cx25840/ obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o - -et61x251-objs := et61x251_core.o et61x251_tas5130d1b.o -zc0301-objs := zc0301_core.o zc0301_pas202bcb.o +obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o +obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o obj-$(CONFIG_USB_DABUSB) += dabusb.o obj-$(CONFIG_USB_DSBR) += dsbr100.o @@ -84,4 +84,4 @@ obj-$(CONFIG_USB_IBMCAM) += usbvideo/ obj-$(CONFIG_USB_KONICAWC) += usbvideo/ obj-$(CONFIG_USB_VICAM) += usbvideo/ -EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/dvb-core +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core diff --git a/drivers/media/video/bt8xx/Makefile b/drivers/media/video/bt8xx/Makefile index 94350f21cdc0..db641a36b197 100644 --- a/drivers/media/video/bt8xx/Makefile +++ b/drivers/media/video/bt8xx/Makefile @@ -9,4 +9,4 @@ bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \ obj-$(CONFIG_VIDEO_BT848) += bttv.o EXTRA_CFLAGS += -I$(src)/.. -EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/dvb-core +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index 74def9c23952..423e954948be 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -973,12 +973,12 @@ audio_mux(struct bttv *btv, int input, int mute) For now this is sufficient. */ switch (input) { case TVAUDIO_INPUT_RADIO: - route.input = MSP_INPUT(MSP_IN_SCART_2, MSP_IN_TUNER_1, - MSP_DSP_OUT_SCART, MSP_DSP_OUT_SCART); + route.input = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1, + MSP_DSP_IN_SCART, MSP_DSP_IN_SCART); break; case TVAUDIO_INPUT_EXTERN: - route.input = MSP_INPUT(MSP_IN_SCART_1, MSP_IN_TUNER_1, - MSP_DSP_OUT_SCART, MSP_DSP_OUT_SCART); + route.input = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, + MSP_DSP_IN_SCART, MSP_DSP_IN_SCART); break; case TVAUDIO_INPUT_INTERN: /* Yes, this is the same input as for RADIO. I doubt @@ -986,8 +986,8 @@ audio_mux(struct bttv *btv, int input, int mute) input is the BTTV_BOARD_AVERMEDIA98. I wonder how that was tested. My guess is that the whole INTERN input does not work. */ - route.input = MSP_INPUT(MSP_IN_SCART_2, MSP_IN_TUNER_1, - MSP_DSP_OUT_SCART, MSP_DSP_OUT_SCART); + route.input = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1, + MSP_DSP_IN_SCART, MSP_DSP_IN_SCART); break; case TVAUDIO_INPUT_TUNER: default: @@ -1023,14 +1023,11 @@ audio_input(struct bttv *btv, int input) static void i2c_vidiocschan(struct bttv *btv) { - struct video_channel c; + v4l2_std_id std = bttv_tvnorms[btv->tvnorm].v4l2_id; - memset(&c,0,sizeof(c)); - c.norm = btv->tvnorm; - c.channel = btv->input; - bttv_call_i2c_clients(btv,VIDIOCSCHAN,&c); + bttv_call_i2c_clients(btv, VIDIOC_S_STD, &std); if (btv->c.type == BTTV_BOARD_VOODOOTV_FM) - bttv_tda9880_setnorm(btv,c.norm); + bttv_tda9880_setnorm(btv,btv->tvnorm); } static int @@ -1184,11 +1181,27 @@ static int get_control(struct bttv *btv, struct v4l2_control *c) break; if (i == BTTV_CTLS) return -EINVAL; - if (i >= 4 && i <= 8) { + if (btv->audio_hook && i >= 4 && i <= 8) { memset(&va,0,sizeof(va)); - bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); - if (btv->audio_hook) - btv->audio_hook(btv,&va,0); + btv->audio_hook(btv,&va,0); + switch (c->id) { + case V4L2_CID_AUDIO_MUTE: + c->value = (VIDEO_AUDIO_MUTE & va.flags) ? 1 : 0; + break; + case V4L2_CID_AUDIO_VOLUME: + c->value = va.volume; + break; + case V4L2_CID_AUDIO_BALANCE: + c->value = va.balance; + break; + case V4L2_CID_AUDIO_BASS: + c->value = va.bass; + break; + case V4L2_CID_AUDIO_TREBLE: + c->value = va.treble; + break; + } + return 0; } switch (c->id) { case V4L2_CID_BRIGHTNESS: @@ -1205,19 +1218,11 @@ static int get_control(struct bttv *btv, struct v4l2_control *c) break; case V4L2_CID_AUDIO_MUTE: - c->value = (VIDEO_AUDIO_MUTE & va.flags) ? 1 : 0; - break; case V4L2_CID_AUDIO_VOLUME: - c->value = va.volume; - break; case V4L2_CID_AUDIO_BALANCE: - c->value = va.balance; - break; case V4L2_CID_AUDIO_BASS: - c->value = va.bass; - break; case V4L2_CID_AUDIO_TREBLE: - c->value = va.treble; + bttv_call_i2c_clients(btv,VIDIOC_G_CTRL,c); break; case V4L2_CID_PRIVATE_CHROMA_AGC: @@ -1269,11 +1274,35 @@ static int set_control(struct bttv *btv, struct v4l2_control *c) break; if (i == BTTV_CTLS) return -EINVAL; - if (i >= 4 && i <= 8) { + if (btv->audio_hook && i >= 4 && i <= 8) { memset(&va,0,sizeof(va)); - bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); - if (btv->audio_hook) - btv->audio_hook(btv,&va,0); + btv->audio_hook(btv,&va,0); + switch (c->id) { + case V4L2_CID_AUDIO_MUTE: + if (c->value) { + va.flags |= VIDEO_AUDIO_MUTE; + audio_mute(btv, 1); + } else { + va.flags &= ~VIDEO_AUDIO_MUTE; + audio_mute(btv, 0); + } + break; + + case V4L2_CID_AUDIO_VOLUME: + va.volume = c->value; + break; + case V4L2_CID_AUDIO_BALANCE: + va.balance = c->value; + break; + case V4L2_CID_AUDIO_BASS: + va.bass = c->value; + break; + case V4L2_CID_AUDIO_TREBLE: + va.treble = c->value; + break; + } + btv->audio_hook(btv,&va,1); + return 0; } switch (c->id) { case V4L2_CID_BRIGHTNESS: @@ -1289,26 +1318,13 @@ static int set_control(struct bttv *btv, struct v4l2_control *c) bt848_sat(btv,c->value); break; case V4L2_CID_AUDIO_MUTE: - if (c->value) { - va.flags |= VIDEO_AUDIO_MUTE; - audio_mute(btv, 1); - } else { - va.flags &= ~VIDEO_AUDIO_MUTE; - audio_mute(btv, 0); - } - break; - + audio_mute(btv, c->value); + /* fall through */ case V4L2_CID_AUDIO_VOLUME: - va.volume = c->value; - break; case V4L2_CID_AUDIO_BALANCE: - va.balance = c->value; - break; case V4L2_CID_AUDIO_BASS: - va.bass = c->value; - break; case V4L2_CID_AUDIO_TREBLE: - va.treble = c->value; + bttv_call_i2c_clients(btv,VIDIOC_S_CTRL,c); break; case V4L2_CID_PRIVATE_CHROMA_AGC: @@ -1364,11 +1380,6 @@ static int set_control(struct bttv *btv, struct v4l2_control *c) default: return -EINVAL; } - if (i >= 4 && i <= 8) { - bttv_call_i2c_clients(btv, VIDIOCSAUDIO, &va); - if (btv->audio_hook) - btv->audio_hook(btv,&va,1); - } return 0; } @@ -1591,12 +1602,16 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) } case VIDIOCSFREQ: { - unsigned long *freq = arg; + struct v4l2_frequency freq; + + memset(&freq, 0, sizeof(freq)); + freq.frequency = *(unsigned long *)arg; mutex_lock(&btv->lock); - btv->freq=*freq; - bttv_call_i2c_clients(btv,VIDIOCSFREQ,freq); + freq.type = btv->radio_user ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + btv->freq = *(unsigned long *)arg; + bttv_call_i2c_clients(btv,VIDIOC_S_FREQUENCY,&freq); if (btv->has_matchbox && btv->radio_user) - tea5757_set_freq(btv,*freq); + tea5757_set_freq(btv,*(unsigned long *)arg); mutex_unlock(&btv->lock); return 0; } @@ -1827,33 +1842,26 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) return -EINVAL; mutex_lock(&btv->lock); memset(t,0,sizeof(*t)); - strcpy(t->name, "Television"); - t->type = V4L2_TUNER_ANALOG_TV; - t->capability = V4L2_TUNER_CAP_NORM; t->rxsubchans = V4L2_TUNER_SUB_MONO; + bttv_call_i2c_clients(btv, VIDIOC_G_TUNER, t); + strcpy(t->name, "Television"); + t->capability = V4L2_TUNER_CAP_NORM; + t->type = V4L2_TUNER_ANALOG_TV; if (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) t->signal = 0xffff; - { - struct video_tuner tuner; - memset(&tuner, 0, sizeof (tuner)); - tuner.rangehigh = 0xffffffffUL; - bttv_call_i2c_clients(btv, VIDIOCGTUNER, &tuner); - t->rangelow = tuner.rangelow; - t->rangehigh = tuner.rangehigh; - } - { + if (btv->audio_hook) { /* Hmmm ... */ struct video_audio va; memset(&va, 0, sizeof(struct video_audio)); - bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); - if (btv->audio_hook) - btv->audio_hook(btv,&va,0); + btv->audio_hook(btv,&va,0); + t->audmode = V4L2_TUNER_MODE_MONO; + t->rxsubchans = V4L2_TUNER_SUB_MONO; if(va.mode & VIDEO_SOUND_STEREO) { - t->audmode = V4L2_TUNER_MODE_STEREO; - t->rxsubchans |= V4L2_TUNER_SUB_STEREO; + t->audmode = V4L2_TUNER_MODE_STEREO; + t->rxsubchans = V4L2_TUNER_SUB_STEREO; } - if(va.mode & VIDEO_SOUND_LANG1) { + if(va.mode & VIDEO_SOUND_LANG2) { t->audmode = V4L2_TUNER_MODE_LANG1; t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; @@ -1872,10 +1880,10 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) if (0 != t->index) return -EINVAL; mutex_lock(&btv->lock); - { + bttv_call_i2c_clients(btv, VIDIOC_S_TUNER, t); + if (btv->audio_hook) { struct video_audio va; memset(&va, 0, sizeof(struct video_audio)); - bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); if (t->audmode == V4L2_TUNER_MODE_MONO) va.mode = VIDEO_SOUND_MONO; else if (t->audmode == V4L2_TUNER_MODE_STEREO || @@ -1885,9 +1893,7 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) va.mode = VIDEO_SOUND_LANG1; else if (t->audmode == V4L2_TUNER_MODE_LANG2) va.mode = VIDEO_SOUND_LANG2; - bttv_call_i2c_clients(btv, VIDIOCSAUDIO, &va); - if (btv->audio_hook) - btv->audio_hook(btv,&va,1); + btv->audio_hook(btv,&va,1); } mutex_unlock(&btv->lock); return 0; @@ -1912,7 +1918,7 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) return -EINVAL; mutex_lock(&btv->lock); btv->freq = f->frequency; - bttv_call_i2c_clients(btv,VIDIOCSFREQ,&btv->freq); + bttv_call_i2c_clients(btv,VIDIOC_S_FREQUENCY,f); if (btv->has_matchbox && btv->radio_user) tea5757_set_freq(btv,btv->freq); mutex_unlock(&btv->lock); @@ -1920,7 +1926,9 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) } case VIDIOC_LOG_STATUS: { + printk(KERN_INFO "bttv%d: ================= START STATUS CARD #%d =================\n", btv->c.nr, btv->c.nr); bttv_call_i2c_clients(btv, VIDIOC_LOG_STATUS, NULL); + printk(KERN_INFO "bttv%d: ================== END STATUS CARD #%d ==================\n", btv->c.nr, btv->c.nr); return 0; } @@ -2870,12 +2878,10 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, return 0; } *c = bttv_ctls[i]; - if (i >= 4 && i <= 8) { + if (btv->audio_hook && i >= 4 && i <= 8) { struct video_audio va; memset(&va,0,sizeof(va)); - bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); - if (btv->audio_hook) - btv->audio_hook(btv,&va,0); + btv->audio_hook(btv,&va,0); switch (bttv_ctls[i].id) { case V4L2_CID_AUDIO_VOLUME: if (!(va.flags & VIDEO_AUDIO_VOLUME)) diff --git a/drivers/media/video/bt8xx/bttv-vbi.c b/drivers/media/video/bt8xx/bttv-vbi.c index e20ff238e409..8c9f0f7cf467 100644 --- a/drivers/media/video/bt8xx/bttv-vbi.c +++ b/drivers/media/video/bt8xx/bttv-vbi.c @@ -184,7 +184,7 @@ void bttv_vbi_try_fmt(struct bttv_fh *fh, struct v4l2_format *f) - tvnorm->vbistart[0]; count1 = (s64) f->fmt.vbi.start[1] + f->fmt.vbi.count[1] - tvnorm->vbistart[1]; - count = clamp (max (count0, count1), 1LL, (s64) VBI_MAXLINES); + count = clamp (max (count0, count1), (s64) 1, (s64) VBI_MAXLINES); f->fmt.vbi.start[0] = tvnorm->vbistart[0]; f->fmt.vbi.start[1] = tvnorm->vbistart[1]; diff --git a/drivers/media/video/cpia.c b/drivers/media/video/cpia.c index 2227c5640c12..85d84e89d8f4 100644 --- a/drivers/media/video/cpia.c +++ b/drivers/media/video/cpia.c @@ -64,14 +64,13 @@ MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("video"); #endif -static unsigned short colorspace_conv = 0; +static unsigned short colorspace_conv; module_param(colorspace_conv, ushort, 0444); MODULE_PARM_DESC(colorspace_conv, - "\n Colorspace conversion:" - "\n0 = disable" - "\n1 = enable" - "\nDefault value is 0" - "\n"); + " Colorspace conversion:" + "\n 0 = disable, 1 = enable" + "\n Default value is 0" + ); #define ABOUT "V4L-Driver for Vision CPiA based cameras" @@ -4042,7 +4041,7 @@ static int __init cpia_init(void) "allowed, it is disabled by default now. Users should fix the " "applications in case they don't work without conversion " "reenabled by setting the 'colorspace_conv' module " - "parameter to 1"); + "parameter to 1\n"); #ifdef CONFIG_PROC_FS proc_cpia_create(); diff --git a/drivers/media/video/cpia2/cpia2.h b/drivers/media/video/cpia2/cpia2.h index 8394283993f6..1764991b0ac9 100644 --- a/drivers/media/video/cpia2/cpia2.h +++ b/drivers/media/video/cpia2/cpia2.h @@ -456,7 +456,7 @@ int cpia2_init_camera(struct camera_data *cam); int cpia2_allocate_buffers(struct camera_data *cam); void cpia2_free_buffers(struct camera_data *cam); long cpia2_read(struct camera_data *cam, - char *buf, unsigned long count, int noblock); + char __user *buf, unsigned long count, int noblock); unsigned int cpia2_poll(struct camera_data *cam, struct file *filp, poll_table *wait); int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma); diff --git a/drivers/media/video/cpia_pp.c b/drivers/media/video/cpia_pp.c index 3021f21aae36..0b00e6027dfb 100644 --- a/drivers/media/video/cpia_pp.c +++ b/drivers/media/video/cpia_pp.c @@ -873,7 +873,7 @@ static int __init cpia_pp_setup(char *str) parport_nr[parport_ptr++] = PPCPIA_PARPORT_NONE; } - return 0; + return 1; } __setup("cpia_pp=", cpia_pp_setup); diff --git a/drivers/media/video/cx25840/Makefile b/drivers/media/video/cx25840/Makefile index 32a896c23d1e..6e8665be8954 100644 --- a/drivers/media/video/cx25840/Makefile +++ b/drivers/media/video/cx25840/Makefile @@ -3,4 +3,4 @@ cx25840-objs := cx25840-core.o cx25840-audio.o cx25840-firmware.o \ obj-$(CONFIG_VIDEO_CX25840) += cx25840.o -EXTRA_CFLAGS += -I$(src)/.. +EXTRA_CFLAGS += -Idrivers/media/video diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c index a4540e858f21..9a4b813152e5 100644 --- a/drivers/media/video/cx25840/cx25840-audio.c +++ b/drivers/media/video/cx25840/cx25840-audio.c @@ -19,8 +19,9 @@ #include #include #include +#include -#include "cx25840.h" +#include "cx25840-core.h" static int set_audclk_freq(struct i2c_client *client, u32 freq) { diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index a65b3cc4bf03..a961bb2ab0fd 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -32,8 +32,9 @@ #include #include #include +#include -#include "cx25840.h" +#include "cx25840-core.h" MODULE_DESCRIPTION("Conexant CX25840 audio/video decoder driver"); MODULE_AUTHOR("Ulf Eklund, Chris Kennedy, Hans Verkuil, Tyler Trafford"); @@ -668,6 +669,7 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, { struct cx25840_state *state = i2c_get_clientdata(client); struct v4l2_tuner *vt = arg; + struct v4l2_routing *route = arg; switch (cmd) { #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -749,19 +751,21 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, state->radio = 1; break; - case VIDIOC_G_INPUT: - *(int *)arg = state->vid_input; + case VIDIOC_INT_G_VIDEO_ROUTING: + route->input = state->vid_input; + route->output = 0; break; - case VIDIOC_S_INPUT: - return set_input(client, *(enum cx25840_video_input *)arg, state->aud_input); + case VIDIOC_INT_S_VIDEO_ROUTING: + return set_input(client, route->input, state->aud_input); - case VIDIOC_S_AUDIO: - { - struct v4l2_audio *input = arg; + case VIDIOC_INT_G_AUDIO_ROUTING: + route->input = state->aud_input; + route->output = 0; + break; - return set_input(client, state->vid_input, input->index); - } + case VIDIOC_INT_S_AUDIO_ROUTING: + return set_input(client, state->vid_input, route->input); case VIDIOC_S_FREQUENCY: input_change(client); diff --git a/drivers/media/video/cx25840/cx25840.h b/drivers/media/video/cx25840/cx25840-core.h similarity index 72% rename from drivers/media/video/cx25840/cx25840.h rename to drivers/media/video/cx25840/cx25840-core.h index dd70664d1dd9..1736929fc204 100644 --- a/drivers/media/video/cx25840/cx25840.h +++ b/drivers/media/video/cx25840/cx25840-core.h @@ -1,4 +1,4 @@ -/* cx25840 API header +/* cx25840 internal API header * * Copyright (C) 2003-2004 Chris Kennedy * @@ -17,8 +17,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef _CX25840_H_ -#define _CX25840_H_ +#ifndef _CX25840_CORE_H_ +#define _CX25840_CORE_H_ #include @@ -32,46 +32,6 @@ providing this information. */ #define CX25840_CID_ENABLE_PVR150_WORKAROUND (V4L2_CID_PRIVATE_BASE+0) -enum cx25840_video_input { - /* Composite video inputs In1-In8 */ - CX25840_COMPOSITE1 = 1, - CX25840_COMPOSITE2, - CX25840_COMPOSITE3, - CX25840_COMPOSITE4, - CX25840_COMPOSITE5, - CX25840_COMPOSITE6, - CX25840_COMPOSITE7, - CX25840_COMPOSITE8, - - /* S-Video inputs consist of one luma input (In1-In4) ORed with one - chroma input (In5-In8) */ - CX25840_SVIDEO_LUMA1 = 0x10, - CX25840_SVIDEO_LUMA2 = 0x20, - CX25840_SVIDEO_LUMA3 = 0x30, - CX25840_SVIDEO_LUMA4 = 0x40, - CX25840_SVIDEO_CHROMA4 = 0x400, - CX25840_SVIDEO_CHROMA5 = 0x500, - CX25840_SVIDEO_CHROMA6 = 0x600, - CX25840_SVIDEO_CHROMA7 = 0x700, - CX25840_SVIDEO_CHROMA8 = 0x800, - - /* S-Video aliases for common luma/chroma combinations */ - CX25840_SVIDEO1 = 0x510, - CX25840_SVIDEO2 = 0x620, - CX25840_SVIDEO3 = 0x730, - CX25840_SVIDEO4 = 0x840, -}; - -enum cx25840_audio_input { - /* Audio inputs: serial or In4-In8 */ - CX25840_AUDIO_SERIAL, - CX25840_AUDIO4 = 4, - CX25840_AUDIO5, - CX25840_AUDIO6, - CX25840_AUDIO7, - CX25840_AUDIO8, -}; - struct cx25840_state { int pvr150_workaround; int radio; diff --git a/drivers/media/video/cx25840/cx25840-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c index e1a7823d82cd..f59ced181c55 100644 --- a/drivers/media/video/cx25840/cx25840-firmware.c +++ b/drivers/media/video/cx25840/cx25840-firmware.c @@ -20,11 +20,22 @@ #include #include #include +#include -#include "cx25840.h" +#include "cx25840-core.h" #define FWFILE "v4l-cx25840.fw" -#define FWSEND 1024 + +/* + * Mike Isely - The FWSEND parameter controls the + * size of the firmware chunks sent down the I2C bus to the chip. + * Previously this had been set to 1024 but unfortunately some I2C + * implementations can't transfer data in such big gulps. + * Specifically, the pvrusb2 driver has a hard limit of around 60 + * bytes, due to the encapsulation there of I2C traffic into USB + * messages. So we have to significantly reduce this parameter. + */ +#define FWSEND 48 #define FWDEV(x) &((x)->adapter->dev) diff --git a/drivers/media/video/cx25840/cx25840-vbi.c b/drivers/media/video/cx25840/cx25840-vbi.c index e96fd1f1d6dc..57feca288d2b 100644 --- a/drivers/media/video/cx25840/cx25840-vbi.c +++ b/drivers/media/video/cx25840/cx25840-vbi.c @@ -19,8 +19,9 @@ #include #include #include +#include -#include "cx25840.h" +#include "cx25840-core.h" static int odd_parity(u8 c) { @@ -151,7 +152,7 @@ int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg) case VIDIOC_G_FMT: { static u16 lcr2vbi[] = { - 0, V4L2_SLICED_TELETEXT_PAL_B, 0, /* 1 */ + 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ 0, V4L2_SLICED_WSS_625, 0, /* 4 */ V4L2_SLICED_CAPTION_525, /* 6 */ 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */ @@ -231,7 +232,7 @@ int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg) for (i = 7; i <= 23; i++) { for (x = 0; x <= 1; x++) { switch (svbi->service_lines[1-x][i]) { - case V4L2_SLICED_TELETEXT_PAL_B: + case V4L2_SLICED_TELETEXT_B: lcr[i] |= 1 << (4 * x); break; case V4L2_SLICED_WSS_625: @@ -282,7 +283,7 @@ int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg) switch (id2) { case 1: - id2 = V4L2_SLICED_TELETEXT_PAL_B; + id2 = V4L2_SLICED_TELETEXT_B; break; case 4: id2 = V4L2_SLICED_WSS_625; diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig index ff0f72340d69..630273992a41 100644 --- a/drivers/media/video/cx88/Kconfig +++ b/drivers/media/video/cx88/Kconfig @@ -1,3 +1,7 @@ +config VIDEO_CX88_VP3054 + tristate + depends on VIDEO_CX88_DVB && DVB_MT352 + config VIDEO_CX88 tristate "Conexant 2388x (bt878 successor) support" depends on VIDEO_DEV && PCI && I2C @@ -25,7 +29,7 @@ config VIDEO_CX88_ALSA It only works with boards with function 01 enabled. To check if your board supports, use lspci -n. - If supported, you should see 1471:8801 or 1471:8811 + If supported, you should see 14f1:8801 or 14f1:8811 PCI device. To compile this driver as a module, choose M here: the @@ -73,10 +77,11 @@ config VIDEO_CX88_DVB_MT352 This adds DVB-T support for cards based on the Connexant 2388x chip and the MT352 demodulator. -config VIDEO_CX88_VP3054 - tristate "VP-3054 Secondary I2C Bus Support" - default m - depends on DVB_MT352 +config VIDEO_CX88_DVB_VP3054 + bool "VP-3054 Secondary I2C Bus Support" + default y + depends on VIDEO_CX88_DVB_MT352 + select VIDEO_CX88_VP3054 ---help--- This adds DVB-T support for cards based on the Connexant 2388x chip and the MT352 demodulator, diff --git a/drivers/media/video/cx88/Makefile b/drivers/media/video/cx88/Makefile index 6482b9aa6a1f..0dcd09b9b727 100644 --- a/drivers/media/video/cx88/Makefile +++ b/drivers/media/video/cx88/Makefile @@ -8,9 +8,9 @@ obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o obj-$(CONFIG_VIDEO_CX88_ALSA) += cx88-alsa.o obj-$(CONFIG_VIDEO_CX88_VP3054) += cx88-vp3054-i2c.o -EXTRA_CFLAGS += -I$(src)/.. -EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/dvb-core -EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/frontends +EXTRA_CFLAGS += -Idrivers/media/video +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core +EXTRA_CFLAGS += -Idrivers/media/dvb/frontends extra-cflags-$(CONFIG_VIDEO_BUF_DVB) += -DHAVE_VIDEO_BUF_DVB=1 extra-cflags-$(CONFIG_DVB_CX22702) += -DHAVE_CX22702=1 diff --git a/drivers/media/video/em28xx/Makefile b/drivers/media/video/em28xx/Makefile index da457a05b0dd..826d0e340753 100644 --- a/drivers/media/video/em28xx/Makefile +++ b/drivers/media/video/em28xx/Makefile @@ -3,4 +3,4 @@ em28xx-objs := em28xx-video.o em28xx-i2c.o em28xx-cards.o em28xx-core.o \ obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o -EXTRA_CFLAGS += -I$(src)/.. +EXTRA_CFLAGS += -Idrivers/media/video diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index f62fd706b45a..3ba3439db580 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -151,8 +151,8 @@ struct em28xx_board em28xx_boards[] = { },{ .type = EM28XX_VMUX_SVIDEO, .vmux = 2, - .amux = MSP_INPUT(MSP_IN_SCART_1, MSP_IN_TUNER_1, - MSP_DSP_OUT_SCART, MSP_DSP_OUT_SCART), + .amux = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, + MSP_DSP_IN_SCART, MSP_DSP_IN_SCART), }}, }, [EM2820_BOARD_MSI_VOX_USB_2] = { diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index dfba33d0fa61..ddc92cbb5276 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -222,7 +222,7 @@ static void video_mux(struct em28xx *dev, int index) if (dev->i2s_speed) em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ, &dev->i2s_speed); route.input = dev->ctl_ainput; - route.output = MSP_OUTPUT(MSP_OUT_SCART1_DA); + route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); /* Note: this is msp3400 specific */ em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING, &route); ainput = EM28XX_AUDIO_SRC_TUNER; @@ -1141,26 +1141,16 @@ static int em28xx_do_ioctl(struct inode *inode, struct file *filp, case VIDIOC_G_TUNER: { struct v4l2_tuner *t = arg; - int status = 0; if (0 != t->index) return -EINVAL; memset(t, 0, sizeof(*t)); strcpy(t->name, "Tuner"); - t->type = V4L2_TUNER_ANALOG_TV; - t->capability = V4L2_TUNER_CAP_NORM; - t->rangehigh = 0xffffffffUL; /* FIXME: set correct range */ -/* t->signal = 0xffff;*/ -/* em28xx_i2c_call_clients(dev,VIDIOC_G_TUNER,t);*/ - /* No way to get signal strength? */ mutex_lock(&dev->lock); - em28xx_i2c_call_clients(dev, DECODER_GET_STATUS, - &status); + /* let clients fill in the remainder of this struct */ + em28xx_i2c_call_clients(dev, cmd, t); mutex_unlock(&dev->lock); - t->signal = - (status & DECODER_STATUS_GOOD) != 0 ? 0xffff : 0; - em28xx_videodbg("VIDIO_G_TUNER: signal=%x, afc=%x\n", t->signal, t->afc); return 0; @@ -1168,26 +1158,13 @@ static int em28xx_do_ioctl(struct inode *inode, struct file *filp, case VIDIOC_S_TUNER: { struct v4l2_tuner *t = arg; - int status = 0; if (0 != t->index) return -EINVAL; - memset(t, 0, sizeof(*t)); - strcpy(t->name, "Tuner"); - t->type = V4L2_TUNER_ANALOG_TV; - t->capability = V4L2_TUNER_CAP_NORM; - t->rangehigh = 0xffffffffUL; /* FIXME: set correct range */ -/* t->signal = 0xffff; */ - /* No way to get signal strength? */ mutex_lock(&dev->lock); - em28xx_i2c_call_clients(dev, DECODER_GET_STATUS, - &status); + /* let clients handle this */ + em28xx_i2c_call_clients(dev, cmd, t); mutex_unlock(&dev->lock); - t->signal = - (status & DECODER_STATUS_GOOD) != 0 ? 0xffff : 0; - - em28xx_videodbg("VIDIO_S_TUNER: signal=%x, afc=%x\n", - t->signal, t->afc); return 0; } case VIDIOC_G_FREQUENCY: diff --git a/drivers/media/video/et61x251/Kconfig b/drivers/media/video/et61x251/Kconfig new file mode 100644 index 000000000000..6c43a90c6569 --- /dev/null +++ b/drivers/media/video/et61x251/Kconfig @@ -0,0 +1,14 @@ +config USB_ET61X251 + tristate "USB ET61X[12]51 PC Camera Controller support" + depends on USB && VIDEO_DEV + ---help--- + Say Y here if you want support for cameras based on Etoms ET61X151 + or ET61X251 PC Camera Controllers. + + See for more info. + + This driver uses the Video For Linux API. You must say Y or M to + "Video For Linux" to use this driver. + + To compile this driver as a module, choose M here: the + module will be called et61x251. diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index 95bacf435414..7e66d83fe0ce 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -411,6 +411,9 @@ static int ir_probe(struct i2c_adapter *adap) case I2C_HW_B_BT848: probe = probe_bttv; break; + case I2C_HW_B_CX2341X: + probe = probe_bttv; + break; case I2C_HW_SAA7134: probe = probe_saa7134; break; diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c index c40e8ba9a2ea..b806999d6e0f 100644 --- a/drivers/media/video/msp3400-driver.c +++ b/drivers/media/video/msp3400-driver.c @@ -279,20 +279,8 @@ void msp_set_scart(struct i2c_client *client, int in, int out) msp_write_dsp(client, 0x13, state->acb); /* Sets I2S speed 0 = 1.024 Mbps, 1 = 2.048 Mbps */ - msp_write_dem(client, 0x40, state->i2s_mode); -} - -void msp_set_mute(struct i2c_client *client) -{ - struct msp_state *state = i2c_get_clientdata(client); - - v4l_dbg(1, msp_debug, client, "mute audio\n"); - msp_write_dsp(client, 0x0000, 0); - msp_write_dsp(client, 0x0007, 1); - if (state->has_scart2_out_volume) - msp_write_dsp(client, 0x0040, 1); - if (state->has_headphones) - msp_write_dsp(client, 0x0006, 0); + if (state->has_i2s_conf) + msp_write_dem(client, 0x40, state->i2s_mode); } void msp_set_audio(struct i2c_client *client) @@ -300,17 +288,19 @@ void msp_set_audio(struct i2c_client *client) struct msp_state *state = i2c_get_clientdata(client); int bal = 0, bass, treble, loudness; int val = 0; + int reallymuted = state->muted | state->scan_in_progress; - if (!state->muted) + if (!reallymuted) val = (state->volume * 0x7f / 65535) << 8; - v4l_dbg(1, msp_debug, client, "mute=%s volume=%d\n", - state->muted ? "on" : "off", state->volume); + v4l_dbg(1, msp_debug, client, "mute=%s scanning=%s volume=%d\n", + state->muted ? "on" : "off", state->scan_in_progress ? "yes" : "no", + state->volume); msp_write_dsp(client, 0x0000, val); - msp_write_dsp(client, 0x0007, state->muted ? 0x1 : (val | 0x1)); + msp_write_dsp(client, 0x0007, reallymuted ? 0x1 : (val | 0x1)); if (state->has_scart2_out_volume) - msp_write_dsp(client, 0x0040, state->muted ? 0x1 : (val | 0x1)); + msp_write_dsp(client, 0x0040, reallymuted ? 0x1 : (val | 0x1)); if (state->has_headphones) msp_write_dsp(client, 0x0006, val); if (!state->has_sound_processing) @@ -346,7 +336,6 @@ static void msp_wake_thread(struct i2c_client *client) if (NULL == state->kthread) return; - msp_set_mute(client); state->watch_stereo = 0; state->restart = 1; wake_up_interruptible(&state->wq); @@ -374,19 +363,15 @@ int msp_sleep(struct msp_state *state, int timeout) /* ------------------------------------------------------------------------ */ -static int msp_mode_v4l2_to_v4l1(int rxsubchans) +static int msp_mode_v4l2_to_v4l1(int rxsubchans, int audmode) { - int mode = 0; - - if (rxsubchans & V4L2_TUNER_SUB_STEREO) - mode |= VIDEO_SOUND_STEREO; - if (rxsubchans & V4L2_TUNER_SUB_LANG2) - mode |= VIDEO_SOUND_LANG2 | VIDEO_SOUND_STEREO; - if (rxsubchans & V4L2_TUNER_SUB_LANG1) - mode |= VIDEO_SOUND_LANG1 | VIDEO_SOUND_STEREO; - if (mode == 0) - mode |= VIDEO_SOUND_MONO; - return mode; + if (rxsubchans == V4L2_TUNER_SUB_MONO) + return VIDEO_SOUND_MONO; + if (rxsubchans == V4L2_TUNER_SUB_STEREO) + return VIDEO_SOUND_STEREO; + if (audmode == V4L2_TUNER_MODE_LANG2) + return VIDEO_SOUND_LANG2; + return VIDEO_SOUND_LANG1; } static int msp_mode_v4l1_to_v4l2(int mode) @@ -605,7 +590,7 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) break; if (state->opmode == OPMODE_AUTOSELECT) msp_detect_stereo(client); - va->mode = msp_mode_v4l2_to_v4l1(state->rxsubchans); + va->mode = msp_mode_v4l2_to_v4l1(state->rxsubchans, state->audmode); break; } @@ -620,7 +605,8 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) state->treble = va->treble; msp_set_audio(client); - if (va->mode != 0 && state->radio == 0) { + if (va->mode != 0 && state->radio == 0 && + state->audmode != msp_mode_v4l1_to_v4l2(va->mode)) { state->audmode = msp_mode_v4l1_to_v4l2(va->mode); msp_set_audmode(client); } @@ -687,21 +673,23 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) int sc_in = rt->input & 0x7; int sc1_out = rt->output & 0xf; int sc2_out = (rt->output >> 4) & 0xf; - u16 val; + u16 val, reg; + if (state->routing.input == rt->input && + state->routing.output == rt->output) + break; state->routing = *rt; - if (state->opmode == OPMODE_AUTOSELECT) { - val = msp_read_dem(client, 0x30) & ~0x100; - msp_write_dem(client, 0x30, val | (tuner ? 0x100 : 0)); - } else { - val = msp_read_dem(client, 0xbb) & ~0x100; - msp_write_dem(client, 0xbb, val | (tuner ? 0x100 : 0)); - } msp_set_scart(client, sc_in, 0); msp_set_scart(client, sc1_out, 1); msp_set_scart(client, sc2_out, 2); msp_set_audmode(client); - msp_wake_thread(client); + reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb; + val = msp_read_dem(client, reg); + if (tuner != ((val >> 8) & 1)) { + msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8)); + /* wake thread when a new tuner input is chosen */ + msp_wake_thread(client); + } break; } @@ -715,7 +703,7 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) msp_detect_stereo(client); vt->audmode = state->audmode; vt->rxsubchans = state->rxsubchans; - vt->capability = V4L2_TUNER_CAP_STEREO | + vt->capability |= V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; break; } @@ -726,6 +714,8 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) if (state->radio) /* TODO: add mono/stereo support for radio */ break; + if (state->audmode == vt->audmode) + break; state->audmode = vt->audmode; /* only set audmode */ msp_set_audmode(client); @@ -887,7 +877,7 @@ static int msp_attach(struct i2c_adapter *adapter, int address, int kind) memset(state, 0, sizeof(*state)); state->v4l2_std = V4L2_STD_NTSC; - state->audmode = V4L2_TUNER_MODE_LANG1; + state->audmode = V4L2_TUNER_MODE_STEREO; state->volume = 58880; /* 0db gain */ state->balance = 32768; /* 0db gain */ state->bass = 32768; @@ -931,13 +921,16 @@ static int msp_attach(struct i2c_adapter *adapter, int address, int kind) state->has_radio = msp_revision >= 'G'; /* Has headphones output: not for stripped down products */ state->has_headphones = msp_prod_lo < 5; + /* Has scart2 input: not in stripped down products of the '3' family */ + state->has_scart2 = msp_family >= 4 || msp_prod_lo < 7; + /* Has scart3 input: not in stripped down products of the '3' family */ + state->has_scart3 = msp_family >= 4 || msp_prod_lo < 5; /* Has scart4 input: not in pre D revisions, not in stripped D revs */ state->has_scart4 = msp_family >= 4 || (msp_revision >= 'D' && msp_prod_lo < 5); - /* Has scart2 and scart3 inputs and scart2 output: not in stripped - down products of the '3' family */ - state->has_scart23_in_scart2_out = msp_family >= 4 || msp_prod_lo < 5; + /* Has scart2 output: not in stripped down products of the '3' family */ + state->has_scart2_out = msp_family >= 4 || msp_prod_lo < 5; /* Has scart2 a volume control? Not in pre-D revisions. */ - state->has_scart2_out_volume = msp_revision > 'C' && state->has_scart23_in_scart2_out; + state->has_scart2_out_volume = msp_revision > 'C' && state->has_scart2_out; /* Has a configurable i2s out? */ state->has_i2s_conf = msp_revision >= 'G' && msp_prod_lo < 7; /* Has subwoofer output: not in pre-D revs and not in stripped down products */ diff --git a/drivers/media/video/msp3400-driver.h b/drivers/media/video/msp3400-driver.h index 1940748bb633..4e451049013d 100644 --- a/drivers/media/video/msp3400-driver.h +++ b/drivers/media/video/msp3400-driver.h @@ -54,8 +54,10 @@ struct msp_state { u8 has_radio; u8 has_headphones; u8 has_ntsc_jp_d_k3; + u8 has_scart2; + u8 has_scart3; u8 has_scart4; - u8 has_scart23_in_scart2_out; + u8 has_scart2_out; u8 has_scart2_out_volume; u8 has_i2s_conf; u8 has_subwoofer; @@ -83,6 +85,7 @@ struct msp_state { int volume, muted; int balance, loudness; int bass, treble; + int scan_in_progress; /* thread */ struct task_struct *kthread; @@ -98,7 +101,6 @@ int msp_read_dem(struct i2c_client *client, int addr); int msp_read_dsp(struct i2c_client *client, int addr); int msp_reset(struct i2c_client *client); void msp_set_scart(struct i2c_client *client, int in, int out); -void msp_set_mute(struct i2c_client *client); void msp_set_audio(struct i2c_client *client); int msp_sleep(struct msp_state *state, int timeout); diff --git a/drivers/media/video/msp3400-kthreads.c b/drivers/media/video/msp3400-kthreads.c index c3984ea9ca07..633a10213789 100644 --- a/drivers/media/video/msp3400-kthreads.c +++ b/drivers/media/video/msp3400-kthreads.c @@ -170,7 +170,7 @@ static void msp_set_source(struct i2c_client *client, u16 src) msp_write_dsp(client, 0x000a, src); msp_write_dsp(client, 0x000b, src); msp_write_dsp(client, 0x000c, src); - if (state->has_scart23_in_scart2_out) + if (state->has_scart2_out) msp_write_dsp(client, 0x0041, src); } @@ -228,6 +228,7 @@ static void msp3400c_set_audmode(struct i2c_client *client) char *modestr = (state->audmode >= 0 && state->audmode < 5) ? strmode[state->audmode] : "unknown"; int src = 0; /* channel source: FM/AM, nicam or SCART */ + int audmode = state->audmode; if (state->opmode == OPMODE_AUTOSELECT) { /* this method would break everything, let's make sure @@ -239,11 +240,29 @@ static void msp3400c_set_audmode(struct i2c_client *client) return; } + /* Note: for the C and D revs no NTSC stereo + SAP is possible as + the hardware does not support SAP. So the rxsubchans combination + of STEREO | LANG2 does not occur. */ + + /* switch to mono if only mono is available */ + if (state->rxsubchans == V4L2_TUNER_SUB_MONO) + audmode = V4L2_TUNER_MODE_MONO; + /* if bilingual */ + else if (state->rxsubchans & V4L2_TUNER_SUB_LANG2) { + /* and mono or stereo, then fallback to lang1 */ + if (audmode == V4L2_TUNER_MODE_MONO || + audmode == V4L2_TUNER_MODE_STEREO) + audmode = V4L2_TUNER_MODE_LANG1; + } + /* if stereo, and audmode is not mono, then switch to stereo */ + else if (audmode != V4L2_TUNER_MODE_MONO) + audmode = V4L2_TUNER_MODE_STEREO; + /* switch demodulator */ switch (state->mode) { case MSP_MODE_FM_TERRA: v4l_dbg(1, msp_debug, client, "FM set_audmode: %s\n", modestr); - switch (state->audmode) { + switch (audmode) { case V4L2_TUNER_MODE_STEREO: msp_write_dsp(client, 0x000e, 0x3001); break; @@ -257,7 +276,7 @@ static void msp3400c_set_audmode(struct i2c_client *client) break; case MSP_MODE_FM_SAT: v4l_dbg(1, msp_debug, client, "SAT set_audmode: %s\n", modestr); - switch (state->audmode) { + switch (audmode) { case V4L2_TUNER_MODE_MONO: msp3400c_set_carrier(client, MSP_CARRIER(6.5), MSP_CARRIER(6.5)); break; @@ -296,7 +315,8 @@ static void msp3400c_set_audmode(struct i2c_client *client) } /* switch audio */ - switch (state->audmode) { + v4l_dbg(1, msp_debug, client, "set audmode %d\n", audmode); + switch (audmode) { case V4L2_TUNER_MODE_STEREO: case V4L2_TUNER_MODE_LANG1_LANG2: src |= 0x0020; @@ -314,10 +334,6 @@ static void msp3400c_set_audmode(struct i2c_client *client) src = 0x0030; break; case V4L2_TUNER_MODE_LANG1: - /* switch to stereo for stereo transmission, otherwise - keep first language */ - if (state->rxsubchans & V4L2_TUNER_SUB_STEREO) - src |= 0x0020; break; case V4L2_TUNER_MODE_LANG2: src |= 0x0010; @@ -367,7 +383,7 @@ static int msp3400c_detect_stereo(struct i2c_client *client) if (val > 32767) val -= 65536; v4l_dbg(2, msp_debug, client, "stereo detect register: %d\n", val); - if (val > 4096) { + if (val > 8192) { rxsubchans = V4L2_TUNER_SUB_STEREO; } else if (val < -4096) { rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; @@ -464,19 +480,22 @@ int msp3400c_thread(void *data) if (state->radio || MSP_MODE_EXTERN == state->mode) { /* no carrier scan, just unmute */ v4l_dbg(1, msp_debug, client, "thread: no carrier scan\n"); + state->scan_in_progress = 0; msp_set_audio(client); continue; } - /* mute */ - msp_set_mute(client); + /* mute audio */ + state->scan_in_progress = 1; + msp_set_audio(client); + msp3400c_set_mode(client, MSP_MODE_AM_DETECT); val1 = val2 = 0; max1 = max2 = -1; state->watch_stereo = 0; state->nicam_on = 0; - /* some time for the tuner to sync */ + /* wait for tuner to settle down after a channel change */ if (msp_sleep(state, 200)) goto restart; @@ -552,7 +571,6 @@ int msp3400c_thread(void *data) /* B/G NICAM */ state->second = msp3400c_carrier_detect_55[max2].cdo; msp3400c_set_mode(client, MSP_MODE_FM_NICAM1); - msp3400c_set_carrier(client, state->second, state->main); state->nicam_on = 1; state->watch_stereo = 1; } else { @@ -563,7 +581,6 @@ int msp3400c_thread(void *data) /* PAL I NICAM */ state->second = MSP_CARRIER(6.552); msp3400c_set_mode(client, MSP_MODE_FM_NICAM2); - msp3400c_set_carrier(client, state->second, state->main); state->nicam_on = 1; state->watch_stereo = 1; break; @@ -577,13 +594,11 @@ int msp3400c_thread(void *data) /* L NICAM or AM-mono */ state->second = msp3400c_carrier_detect_65[max2].cdo; msp3400c_set_mode(client, MSP_MODE_AM_NICAM); - msp3400c_set_carrier(client, state->second, state->main); state->watch_stereo = 1; } else if (max2 == 0 && state->has_nicam) { /* D/K NICAM */ state->second = msp3400c_carrier_detect_65[max2].cdo; msp3400c_set_mode(client, MSP_MODE_FM_NICAM1); - msp3400c_set_carrier(client, state->second, state->main); state->nicam_on = 1; state->watch_stereo = 1; } else { @@ -595,25 +610,25 @@ int msp3400c_thread(void *data) no_second: state->second = msp3400c_carrier_detect_main[max1].cdo; msp3400c_set_mode(client, MSP_MODE_FM_TERRA); - msp3400c_set_carrier(client, state->second, state->main); - state->rxsubchans = V4L2_TUNER_SUB_MONO; break; } + msp3400c_set_carrier(client, state->second, state->main); /* unmute */ - msp_set_audio(client); + state->scan_in_progress = 0; msp3400c_set_audmode(client); + msp_set_audio(client); if (msp_debug) msp3400c_print_mode(client); /* monitor tv audio mode, the first time don't wait so long to get a quick stereo/bilingual result */ - if (msp_sleep(state, 1000)) - goto restart; + count = 3; while (state->watch_stereo) { - if (msp_sleep(state, 5000)) + if (msp_sleep(state, count ? 1000 : 5000)) goto restart; + if (count) count--; watch_stereo(client); } } @@ -626,7 +641,7 @@ int msp3410d_thread(void *data) { struct i2c_client *client = data; struct msp_state *state = i2c_get_clientdata(client); - int val, i, std; + int val, i, std, count; v4l_dbg(1, msp_debug, client, "msp3410 daemon started\n"); @@ -644,16 +659,14 @@ int msp3410d_thread(void *data) if (state->mode == MSP_MODE_EXTERN) { /* no carrier scan needed, just unmute */ v4l_dbg(1, msp_debug, client, "thread: no carrier scan\n"); + state->scan_in_progress = 0; msp_set_audio(client); continue; } - /* put into sane state (and mute) */ - msp_reset(client); - - /* some time for the tuner to sync */ - if (msp_sleep(state,200)) - goto restart; + /* mute audio */ + state->scan_in_progress = 1; + msp_set_audio(client); /* start autodetect. Note: autodetect is not supported for NTSC-M and radio, hence we force the standard in those cases. */ @@ -664,6 +677,10 @@ int msp3410d_thread(void *data) state->watch_stereo = 0; state->nicam_on = 0; + /* wait for tuner to settle down after a channel change */ + if (msp_sleep(state, 200)) + goto restart; + if (msp_debug) v4l_dbg(2, msp_debug, client, "setting standard: %s (0x%04x)\n", msp_standard_std_name(std), std); @@ -693,6 +710,7 @@ int msp3410d_thread(void *data) state->main = msp_stdlist[i].main; state->second = msp_stdlist[i].second; state->std = val; + state->rxsubchans = V4L2_TUNER_SUB_MONO; if (msp_amsound && !state->radio && (state->v4l2_std & V4L2_STD_SECAM) && (val != 0x0009)) { @@ -714,20 +732,17 @@ int msp3410d_thread(void *data) else state->mode = MSP_MODE_FM_NICAM1; /* just turn on stereo */ - state->rxsubchans = V4L2_TUNER_SUB_STEREO; state->nicam_on = 1; state->watch_stereo = 1; break; case 0x0009: state->mode = MSP_MODE_AM_NICAM; - state->rxsubchans = V4L2_TUNER_SUB_MONO; state->nicam_on = 1; state->watch_stereo = 1; break; case 0x0020: /* BTSC */ /* The pre-'G' models only have BTSC-mono */ state->mode = MSP_MODE_BTSC; - state->rxsubchans = V4L2_TUNER_SUB_MONO; break; case 0x0040: /* FM radio */ state->mode = MSP_MODE_FM_RADIO; @@ -737,15 +752,12 @@ int msp3410d_thread(void *data) msp3400c_set_mode(client, MSP_MODE_FM_RADIO); msp3400c_set_carrier(client, MSP_CARRIER(10.7), MSP_CARRIER(10.7)); - /* scart routing (this doesn't belong here I think) */ - msp_set_scart(client,SCART_IN2,0); break; case 0x0002: case 0x0003: case 0x0004: case 0x0005: state->mode = MSP_MODE_FM_TERRA; - state->rxsubchans = V4L2_TUNER_SUB_MONO; state->watch_stereo = 1; break; } @@ -759,20 +771,19 @@ int msp3410d_thread(void *data) if (state->has_i2s_conf) msp_write_dem(client, 0x40, state->i2s_mode); - /* unmute, restore misc registers */ - msp_set_audio(client); - - msp_write_dsp(client, 0x13, state->acb); + /* unmute */ msp3400c_set_audmode(client); + state->scan_in_progress = 0; + msp_set_audio(client); /* monitor tv audio mode, the first time don't wait so long to get a quick stereo/bilingual result */ - if (msp_sleep(state, 1000)) - goto restart; + count = 3; while (state->watch_stereo) { - watch_stereo(client); - if (msp_sleep(state, 5000)) + if (msp_sleep(state, count ? 1000 : 5000)) goto restart; + if (count) count--; + watch_stereo(client); } } v4l_dbg(1, msp_debug, client, "thread: exit\n"); @@ -829,27 +840,27 @@ static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in) source = 0; /* mono only */ matrix = 0x30; break; - case V4L2_TUNER_MODE_LANG1: - source = 3; /* stereo or A */ - matrix = 0x00; - break; case V4L2_TUNER_MODE_LANG2: source = 4; /* stereo or B */ matrix = 0x10; break; - case V4L2_TUNER_MODE_STEREO: case V4L2_TUNER_MODE_LANG1_LANG2: - default: source = 1; /* stereo or A|B */ matrix = 0x20; break; + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1: + default: + source = 3; /* stereo or A */ + matrix = 0x00; + break; } - if (in == MSP_DSP_OUT_TUNER) + if (in == MSP_DSP_IN_TUNER) source = (source << 8) | 0x20; /* the msp34x2g puts the MAIN_AVC, MAIN and AUX sources in 12, 13, 14 instead of 11, 12, 13. So we add one for that msp version. */ - else if (in >= MSP_DSP_OUT_MAIN_AVC && state->has_dolby_pro_logic) + else if (in >= MSP_DSP_IN_MAIN_AVC && state->has_dolby_pro_logic) source = ((in + 1) << 8) | matrix; else source = (in << 8) | matrix; @@ -869,7 +880,7 @@ static void msp34xxg_set_sources(struct i2c_client *client) msp34xxg_set_source(client, 0x000c, (in >> 4) & 0xf); msp34xxg_set_source(client, 0x0009, (in >> 8) & 0xf); msp34xxg_set_source(client, 0x000a, (in >> 12) & 0xf); - if (state->has_scart23_in_scart2_out) + if (state->has_scart2_out) msp34xxg_set_source(client, 0x0041, (in >> 16) & 0xf); msp34xxg_set_source(client, 0x000b, (in >> 20) & 0xf); } @@ -887,10 +898,6 @@ static void msp34xxg_reset(struct i2c_client *client) msp_reset(client); - /* make sure that input/output is muted (paranoid mode) */ - /* ACB, mute DSP input, mute SCART 1 */ - msp_write_dsp(client, 0x13, 0x0f20); - if (state->has_i2s_conf) msp_write_dem(client, 0x40, state->i2s_mode); @@ -1028,7 +1035,7 @@ static void msp34xxg_set_audmode(struct i2c_client *client) if (state->std == 0x20) { if ((state->rxsubchans & V4L2_TUNER_SUB_SAP) && - (state->audmode == V4L2_TUNER_MODE_STEREO || + (state->audmode == V4L2_TUNER_MODE_LANG1_LANG2 || state->audmode == V4L2_TUNER_MODE_LANG2)) { msp_write_dem(client, 0x20, 0x21); } else { diff --git a/drivers/media/video/pwc/Kconfig b/drivers/media/video/pwc/Kconfig new file mode 100644 index 000000000000..86376556f108 --- /dev/null +++ b/drivers/media/video/pwc/Kconfig @@ -0,0 +1,28 @@ +config USB_PWC + tristate "USB Philips Cameras" + depends on USB && VIDEO_DEV + ---help--- + Say Y or M here if you want to use one of these Philips & OEM + webcams: + * Philips PCA645, PCA646 + * Philips PCVC675, PCVC680, PCVC690 + * Philips PCVC720/40, PCVC730, PCVC740, PCVC750 + * Askey VC010 + * Logitech QuickCam Pro 3000, 4000, 'Zoom', 'Notebook Pro' + and 'Orbit'/'Sphere' + * Samsung MPC-C10, MPC-C30 + * Creative Webcam 5, Pro Ex + * SOTEC Afina Eye + * Visionite VCS-UC300, VCS-UM100 + + The PCA635, PCVC665 and PCVC720/20 are not supported by this driver + and never will be, but the 665 and 720/20 are supported by other + drivers. + + See for more information and + installation instructions. + + The built-in microphone is enabled by selecting USB Audio support. + + To compile this driver as a module, choose M here: the + module will be called pwc. diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c index b05015282601..dceebc0b1250 100644 --- a/drivers/media/video/saa7115.c +++ b/drivers/media/video/saa7115.c @@ -40,6 +40,7 @@ #include #include #include +#include #include MODULE_DESCRIPTION("Philips SAA7113/SAA7114/SAA7115 video decoder driver"); @@ -53,7 +54,7 @@ module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Debug level (0-1)"); static unsigned short normal_i2c[] = { - 0x4a >>1, 0x48 >>1, /* SAA7113 */ + 0x4a >> 1, 0x48 >> 1, /* SAA7113 */ 0x42 >> 1, 0x40 >> 1, /* SAA7114 and SAA7115 */ I2C_CLIENT_END }; @@ -722,16 +723,16 @@ static void saa7115_set_v4lstd(struct i2c_client *client, v4l2_std_id std) 100 reserved NTSC-Japan (3.58MHz) */ if (state->ident == V4L2_IDENT_SAA7113) { - u8 reg = saa7115_read(client, 0x0e) & 0x8f; + u8 reg = saa7115_read(client, 0x0e) & 0x8f; if (std == V4L2_STD_PAL_M) { - reg|=0x30; + reg |= 0x30; } else if (std == V4L2_STD_PAL_N) { - reg|=0x20; + reg |= 0x20; } else if (std == V4L2_STD_PAL_60) { - reg|=0x10; + reg |= 0x10; } else if (std == V4L2_STD_NTSC_M_JP) { - reg|=0x40; + reg |= 0x40; } saa7115_write(client, 0x0e, reg); } @@ -811,7 +812,7 @@ static void saa7115_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_fo u8 lcr[24]; int i, x; - /* saa7113/71144 doesn't yet support VBI */ + /* saa7113/7114 doesn't yet support VBI */ if (state->ident != V4L2_IDENT_SAA7115) return; @@ -851,7 +852,7 @@ static void saa7115_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_fo case 0: lcr[i] |= 0xf << (4 * x); break; - case V4L2_SLICED_TELETEXT_PAL_B: + case V4L2_SLICED_TELETEXT_B: lcr[i] |= 1 << (4 * x); break; case V4L2_SLICED_CAPTION_525: @@ -880,7 +881,7 @@ static void saa7115_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_fo static int saa7115_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt) { static u16 lcr2vbi[] = { - 0, V4L2_SLICED_TELETEXT_PAL_B, 0, /* 1 */ + 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ 0, V4L2_SLICED_CAPTION_525, /* 4 */ V4L2_SLICED_WSS_625, 0, /* 5 */ V4L2_SLICED_VPS, 0, 0, 0, 0, /* 7 */ @@ -1045,7 +1046,7 @@ static void saa7115_decode_vbi_line(struct i2c_client *client, /* decode payloads */ switch (id2) { case 1: - vbi->type = V4L2_SLICED_TELETEXT_PAL_B; + vbi->type = V4L2_SLICED_TELETEXT_B; break; case 4: if (!saa7115_odd_parity(p[0]) || !saa7115_odd_parity(p[1])) @@ -1180,6 +1181,46 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar state->radio = 1; break; + case VIDIOC_INT_G_VIDEO_ROUTING: + { + struct v4l2_routing *route = arg; + + route->input = state->input; + route->output = 0; + break; + } + + case VIDIOC_INT_S_VIDEO_ROUTING: + { + struct v4l2_routing *route = arg; + + v4l_dbg(1, debug, client, "decoder set input %d\n", route->input); + /* saa7113 does not have these inputs */ + if (state->ident == V4L2_IDENT_SAA7113 && + (route->input == SAA7115_COMPOSITE4 || + route->input == SAA7115_COMPOSITE5)) { + return -EINVAL; + } + if (route->input > SAA7115_SVIDEO3) + return -EINVAL; + if (state->input == route->input) + break; + v4l_dbg(1, debug, client, "now setting %s input\n", + (route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite"); + state->input = route->input; + + /* select mode */ + saa7115_write(client, 0x02, + (saa7115_read(client, 0x02) & 0xf0) | + state->input); + + /* bypass chrominance trap for S-Video modes */ + saa7115_write(client, 0x09, + (saa7115_read(client, 0x09) & 0x7f) | + (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0)); + break; + } + case VIDIOC_G_INPUT: *(int *)arg = state->input; break; @@ -1321,7 +1362,7 @@ static int saa7115_attach(struct i2c_adapter *adapter, int address, int kind) saa7115_write(client, 0, 5); chip_id = saa7115_read(client, 0) & 0x0f; - if (chip_id <3 && chip_id > 5) { + if (chip_id < 3 && chip_id > 5) { v4l_dbg(1, debug, client, "saa7115 not found\n"); kfree(client); return 0; @@ -1360,7 +1401,7 @@ static int saa7115_attach(struct i2c_adapter *adapter, int address, int kind) v4l_dbg(1, debug, client, "writing init values\n"); /* init to 60hz/48khz */ - if (state->ident==V4L2_IDENT_SAA7113) + if (state->ident == V4L2_IDENT_SAA7113) saa7115_writeregs(client, saa7113_init_auto_input); else saa7115_writeregs(client, saa7115_init_auto_input); diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c index 992c71774f30..133f9e5252fe 100644 --- a/drivers/media/video/saa7127.c +++ b/drivers/media/video/saa7127.c @@ -54,6 +54,7 @@ #include #include #include +#include static int debug = 0; static int test_image = 0; @@ -222,22 +223,6 @@ static struct i2c_reg_value saa7127_init_config_50hz[] = { { 0, 0 } }; -/* Enumeration for the Supported input types */ -enum saa7127_input_type { - SAA7127_INPUT_TYPE_NORMAL, - SAA7127_INPUT_TYPE_TEST_IMAGE -}; - -/* Enumeration for the Supported Output signal types */ -enum saa7127_output_type { - SAA7127_OUTPUT_TYPE_BOTH, - SAA7127_OUTPUT_TYPE_COMPOSITE, - SAA7127_OUTPUT_TYPE_SVIDEO, - SAA7127_OUTPUT_TYPE_RGB, - SAA7127_OUTPUT_TYPE_YUV_C, - SAA7127_OUTPUT_TYPE_YUV_V -}; - /* ********************************************************************** * @@ -561,7 +546,7 @@ static int saa7127_command(struct i2c_client *client, { struct saa7127_state *state = i2c_get_clientdata(client); struct v4l2_format *fmt = arg; - int *iarg = arg; + struct v4l2_routing *route = arg; switch (cmd) { case VIDIOC_S_STD: @@ -573,15 +558,23 @@ static int saa7127_command(struct i2c_client *client, *(v4l2_std_id *)arg = state->std; break; - case VIDIOC_S_INPUT: - if (state->input_type == *iarg) - break; - return saa7127_set_input_type(client, *iarg); + case VIDIOC_INT_G_VIDEO_ROUTING: + route->input = state->input_type; + route->output = state->output_type; + break; - case VIDIOC_S_OUTPUT: - if (state->output_type == *iarg) - break; - return saa7127_set_output_type(client, *iarg); + case VIDIOC_INT_S_VIDEO_ROUTING: + { + int rc = 0; + + if (state->input_type != route->input) { + rc = saa7127_set_input_type(client, route->input); + } + if (rc == 0 && state->output_type != route->output) { + rc = saa7127_set_output_type(client, route->output); + } + return rc; + } case VIDIOC_STREAMON: case VIDIOC_STREAMOFF: diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig index 86671a43e769..e1c1805df1fb 100644 --- a/drivers/media/video/saa7134/Kconfig +++ b/drivers/media/video/saa7134/Kconfig @@ -39,6 +39,7 @@ config VIDEO_SAA7134_DVB tristate "DVB/ATSC Support for saa7134 based TV cards" depends on VIDEO_SAA7134 && DVB_CORE select VIDEO_BUF_DVB + select FW_LOADER ---help--- This adds support for DVB cards based on the Philips saa7134 chip. diff --git a/drivers/media/video/saa7134/Makefile b/drivers/media/video/saa7134/Makefile index 1ba998424bbd..be7b9ee697d6 100644 --- a/drivers/media/video/saa7134/Makefile +++ b/drivers/media/video/saa7134/Makefile @@ -11,9 +11,9 @@ obj-$(CONFIG_VIDEO_SAA7134_OSS) += saa7134-oss.o obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.o -EXTRA_CFLAGS += -I$(src)/.. -EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/dvb-core -EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/frontends +EXTRA_CFLAGS += -Idrivers/media/video +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core +EXTRA_CFLAGS += -Idrivers/media/dvb/frontends extra-cflags-$(CONFIG_VIDEO_BUF_DVB) += -DHAVE_VIDEO_BUF_DVB=1 extra-cflags-$(CONFIG_DVB_MT352) += -DHAVE_MT352=1 diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index fdd7f48f3b76..e666a4465ca4 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -208,7 +208,7 @@ struct saa7134_board saa7134_boards[] = { [SAA7134_BOARD_FLYTVPLATINUM_FM] = { /* LifeView FlyTV Platinum FM (LR214WF) */ /* "Peter Missel */ - .name = "LifeView FlyTV Platinum FM", + .name = "LifeView FlyTV Platinum FM / Gold", .audio_clock = 0x00200000, .tuner_type = TUNER_PHILIPS_TDA8290, .radio_type = UNSET, @@ -2660,7 +2660,7 @@ struct saa7134_board saa7134_boards[] = { .mpeg = SAA7134_MPEG_DVB, .inputs = {{ .name = name_comp1, - .vmux = 0, + .vmux = 1, .amux = LINE1, },{ .name = name_svideo, @@ -2671,7 +2671,7 @@ struct saa7134_board saa7134_boards[] = { [SAA7134_BOARD_FLYDVBT_LR301] = { /* LifeView FlyDVB-T */ /* Giampiero Giancipoli */ - .name = "LifeView FlyDVB-T", + .name = "LifeView FlyDVB-T / Genius VideoWonder DVB-T", .audio_clock = 0x00200000, .tuner_type = TUNER_ABSENT, .radio_type = UNSET, @@ -2808,6 +2808,40 @@ struct saa7134_board saa7134_boards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, + [SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS] = { + .name = "LifeView FlyDVB-T Hybrid Cardbus", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 0x00600000, /* Bit 21 0=Radio, Bit 22 0=TV */ + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .gpio = 0x200000, /* GPIO21=High for TV input */ + .tv = 1, + },{ + .name = name_svideo, /* S-Video signal on S-Video input */ + .vmux = 8, + .amux = LINE2, + },{ + .name = name_comp1, /* Composite signal on S-Video input */ + .vmux = 0, + .amux = LINE2, + },{ + .name = name_comp2, /* Composite input */ + .vmux = 3, + .amux = LINE2, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x000000, /* GPIO21=Low for FM radio antenna */ + }, + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -3332,6 +3366,30 @@ struct pci_device_id saa7134_pci_tbl[] = { .subvendor = 0x16be, .subdevice = 0x0005, .driver_data = SAA7134_BOARD_MD7134_BRIDGE_2, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1489, + .subdevice = 0x0301, + .driver_data = SAA7134_BOARD_FLYDVBT_LR301, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5168, /* Animation Technologies (LifeView) */ + .subdevice = 0x0304, + .driver_data = SAA7134_BOARD_FLYTVPLATINUM_FM, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5168, + .subdevice = 0x3306, + .driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5168, + .subdevice = 0x3502, /* whats the difference to 0x3306 ?*/ + .driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS, },{ /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, @@ -3462,6 +3520,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_writeb(SAA7134_GPIO_GPSTATUS3, 0x06); break; case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: + case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: saa_writeb(SAA7134_GPIO_GPMODE3, 0x08); saa_writeb(SAA7134_GPIO_GPSTATUS3, 0x00); break; @@ -3633,6 +3692,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) } break; case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: + case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: /* make the tda10046 find its eeprom */ { u8 data[] = { 0x3c, 0x33, 0x62}; diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index c98571c9d5a6..13de05532e0a 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "saa7134-reg.h" #include "saa7134.h" @@ -870,7 +871,7 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, pci_name(pci_dev), dev->pci_rev, pci_dev->irq, dev->pci_lat,pci_resource_start(pci_dev,0)); pci_set_master(pci_dev); - if (!pci_dma_supported(pci_dev,0xffffffff)) { + if (!pci_dma_supported(pci_dev, DMA_32BIT_MASK)) { printk("%s: Oops: no 32bit PCI DMA ???\n",dev->name); err = -EIO; goto fail1; diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 86cfdb8514cb..222a36c38917 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -1064,6 +1064,10 @@ static int dvb_init(struct saa7134_dev *dev) dev->dvb.frontend = tda10046_attach(&tevion_dvbt220rf_config, &dev->i2c_adap); break; + case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: + dev->dvb.frontend = tda10046_attach(&ads_tech_duo_config, + &dev->i2c_adap); + break; #endif #ifdef HAVE_NXT200X case SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180: diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 31ba293854c1..353af3a8b766 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -220,6 +220,7 @@ struct saa7134_format { #define SAA7134_BOARD_AVERMEDIA_A169_B 91 #define SAA7134_BOARD_AVERMEDIA_A169_B1 92 #define SAA7134_BOARD_MD7134_BRIDGE_2 93 +#define SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS 94 #define SAA7134_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 diff --git a/drivers/media/video/sn9c102/Kconfig b/drivers/media/video/sn9c102/Kconfig new file mode 100644 index 000000000000..55f2bc11964b --- /dev/null +++ b/drivers/media/video/sn9c102/Kconfig @@ -0,0 +1,11 @@ +config USB_SN9C102 + tristate "USB SN9C10x PC Camera Controller support" + depends on USB && VIDEO_DEV + ---help--- + Say Y here if you want support for cameras based on SONiX SN9C101, + SN9C102 or SN9C103 PC Camera Controllers. + + See for more info. + + To compile this driver as a module, choose M here: the + module will be called sn9c102. diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index df195c905366..1013b4de89a2 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -401,7 +401,7 @@ static void tuner_status(struct i2c_client *client) } tuner_info("Tuner mode: %s\n", p); tuner_info("Frequency: %lu.%02lu MHz\n", freq, freq_fraction); - tuner_info("Standard: 0x%08llx\n", t->std); + tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std); if (t->mode != V4L2_TUNER_RADIO) return; if (t->has_signal) { @@ -558,10 +558,10 @@ static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, static inline int check_v4l2(struct tuner *t) { - if (t->using_v4l2) { - tuner_dbg ("ignore v4l1 call\n"); - return EINVAL; - } + /* bttv still uses both v4l1 and v4l2 calls to the tuner (v4l2 for + TV, v4l1 for radio), until that is fixed this code is disabled. + Otherwise the radio (v4l1) wouldn't tune after using the TV (v4l2) + first. */ return 0; } @@ -744,6 +744,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) switch_v4l2(); tuner->type = t->mode; + if (t->mode == V4L2_TUNER_ANALOG_TV) + tuner->capability |= V4L2_TUNER_CAP_NORM; if (t->mode != V4L2_TUNER_RADIO) { tuner->rangelow = tv_range[0] * 16; tuner->rangehigh = tv_range[1] * 16; diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index 356bff455ad1..c2b756107548 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -1706,21 +1706,6 @@ static int chip_command(struct i2c_client *client, break; } - case VIDIOC_S_AUDIO: - { - struct v4l2_audio *sarg = arg; - - if (!(desc->flags & CHIP_HAS_INPUTSEL) || sarg->index >= 4) - return -EINVAL; - /* There are four inputs: tuner, radio, extern and intern. */ - chip->input = sarg->index; - if (chip->muted) - break; - chip_write_masked(chip, desc->inputreg, - desc->inputmap[chip->input], desc->inputmask); - break; - } - case VIDIOC_S_TUNER: { struct v4l2_tuner *vt = arg; diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c index e0d2ff83fc91..431c3e2f6c42 100644 --- a/drivers/media/video/tveeprom.c +++ b/drivers/media/video/tveeprom.c @@ -757,9 +757,9 @@ tveeprom_detect_client(struct i2c_adapter *adapter, static int tveeprom_attach_adapter (struct i2c_adapter *adapter) { - if (adapter->id != I2C_HW_B_BT848) - return 0; - return i2c_probe(adapter, &addr_data, tveeprom_detect_client); + if (adapter->class & I2C_CLASS_TV_ANALOG) + return i2c_probe(adapter, &addr_data, tveeprom_detect_client); + return 0; } static int diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index 69d0fe159f4d..dab4973bcf82 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -53,7 +53,7 @@ static struct v4l2_queryctrl tvp5150_qctrl[] = { .minimum = 0, .maximum = 255, .step = 1, - .default_value = 0, + .default_value = 128, .flags = 0, }, { .id = V4L2_CID_CONTRAST, @@ -62,7 +62,7 @@ static struct v4l2_queryctrl tvp5150_qctrl[] = { .minimum = 0, .maximum = 255, .step = 0x1, - .default_value = 0x10, + .default_value = 128, .flags = 0, }, { .id = V4L2_CID_SATURATION, @@ -71,7 +71,7 @@ static struct v4l2_queryctrl tvp5150_qctrl[] = { .minimum = 0, .maximum = 255, .step = 0x1, - .default_value = 0x10, + .default_value = 128, .flags = 0, }, { .id = V4L2_CID_HUE, @@ -80,7 +80,7 @@ static struct v4l2_queryctrl tvp5150_qctrl[] = { .minimum = -128, .maximum = 127, .step = 0x1, - .default_value = 0x10, + .default_value = 0, .flags = 0, } }; @@ -500,16 +500,21 @@ struct i2c_vbi_ram_value { static struct i2c_vbi_ram_value vbi_ram_default[] = { + /* FIXME: Current api doesn't handle all VBI types, those not + yet supported are placed under #if 0 */ +#if 0 {0x010, /* Teletext, SECAM, WST System A */ {V4L2_SLICED_TELETEXT_SECAM,6,23,1}, { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x26, 0xe6, 0xb4, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00 } }, +#endif {0x030, /* Teletext, PAL, WST System B */ - {V4L2_SLICED_TELETEXT_PAL_B,6,22,1}, + {V4L2_SLICED_TELETEXT_B,6,22,1}, { 0xaa, 0xaa, 0xff, 0xff, 0x27, 0x2e, 0x20, 0x2b, 0xa6, 0x72, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00 } }, +#if 0 {0x050, /* Teletext, PAL, WST System C */ {V4L2_SLICED_TELETEXT_PAL_C,6,22,1}, { 0xaa, 0xaa, 0xff, 0xff, 0xe7, 0x2e, 0x20, 0x22, @@ -535,6 +540,7 @@ static struct i2c_vbi_ram_value vbi_ram_default[] = { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02, 0xa6, 0x7b, 0x09, 0x00, 0x00, 0x00, 0x27, 0x00 } }, +#endif {0x0f0, /* Closed Caption, NTSC */ {V4L2_SLICED_CAPTION_525,21,21,1}, { 0xaa, 0x2a, 0xff, 0x3f, 0x04, 0x51, 0x6e, 0x02, @@ -545,6 +551,7 @@ static struct i2c_vbi_ram_value vbi_ram_default[] = { 0x5b, 0x55, 0xc5, 0xff, 0x00, 0x71, 0x6e, 0x42, 0xa6, 0xcd, 0x0f, 0x00, 0x00, 0x00, 0x3a, 0x00 } }, +#if 0 {0x130, /* Wide Screen Signal, NTSC C */ {V4L2_SLICED_WSS_525,20,20,1}, { 0x38, 0x00, 0x3f, 0x00, 0x00, 0x71, 0x6e, 0x43, @@ -560,6 +567,7 @@ static struct i2c_vbi_ram_value vbi_ram_default[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x6d, 0x49, 0x69, 0x94, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x00 } }, +#endif {0x190, /* Video Program System (VPS), PAL */ {V4L2_SLICED_VPS,16,16,0}, { 0xaa, 0xaa, 0xff, 0xff, 0xba, 0xce, 0x2b, 0x0d, @@ -850,7 +858,6 @@ static int tvp5150_command(struct i2c_client *c, case 0: case VIDIOC_INT_RESET: - case DECODER_INIT: tvp5150_reset(c); break; case VIDIOC_S_STD: @@ -949,99 +956,15 @@ static int tvp5150_command(struct i2c_client *c, #endif case VIDIOC_LOG_STATUS: - case DECODER_DUMP: dump_reg(c); break; - case DECODER_GET_CAPABILITIES: + case VIDIOC_G_TUNER: { - struct video_decoder_capability *cap = arg; - - cap->flags = VIDEO_DECODER_PAL | - VIDEO_DECODER_NTSC | - VIDEO_DECODER_SECAM | - VIDEO_DECODER_AUTO | VIDEO_DECODER_CCIR; - cap->inputs = 3; - cap->outputs = 1; - break; - } - case DECODER_GET_STATUS: - { - int *iarg = arg; - int status; - int res=0; - status = tvp5150_read(c, 0x88); - if(status&0x08){ - res |= DECODER_STATUS_COLOR; - } - if(status&0x04 && status&0x02){ - res |= DECODER_STATUS_GOOD; - } - *iarg=res; - break; - } - - case DECODER_SET_GPIO: - break; - - case DECODER_SET_VBI_BYPASS: - break; - - case DECODER_SET_NORM: - { - int *iarg = arg; - - switch (*iarg) { - - case VIDEO_MODE_NTSC: - break; - - case VIDEO_MODE_PAL: - break; - - case VIDEO_MODE_SECAM: - break; - - case VIDEO_MODE_AUTO: - break; - - default: - return -EINVAL; - - } - decoder->norm = *iarg; - break; - } - case DECODER_SET_INPUT: - { - int *iarg = arg; - if (*iarg < 0 || *iarg > 3) { - return -EINVAL; - } - - decoder->input = *iarg; - tvp5150_selmux(c, decoder->input); - - break; - } - case DECODER_SET_OUTPUT: - { - int *iarg = arg; - - /* not much choice of outputs */ - if (*iarg != 0) { - return -EINVAL; - } - break; - } - case DECODER_ENABLE_OUTPUT: - { - int *iarg = arg; - - decoder->enable = (*iarg != 0); - - tvp5150_selmux(c, decoder->input); + struct v4l2_tuner *vt = arg; + int status = tvp5150_read(c, 0x88); + vt->signal = ((status & 0x04) && (status & 0x02)) ? 0xffff : 0x0; break; } case VIDIOC_QUERYCTRL: @@ -1087,35 +1010,6 @@ static int tvp5150_command(struct i2c_client *c, return -EINVAL; } - case DECODER_SET_PICTURE: - { - struct video_picture *pic = arg; - if (decoder->bright != pic->brightness) { - /* We want 0 to 255 we get 0-65535 */ - decoder->bright = pic->brightness; - tvp5150_write(c, TVP5150_BRIGHT_CTL, - decoder->bright >> 8); - } - if (decoder->contrast != pic->contrast) { - /* We want 0 to 255 we get 0-65535 */ - decoder->contrast = pic->contrast; - tvp5150_write(c, TVP5150_CONTRAST_CTL, - decoder->contrast >> 8); - } - if (decoder->sat != pic->colour) { - /* We want 0 to 255 we get 0-65535 */ - decoder->sat = pic->colour; - tvp5150_write(c, TVP5150_SATURATION_CTL, - decoder->contrast >> 8); - } - if (decoder->hue != pic->hue) { - /* We want -128 to 127 we get 0-65535 */ - decoder->hue = pic->hue; - tvp5150_write(c, TVP5150_HUE_CTL, - (decoder->hue - 32768) >> 8); - } - break; - } default: return -EINVAL; } diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c new file mode 100644 index 000000000000..fc52201d607e --- /dev/null +++ b/drivers/media/video/upd64031a.c @@ -0,0 +1,286 @@ +/* + * upd64031A - NEC Electronics Ghost Reduction for NTSC in Japan + * + * 2003 by T.Adachi + * 2003 by Takeru KOMORIYA + * 2006 by Hans Verkuil + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include +#include +#include +#include +#include +#include +#include + +// --------------------- read registers functions define ----------------------- + +/* bit masks */ +#define GR_MODE_MASK 0xc0 +#define DIRECT_3DYCS_CONNECT_MASK 0xc0 +#define SYNC_CIRCUIT_MASK 0xa0 + +// ----------------------------------------------------------------------------- + +MODULE_DESCRIPTION("uPD64031A driver"); +MODULE_AUTHOR("T. Adachi, Takeru KOMORIYA, Hans Verkuil"); +MODULE_LICENSE("GPL"); + +static int debug = 0; +module_param(debug, int, 0644); + +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +static unsigned short normal_i2c[] = { 0x24 >> 1, 0x26 >> 1, I2C_CLIENT_END }; + + +I2C_CLIENT_INSMOD; + +enum { + R00 = 0, R01, R02, R03, R04, + R05, R06, R07, R08, R09, + R0A, R0B, R0C, R0D, R0E, R0F, + /* unused registers + R10, R11, R12, R13, R14, + R15, R16, R17, + */ + TOT_REGS +}; + +struct upd64031a_state { + u8 regs[TOT_REGS]; + u8 gr_mode; + u8 direct_3dycs_connect; + u8 ext_comp_sync; + u8 ext_vert_sync; +}; + +static u8 upd64031a_init[] = { + 0x00, 0xb8, 0x48, 0xd2, 0xe6, + 0x03, 0x10, 0x0b, 0xaf, 0x7f, + 0x00, 0x00, 0x1d, 0x5e, 0x00, + 0xd0 +}; + +/* ------------------------------------------------------------------------ */ + +static u8 upd64031a_read(struct i2c_client *client, u8 reg) +{ + u8 buf[2]; + + if (reg >= sizeof(buf)) + return 0xff; + i2c_master_recv(client, buf, 2); + return buf[reg]; +} + +/* ------------------------------------------------------------------------ */ + +static void upd64031a_write(struct i2c_client *client, u8 reg, u8 val) +{ + u8 buf[2]; + + buf[0] = reg; + buf[1] = val; + v4l_dbg(1, debug, client, "writing reg addr: %02X val: %02X\n", reg, val); + if (i2c_master_send(client, buf, 2) != 2) + v4l_err(client, "I/O error write 0x%02x/0x%02x\n", reg, val); +} + +/* ------------------------------------------------------------------------ */ + +/* The input changed due to new input or channel changed */ +static void upd64031a_change(struct i2c_client *client) +{ + struct upd64031a_state *state = i2c_get_clientdata(client); + u8 reg = state->regs[R00]; + + v4l_dbg(1, debug, client, "changed input or channel\n"); + upd64031a_write(client, R00, reg | 0x10); + upd64031a_write(client, R00, reg & ~0x10); +} + +/* ------------------------------------------------------------------------ */ + +static int upd64031a_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + struct upd64031a_state *state = i2c_get_clientdata(client); + struct v4l2_routing *route = arg; + + switch (cmd) { + case VIDIOC_S_FREQUENCY: + upd64031a_change(client); + break; + + case VIDIOC_INT_G_VIDEO_ROUTING: + route->input = (state->gr_mode >> 6) | + (state->direct_3dycs_connect >> 4) | + (state->ext_comp_sync >> 1) | + (state->ext_vert_sync >> 2); + route->output = 0; + break; + + case VIDIOC_INT_S_VIDEO_ROUTING: + { + u8 r00, r05, r08; + + state->gr_mode = (route->input & 3) << 6; + state->direct_3dycs_connect = (route->input & 0xc) << 4; + state->ext_comp_sync = (route->input & UPD64031A_COMPOSITE_EXTERNAL) << 1; + state->ext_vert_sync = (route->input & UPD64031A_VERTICAL_EXTERNAL) << 2; + r00 = (state->regs[R00] & ~GR_MODE_MASK) | state->gr_mode; + r05 = (state->regs[R00] & ~SYNC_CIRCUIT_MASK) | + state->ext_comp_sync | state->ext_vert_sync; + r08 = (state->regs[R08] & ~DIRECT_3DYCS_CONNECT_MASK) | + state->direct_3dycs_connect; + upd64031a_write(client, R00, r00); + upd64031a_write(client, R05, r05); + upd64031a_write(client, R08, r08); + upd64031a_change(client); + break; + } + + case VIDIOC_LOG_STATUS: + v4l_info(client, "Status: SA00=0x%02x SA01=0x%02x\n", + upd64031a_read(client, 0), upd64031a_read(client, 1)); + break; + +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_INT_G_REGISTER: + { + struct v4l2_register *reg = arg; + + if (reg->i2c_id != I2C_DRIVERID_UPD64031A) + return -EINVAL; + reg->val = upd64031a_read(client, reg->reg & 0xff); + break; + } + + case VIDIOC_INT_S_REGISTER: + { + struct v4l2_register *reg = arg; + u8 addr = reg->reg & 0xff; + u8 val = reg->val & 0xff; + + if (reg->i2c_id != I2C_DRIVERID_UPD64031A) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + upd64031a_write(client, addr, val); + break; + } +#endif + + default: + break; + } + return 0; +} + +/* ------------------------------------------------------------------------ */ + +/* i2c implementation */ + +static struct i2c_driver i2c_driver; + +static int upd64031a_attach(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *client; + struct upd64031a_state *state; + int i; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return 0; + + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == NULL) { + return -ENOMEM; + } + + client->addr = address; + client->adapter = adapter; + client->driver = &i2c_driver; + snprintf(client->name, sizeof(client->name) - 1, "uPD64031A"); + + v4l_info(client, "chip found @ 0x%x (%s)\n", address << 1, adapter->name); + + state = kmalloc(sizeof(struct upd64031a_state), GFP_KERNEL); + if (state == NULL) { + kfree(client); + return -ENOMEM; + } + i2c_set_clientdata(client, state); + memcpy(state->regs, upd64031a_init, sizeof(state->regs)); + state->gr_mode = UPD64031A_GR_ON << 6; + state->direct_3dycs_connect = UPD64031A_3DYCS_COMPOSITE << 4; + state->ext_comp_sync = state->ext_vert_sync = 0; + for (i = 0; i < TOT_REGS; i++) { + upd64031a_write(client, i, state->regs[i]); + } + + i2c_attach_client(client); + + return 0; +} + +static int upd64031a_probe(struct i2c_adapter *adapter) +{ + if (adapter->class & I2C_CLASS_TV_ANALOG) + return i2c_probe(adapter, &addr_data, upd64031a_attach); + return 0; +} + +static int upd64031a_detach(struct i2c_client *client) +{ + int err; + + err = i2c_detach_client(client); + if (err) + return err; + + kfree(client); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* i2c implementation */ +static struct i2c_driver i2c_driver = { + .driver = { + .name = "upd64031a", + }, + .id = I2C_DRIVERID_UPD64031A, + .attach_adapter = upd64031a_probe, + .detach_client = upd64031a_detach, + .command = upd64031a_command, +}; + + +static int __init upd64031a_init_module(void) +{ + return i2c_add_driver(&i2c_driver); +} + +static void __exit upd64031a_exit_module(void) +{ + i2c_del_driver(&i2c_driver); +} + +module_init(upd64031a_init_module); +module_exit(upd64031a_exit_module); diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c new file mode 100644 index 000000000000..c3a7ffe5c267 --- /dev/null +++ b/drivers/media/video/upd64083.c @@ -0,0 +1,262 @@ +/* + * upd6408x - NEC Electronics 3-Dimensional Y/C separation driver + * + * 2003 by T.Adachi (tadachi@tadachi-net.com) + * 2003 by Takeru KOMORIYA + * 2006 by Hans Verkuil + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("uPD64083 driver"); +MODULE_AUTHOR("T. Adachi, Takeru KOMORIYA, Hans Verkuil"); +MODULE_LICENSE("GPL"); + +static int debug = 0; +module_param(debug, bool, 0644); + +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +static unsigned short normal_i2c[] = { 0xb8 >> 1, 0xba >> 1, I2C_CLIENT_END }; + + +I2C_CLIENT_INSMOD; + +enum { + R00 = 0, R01, R02, R03, R04, + R05, R06, R07, R08, R09, + R0A, R0B, R0C, R0D, R0E, R0F, + R10, R11, R12, R13, R14, + R15, R16, + TOT_REGS +}; + +struct upd64083_state { + u8 mode; + u8 ext_y_adc; + u8 regs[TOT_REGS]; +}; + +/* Initial values when used in combination with the + NEC upd64031a ghost reduction chip. */ +static u8 upd64083_init[] = { + 0x1f, 0x01, 0xa0, 0x2d, 0x29, /* we use EXCSS=0 */ + 0x36, 0xdd, 0x05, 0x56, 0x48, + 0x00, 0x3a, 0xa0, 0x05, 0x08, + 0x44, 0x60, 0x08, 0x52, 0xf8, + 0x53, 0x60, 0x10 +}; + +/* ------------------------------------------------------------------------ */ + +static void upd64083_log_status(struct i2c_client *client) +{ + u8 buf[7]; + + i2c_master_recv(client, buf, 7); + v4l_info(client, "Status: SA00=%02x SA01=%02x SA02=%02x SA03=%02x " + "SA04=%02x SA05=%02x SA06=%02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]); +} + +/* ------------------------------------------------------------------------ */ + +static void upd64083_write(struct i2c_client *client, u8 reg, u8 val) +{ + u8 buf[2]; + + buf[0] = reg; + buf[1] = val; + v4l_dbg(1, debug, client, "writing reg addr: %02x val: %02x\n", reg, val); + if (i2c_master_send(client, buf, 2) != 2) + v4l_err(client, "I/O error write 0x%02x/0x%02x\n", reg, val); +} + +/* ------------------------------------------------------------------------ */ + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static u8 upd64083_read(struct i2c_client *client, u8 reg) +{ + u8 buf[7]; + + if (reg >= sizeof(buf)) + return 0xff; + i2c_master_recv(client, buf, sizeof(buf)); + return buf[reg]; +} +#endif + +/* ------------------------------------------------------------------------ */ + +static int upd64083_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + struct upd64083_state *state = i2c_get_clientdata(client); + struct v4l2_routing *route = arg; + + switch (cmd) { + case VIDIOC_INT_G_VIDEO_ROUTING: + route->input = (state->mode >> 6) | (state->ext_y_adc >> 3); + route->output = 0; + break; + + case VIDIOC_INT_S_VIDEO_ROUTING: + { + u8 r00, r02; + + if (route->input > 7 || (route->input & 6) == 6) + return -EINVAL; + state->mode = (route->input & 3) << 6; + state->ext_y_adc = (route->input & UPD64083_EXT_Y_ADC) << 3; + r00 = (state->regs[R00] & ~(3 << 6)) | state->mode; + r02 = (state->regs[R02] & ~(1 << 5)) | state->ext_y_adc; + upd64083_write(client, R00, r00); + upd64083_write(client, R02, r02); + break; + } + + case VIDIOC_LOG_STATUS: + upd64083_log_status(client); + break; + +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_INT_G_REGISTER: + { + struct v4l2_register *reg = arg; + + if (reg->i2c_id != I2C_DRIVERID_UPD64083) + return -EINVAL; + reg->val = upd64083_read(client, reg->reg & 0xff); + break; + } + + case VIDIOC_INT_S_REGISTER: + { + struct v4l2_register *reg = arg; + u8 addr = reg->reg & 0xff; + u8 val = reg->val & 0xff; + + if (reg->i2c_id != I2C_DRIVERID_UPD64083) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + upd64083_write(client, addr, val); + break; + } +#endif + default: + break; + } + + return 0; +} + +/* ------------------------------------------------------------------------ */ + +/* i2c implementation */ + +static struct i2c_driver i2c_driver; + +static int upd64083_attach(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *client; + struct upd64083_state *state; + int i; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return 0; + + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == NULL) { + return -ENOMEM; + } + + client->addr = address; + client->adapter = adapter; + client->driver = &i2c_driver; + snprintf(client->name, sizeof(client->name) - 1, "uPD64083"); + + v4l_info(client, "chip found @ 0x%x (%s)\n", address << 1, adapter->name); + + state = kmalloc(sizeof(struct upd64083_state), GFP_KERNEL); + if (state == NULL) { + kfree(client); + return -ENOMEM; + } + i2c_set_clientdata(client, state); + /* Initially assume that a ghost reduction chip is present */ + state->mode = 0; /* YCS mode */ + state->ext_y_adc = (1 << 5); + memcpy(state->regs, upd64083_init, TOT_REGS); + for (i = 0; i < TOT_REGS; i++) { + upd64083_write(client, i, state->regs[i]); + } + i2c_attach_client(client); + + return 0; +} + +static int upd64083_probe(struct i2c_adapter *adapter) +{ + if (adapter->class & I2C_CLASS_TV_ANALOG) + return i2c_probe(adapter, &addr_data, upd64083_attach); + return 0; +} + +static int upd64083_detach(struct i2c_client *client) +{ + int err; + + err = i2c_detach_client(client); + if (err) + return err; + + kfree(client); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* i2c implementation */ +static struct i2c_driver i2c_driver = { + .driver = { + .name = "upd64083", + }, + .id = I2C_DRIVERID_UPD64083, + .attach_adapter = upd64083_probe, + .detach_client = upd64083_detach, + .command = upd64083_command, +}; + + +static int __init upd64083_init_module(void) +{ + return i2c_add_driver(&i2c_driver); +} + +static void __exit upd64083_exit_module(void) +{ + i2c_del_driver(&i2c_driver); +} + +module_init(upd64083_init_module); +module_exit(upd64083_exit_module); diff --git a/drivers/media/video/usbvideo/Kconfig b/drivers/media/video/usbvideo/Kconfig new file mode 100644 index 000000000000..08a5d20bb2c0 --- /dev/null +++ b/drivers/media/video/usbvideo/Kconfig @@ -0,0 +1,38 @@ +config VIDEO_USBVIDEO + tristate + +config USB_VICAM + tristate "USB 3com HomeConnect (aka vicam) support (EXPERIMENTAL)" + depends on USB && VIDEO_DEV && EXPERIMENTAL + select VIDEO_USBVIDEO + ---help--- + Say Y here if you have 3com homeconnect camera (vicam). + + To compile this driver as a module, choose M here: the + module will be called vicam. + +config USB_IBMCAM + tristate "USB IBM (Xirlink) C-it Camera support" + depends on USB && VIDEO_DEV + select VIDEO_USBVIDEO + ---help--- + Say Y here if you want to connect a IBM "C-It" camera, also known as + "Xirlink PC Camera" to your computer's USB port. + + To compile this driver as a module, choose M here: the + module will be called ibmcam. + + This camera has several configuration options which + can be specified when you load the module. Read + to learn more. + +config USB_KONICAWC + tristate "USB Konica Webcam support" + depends on USB && VIDEO_DEV + select VIDEO_USBVIDEO + ---help--- + Say Y here if you want support for webcams based on a Konica + chipset. This is known to work with the Intel YC76 webcam. + + To compile this driver as a module, choose M here: the + module will be called konicawc. diff --git a/drivers/media/video/usbvideo/Makefile b/drivers/media/video/usbvideo/Makefile index ed410a5ee8c9..bb52eb8dc2f9 100644 --- a/drivers/media/video/usbvideo/Makefile +++ b/drivers/media/video/usbvideo/Makefile @@ -1,4 +1,4 @@ -obj-$(CONFIG_USB_IBMCAM) += ibmcam.o usbvideo.o ultracam.o -obj-$(CONFIG_USB_KONICAWC) += konicawc.o usbvideo.o -obj-$(CONFIG_USB_VICAM) += vicam.o usbvideo.o - +obj-$(CONFIG_VIDEO_USBVIDEO) += usbvideo.o +obj-$(CONFIG_USB_IBMCAM) += ibmcam.o ultracam.o +obj-$(CONFIG_USB_KONICAWC) += konicawc.o +obj-$(CONFIG_USB_VICAM) += vicam.o diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 11a97f30b876..d330fa985bcc 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -317,6 +317,7 @@ static const char *v4l2_int_ioctls[] = { [_IOC_NR(TUNER_SET_STANDBY)] = "TUNER_SET_STANDBY", [_IOC_NR(TDA9887_SET_CONFIG)] = "TDA9887_SET_CONFIG", + [_IOC_NR(VIDIOC_INT_S_TUNER_MODE)] = "VIDIOC_INT_S_TUNER_MODE", [_IOC_NR(VIDIOC_INT_S_REGISTER)] = "VIDIOC_INT_S_REGISTER", [_IOC_NR(VIDIOC_INT_G_REGISTER)] = "VIDIOC_INT_G_REGISTER", [_IOC_NR(VIDIOC_INT_RESET)] = "VIDIOC_INT_RESET", @@ -325,7 +326,12 @@ static const char *v4l2_int_ioctls[] = { [_IOC_NR(VIDIOC_INT_S_VBI_DATA)] = "VIDIOC_INT_S_VBI_DATA", [_IOC_NR(VIDIOC_INT_G_VBI_DATA)] = "VIDIOC_INT_G_VBI_DATA", [_IOC_NR(VIDIOC_INT_G_CHIP_IDENT)] = "VIDIOC_INT_G_CHIP_IDENT", - [_IOC_NR(VIDIOC_INT_I2S_CLOCK_FREQ)] = "VIDIOC_INT_I2S_CLOCK_FREQ" + [_IOC_NR(VIDIOC_INT_I2S_CLOCK_FREQ)] = "VIDIOC_INT_I2S_CLOCK_FREQ", + [_IOC_NR(VIDIOC_INT_S_STANDBY)] = "VIDIOC_INT_S_STANDBY", + [_IOC_NR(VIDIOC_INT_S_AUDIO_ROUTING)] = "VIDIOC_INT_S_AUDIO_ROUTING", + [_IOC_NR(VIDIOC_INT_G_AUDIO_ROUTING)] = "VIDIOC_INT_G_AUDIO_ROUTING", + [_IOC_NR(VIDIOC_INT_S_VIDEO_ROUTING)] = "VIDIOC_INT_S_VIDEO_ROUTING", + [_IOC_NR(VIDIOC_INT_G_VIDEO_ROUTING)] = "VIDIOC_INT_G_VIDEO_ROUTING" }; #define V4L2_INT_IOCTLS ARRAY_SIZE(v4l2_int_ioctls) diff --git a/drivers/media/video/video-buf.c b/drivers/media/video/video-buf.c index d2ca0f08d0df..acc5ea936687 100644 --- a/drivers/media/video/video-buf.c +++ b/drivers/media/video/video-buf.c @@ -399,19 +399,25 @@ void videobuf_queue_pci(struct videobuf_queue* q) int videobuf_pci_dma_map(struct pci_dev *pci,struct videobuf_dmabuf *dma) { struct videobuf_queue q; + struct videobuf_queue_ops qops; q.dev=pci; - q.ops->vb_map_sg=(vb_map_sg_t *)pci_unmap_sg; + qops.vb_map_sg=(vb_map_sg_t *)pci_map_sg; + qops.vb_unmap_sg=(vb_map_sg_t *)pci_unmap_sg; + q.ops = &qops; - return (videobuf_dma_unmap(&q,dma)); + return (videobuf_dma_map(&q,dma)); } int videobuf_pci_dma_unmap(struct pci_dev *pci,struct videobuf_dmabuf *dma) { struct videobuf_queue q; + struct videobuf_queue_ops qops; q.dev=pci; - q.ops->vb_map_sg=(vb_map_sg_t *)pci_unmap_sg; + qops.vb_map_sg=(vb_map_sg_t *)pci_map_sg; + qops.vb_unmap_sg=(vb_map_sg_t *)pci_unmap_sg; + q.ops = &qops; return (videobuf_dma_unmap(&q,dma)); } @@ -923,7 +929,7 @@ ssize_t videobuf_read_one(struct videobuf_queue *q, /* need to capture a new frame */ retval = -ENOMEM; q->read_buf = videobuf_alloc(q->msize); - dprintk(1,"video alloc=0x%08x\n",(unsigned int) q->read_buf); + dprintk(1,"video alloc=0x%p\n", q->read_buf); if (NULL == q->read_buf) goto done; q->read_buf->memory = V4L2_MEMORY_USERPTR; diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c new file mode 100644 index 000000000000..a9b59c35cd67 --- /dev/null +++ b/drivers/media/video/wm8739.c @@ -0,0 +1,355 @@ +/* + * wm8739 + * + * Copyright (C) 2005 T. Adachi + * + * Copyright (C) 2005 Hans Verkuil + * - Cleanup + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("wm8739 driver"); +MODULE_AUTHOR("T. Adachi, Hans Verkuil"); +MODULE_LICENSE("GPL"); + +static int debug = 0; +static unsigned short normal_i2c[] = { 0x34 >> 1, 0x36 >> 1, I2C_CLIENT_END }; + +module_param(debug, int, 0644); + +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + + +I2C_CLIENT_INSMOD; + +/* ------------------------------------------------------------------------ */ + +enum { + R0 = 0, R1, + R5 = 5, R6, R7, R8, R9, R15 = 15, + TOT_REGS +}; + +struct wm8739_state { + u32 clock_freq; + u8 muted; + u16 volume; + u16 balance; + u8 vol_l; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ + u8 vol_r; /* +12dB to -34.5dB 1.5dB step (5bit) def:0dB */ +}; + +/* ------------------------------------------------------------------------ */ + +static int wm8739_write(struct i2c_client *client, int reg, u16 val) +{ + int i; + + if (reg < 0 || reg >= TOT_REGS) { + v4l_err(client, "Invalid register R%d\n", reg); + return -1; + } + + v4l_dbg(1, debug, client, "write: %02x %02x\n", reg, val); + + for (i = 0; i < 3; i++) { + if (i2c_smbus_write_byte_data(client, (reg << 1) | + (val >> 8), val & 0xff) == 0) { + return 0; + } + } + v4l_err(client, "I2C: cannot write %03x to register R%d\n", val, reg); + return -1; +} + +/* write regs to set audio volume etc */ +static void wm8739_set_audio(struct i2c_client *client) +{ + struct wm8739_state *state = i2c_get_clientdata(client); + u16 mute = state->muted ? 0x80 : 0; + + /* Volume setting: bits 0-4, 0x1f = 12 dB, 0x00 = -34.5 dB + * Default setting: 0x17 = 0 dB + */ + wm8739_write(client, R0, (state->vol_l & 0x1f) | mute); + wm8739_write(client, R1, (state->vol_r & 0x1f) | mute); +} + +static int wm8739_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct wm8739_state *state = i2c_get_clientdata(client); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = state->muted; + break; + + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = state->volume; + break; + + case V4L2_CID_AUDIO_BALANCE: + ctrl->value = state->balance; + break; + + default: + return -EINVAL; + } + return 0; +} + +static int wm8739_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +{ + struct wm8739_state *state = i2c_get_clientdata(client); + unsigned int work_l, work_r; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + state->muted = ctrl->value; + break; + + case V4L2_CID_AUDIO_VOLUME: + state->volume = ctrl->value; + break; + + case V4L2_CID_AUDIO_BALANCE: + state->balance = ctrl->value; + break; + + default: + return -EINVAL; + } + + /* normalize ( 65535 to 0 -> 31 to 0 (12dB to -34.5dB) ) */ + work_l = (min(65536 - state->balance, 32768) * state->volume) / 32768; + work_r = (min(state->balance, (u16)32768) * state->volume) / 32768; + + state->vol_l = (long)work_l * 31 / 65535; + state->vol_r = (long)work_r * 31 / 65535; + + /* set audio volume etc. */ + wm8739_set_audio(client); + return 0; +} + +/* ------------------------------------------------------------------------ */ + +static struct v4l2_queryctrl wm8739_qctrl[] = { + { + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 58880, + .flags = 0, + .type = V4L2_CTRL_TYPE_INTEGER, + },{ + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + .flags = 0, + .type = V4L2_CTRL_TYPE_BOOLEAN, + },{ + .id = V4L2_CID_AUDIO_BALANCE, + .name = "Balance", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 32768, + .flags = 0, + .type = V4L2_CTRL_TYPE_INTEGER, + } +}; + +/* ------------------------------------------------------------------------ */ + +static int wm8739_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + struct wm8739_state *state = i2c_get_clientdata(client); + + switch (cmd) { + case VIDIOC_INT_AUDIO_CLOCK_FREQ: + { + u32 audiofreq = *(u32 *)arg; + + state->clock_freq = audiofreq; + wm8739_write(client, R9, 0x000); /* de-activate */ + switch (audiofreq) { + case 44100: + wm8739_write(client, R8, 0x020); /* 256fps, fs=44.1k */ + break; + case 48000: + wm8739_write(client, R8, 0x000); /* 256fps, fs=48k */ + break; + case 32000: + wm8739_write(client, R8, 0x018); /* 256fps, fs=32k */ + break; + default: + break; + } + wm8739_write(client, R9, 0x001); /* activate */ + break; + } + + case VIDIOC_G_CTRL: + return wm8739_get_ctrl(client, arg); + + case VIDIOC_S_CTRL: + return wm8739_set_ctrl(client, arg); + + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *qc = arg; + int i; + + for (i = 0; i < ARRAY_SIZE(wm8739_qctrl); i++) + if (qc->id && qc->id == wm8739_qctrl[i].id) { + memcpy(qc, &wm8739_qctrl[i], sizeof(*qc)); + return 0; + } + return -EINVAL; + } + + case VIDIOC_LOG_STATUS: + v4l_info(client, "Frequency: %u Hz\n", state->clock_freq); + v4l_info(client, "Volume L: %02x%s\n", state->vol_l & 0x1f, + state->muted ? " (muted)" : ""); + v4l_info(client, "Volume R: %02x%s\n", state->vol_r & 0x1f, + state->muted ? " (muted)" : ""); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* ------------------------------------------------------------------------ */ + +/* i2c implementation */ + +static struct i2c_driver i2c_driver; + +static int wm8739_attach(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *client; + struct wm8739_state *state; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return 0; + + client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == NULL) + return -ENOMEM; + + client->addr = address; + client->adapter = adapter; + client->driver = &i2c_driver; + snprintf(client->name, sizeof(client->name) - 1, "wm8739"); + + v4l_info(client, "chip found @ 0x%x (%s)\n", address << 1, adapter->name); + + state = kmalloc(sizeof(struct wm8739_state), GFP_KERNEL); + if (state == NULL) { + kfree(client); + return -ENOMEM; + } + state->vol_l = 0x17; /* 0dB */ + state->vol_r = 0x17; /* 0dB */ + state->muted = 0; + state->balance = 32768; + /* normalize (12dB(31) to -34.5dB(0) [0dB(23)] -> 65535 to 0) */ + state->volume = ((long)state->vol_l + 1) * 65535 / 31; + state->clock_freq = 48000; + i2c_set_clientdata(client, state); + + /* initialize wm8739 */ + wm8739_write(client, R15, 0x00); /* reset */ + wm8739_write(client, R5, 0x000); /* filter setting, high path, offet clear */ + wm8739_write(client, R6, 0x000); /* ADC, OSC, Power Off mode Disable */ + wm8739_write(client, R7, 0x049); /* Digital Audio interface format */ + /* Enable Master mode */ + /* 24 bit, MSB first/left justified */ + wm8739_write(client, R8, 0x000); /* sampling control */ + /* normal, 256fs, 48KHz sampling rate */ + wm8739_write(client, R9, 0x001); /* activate */ + wm8739_set_audio(client); /* set volume/mute */ + + i2c_attach_client(client); + + return 0; +} + +static int wm8739_probe(struct i2c_adapter *adapter) +{ + if (adapter->class & I2C_CLASS_TV_ANALOG) + return i2c_probe(adapter, &addr_data, wm8739_attach); + return 0; +} + +static int wm8739_detach(struct i2c_client *client) +{ + int err; + + err = i2c_detach_client(client); + if (err) + return err; + + kfree(client); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* i2c implementation */ +static struct i2c_driver i2c_driver = { + .driver = { + .name = "wm8739", + }, + .id = I2C_DRIVERID_WM8739, + .attach_adapter = wm8739_probe, + .detach_client = wm8739_detach, + .command = wm8739_command, +}; + + +static int __init wm8739_init_module(void) +{ + return i2c_add_driver(&i2c_driver); +} + +static void __exit wm8739_cleanup_module(void) +{ + i2c_del_driver(&i2c_driver); +} + +module_init(wm8739_init_module); +module_exit(wm8739_cleanup_module); diff --git a/drivers/media/video/zc0301/Kconfig b/drivers/media/video/zc0301/Kconfig new file mode 100644 index 000000000000..c3bf886b80cd --- /dev/null +++ b/drivers/media/video/zc0301/Kconfig @@ -0,0 +1,11 @@ +config USB_ZC0301 + tristate "USB ZC0301 Image Processor and Control Chip support" + depends on USB && VIDEO_DEV + ---help--- + Say Y here if you want support for cameras based on the ZC0301 + Image Processor and Control Chip. + + See for more info. + + To compile this driver as a module, choose M here: the + module will be called zc0301. diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 3f5d77f633fa..003b077c2324 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -60,6 +60,17 @@ config MMC_SDHCI If unsure, say N. +config MMC_OMAP + tristate "TI OMAP Multimedia Card Interface support" + depends on ARCH_OMAP && MMC + select TPS65010 if MACH_OMAP_H2 + help + This selects the TI OMAP Multimedia card Interface. + If you have an OMAP board with a Multimedia Card slot, + say Y or M here. + + If unsure, say N. + config MMC_WBSD tristate "Winbond W83L51xD SD/MMC Card Interface support" depends on MMC && ISA_DMA_API @@ -80,4 +91,22 @@ config MMC_AU1X If unsure, say N. +config MMC_AT91RM9200 + tristate "AT91RM9200 SD/MMC Card Interface support" + depends on ARCH_AT91RM9200 && MMC + help + This selects the AT91RM9200 MCI controller. + + If unsure, say N. + +config MMC_IMX + tristate "Motorola i.MX Multimedia Card Interface support" + depends on ARCH_IMX && MMC + help + This selects the Motorola i.MX Multimedia card Interface. + If you have a i.MX platform with a Multimedia Card slot, + say Y or M here. + + If unsure, say N. + endmenu diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 769d545284a4..d2957e35cc6f 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -17,8 +17,15 @@ obj-$(CONFIG_MMC_BLOCK) += mmc_block.o # obj-$(CONFIG_MMC_ARMMMCI) += mmci.o obj-$(CONFIG_MMC_PXA) += pxamci.o +obj-$(CONFIG_MMC_IMX) += imxmmc.o obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_WBSD) += wbsd.o obj-$(CONFIG_MMC_AU1X) += au1xmmc.o +obj-$(CONFIG_MMC_OMAP) += omap.o +obj-$(CONFIG_MMC_AT91RM9200) += at91_mci.o mmc_core-y := mmc.o mmc_queue.o mmc_sysfs.o + +ifeq ($(CONFIG_MMC_DEBUG),y) +EXTRA_CFLAGS += -DDEBUG +endif diff --git a/drivers/mmc/at91_mci.c b/drivers/mmc/at91_mci.c new file mode 100644 index 000000000000..6061c2d101a0 --- /dev/null +++ b/drivers/mmc/at91_mci.c @@ -0,0 +1,988 @@ +/* + * linux/drivers/mmc/at91_mci.c - ATMEL AT91RM9200 MCI Driver + * + * Copyright (C) 2005 Cougar Creek Computing Devices Ltd, All Rights Reserved + * + * Copyright (C) 2006 Malcolm Noyes + * + * 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 is the AT91RM9200 MCI driver that has been tested with both MMC cards + and SD-cards. Boards that support write protect are now supported. + The CCAT91SBC001 board does not support SD cards. + + The three entry points are at91_mci_request, at91_mci_set_ios + and at91_mci_get_ro. + + SET IOS + This configures the device to put it into the correct mode and clock speed + required. + + MCI REQUEST + MCI request processes the commands sent in the mmc_request structure. This + can consist of a processing command and a stop command in the case of + multiple block transfers. + + There are three main types of request, commands, reads and writes. + + Commands are straight forward. The command is submitted to the controller and + the request function returns. When the controller generates an interrupt to indicate + the command is finished, the response to the command are read and the mmc_request_done + function called to end the request. + + Reads and writes work in a similar manner to normal commands but involve the PDC (DMA) + controller to manage the transfers. + + A read is done from the controller directly to the scatterlist passed in from the request. + Due to a bug in the controller, when a read is completed, all the words are byte + swapped in the scatterlist buffers. + + The sequence of read interrupts is: ENDRX, RXBUFF, CMDRDY + + A write is slightly different in that the bytes to write are read from the scatterlist + into a dma memory buffer (this is in case the source buffer should be read only). The + entire write buffer is then done from this single dma memory buffer. + + The sequence of write interrupts is: ENDTX, TXBUFE, NOTBUSY, CMDRDY + + GET RO + Gets the status of the write protect pin, if available. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "at91_mci" + +#undef SUPPORT_4WIRE + +#ifdef CONFIG_MMC_DEBUG +#define DBG(fmt...) \ + printk(fmt) +#else +#define DBG(fmt...) do { } while (0) +#endif + +static struct clk *mci_clk; + +#define FL_SENT_COMMAND (1 << 0) +#define FL_SENT_STOP (1 << 1) + + + +/* + * Read from a MCI register. + */ +static inline unsigned long at91_mci_read(unsigned int reg) +{ + void __iomem *mci_base = (void __iomem *)AT91_VA_BASE_MCI; + + return __raw_readl(mci_base + reg); +} + +/* + * Write to a MCI register. + */ +static inline void at91_mci_write(unsigned int reg, unsigned long value) +{ + void __iomem *mci_base = (void __iomem *)AT91_VA_BASE_MCI; + + __raw_writel(value, mci_base + reg); +} + +/* + * Low level type for this driver + */ +struct at91mci_host +{ + struct mmc_host *mmc; + struct mmc_command *cmd; + struct mmc_request *request; + + struct at91_mmc_data *board; + int present; + + /* + * Flag indicating when the command has been sent. This is used to + * work out whether or not to send the stop + */ + unsigned int flags; + /* flag for current bus settings */ + u32 bus_mode; + + /* DMA buffer used for transmitting */ + unsigned int* buffer; + dma_addr_t physical_address; + unsigned int total_length; + + /* Latest in the scatterlist that has been enabled for transfer, but not freed */ + int in_use_index; + + /* Latest in the scatterlist that has been enabled for transfer */ + int transfer_index; +}; + +/* + * Copy from sg to a dma block - used for transfers + */ +static inline void at91mci_sg_to_dma(struct at91mci_host *host, struct mmc_data *data) +{ + unsigned int len, i, size; + unsigned *dmabuf = host->buffer; + + size = host->total_length; + len = data->sg_len; + + /* + * Just loop through all entries. Size might not + * be the entire list though so make sure that + * we do not transfer too much. + */ + for (i = 0; i < len; i++) { + struct scatterlist *sg; + int amount; + int index; + unsigned int *sgbuffer; + + sg = &data->sg[i]; + + sgbuffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset; + amount = min(size, sg->length); + size -= amount; + amount /= 4; + + for (index = 0; index < amount; index++) + *dmabuf++ = swab32(sgbuffer[index]); + + kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ); + + if (size == 0) + break; + } + + /* + * Check that we didn't get a request to transfer + * more data than can fit into the SG list. + */ + BUG_ON(size != 0); +} + +/* + * Prepare a dma read + */ +static void at91mci_pre_dma_read(struct at91mci_host *host) +{ + int i; + struct scatterlist *sg; + struct mmc_command *cmd; + struct mmc_data *data; + + DBG("pre dma read\n"); + + cmd = host->cmd; + if (!cmd) { + DBG("no command\n"); + return; + } + + data = cmd->data; + if (!data) { + DBG("no data\n"); + return; + } + + for (i = 0; i < 2; i++) { + /* nothing left to transfer */ + if (host->transfer_index >= data->sg_len) { + DBG("Nothing left to transfer (index = %d)\n", host->transfer_index); + break; + } + + /* Check to see if this needs filling */ + if (i == 0) { + if (at91_mci_read(AT91_PDC_RCR) != 0) { + DBG("Transfer active in current\n"); + continue; + } + } + else { + if (at91_mci_read(AT91_PDC_RNCR) != 0) { + DBG("Transfer active in next\n"); + continue; + } + } + + /* Setup the next transfer */ + DBG("Using transfer index %d\n", host->transfer_index); + + sg = &data->sg[host->transfer_index++]; + DBG("sg = %p\n", sg); + + sg->dma_address = dma_map_page(NULL, sg->page, sg->offset, sg->length, DMA_FROM_DEVICE); + + DBG("dma address = %08X, length = %d\n", sg->dma_address, sg->length); + + if (i == 0) { + at91_mci_write(AT91_PDC_RPR, sg->dma_address); + at91_mci_write(AT91_PDC_RCR, sg->length / 4); + } + else { + at91_mci_write(AT91_PDC_RNPR, sg->dma_address); + at91_mci_write(AT91_PDC_RNCR, sg->length / 4); + } + } + + DBG("pre dma read done\n"); +} + +/* + * Handle after a dma read + */ +static void at91mci_post_dma_read(struct at91mci_host *host) +{ + struct mmc_command *cmd; + struct mmc_data *data; + + DBG("post dma read\n"); + + cmd = host->cmd; + if (!cmd) { + DBG("no command\n"); + return; + } + + data = cmd->data; + if (!data) { + DBG("no data\n"); + return; + } + + while (host->in_use_index < host->transfer_index) { + unsigned int *buffer; + int index; + int len; + + struct scatterlist *sg; + + DBG("finishing index %d\n", host->in_use_index); + + sg = &data->sg[host->in_use_index++]; + + DBG("Unmapping page %08X\n", sg->dma_address); + + dma_unmap_page(NULL, sg->dma_address, sg->length, DMA_FROM_DEVICE); + + /* Swap the contents of the buffer */ + buffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset; + DBG("buffer = %p, length = %d\n", buffer, sg->length); + + data->bytes_xfered += sg->length; + + len = sg->length / 4; + + for (index = 0; index < len; index++) { + buffer[index] = swab32(buffer[index]); + } + kunmap_atomic(buffer, KM_BIO_SRC_IRQ); + flush_dcache_page(sg->page); + } + + /* Is there another transfer to trigger? */ + if (host->transfer_index < data->sg_len) + at91mci_pre_dma_read(host); + else { + at91_mci_write(AT91_MCI_IER, AT91_MCI_RXBUFF); + at91_mci_write(AT91_PDC_PTCR, AT91_PDC_RXTDIS | AT91_PDC_TXTDIS); + } + + DBG("post dma read done\n"); +} + +/* + * Handle transmitted data + */ +static void at91_mci_handle_transmitted(struct at91mci_host *host) +{ + struct mmc_command *cmd; + struct mmc_data *data; + + DBG("Handling the transmit\n"); + + /* Disable the transfer */ + at91_mci_write(AT91_PDC_PTCR, AT91_PDC_RXTDIS | AT91_PDC_TXTDIS); + + /* Now wait for cmd ready */ + at91_mci_write(AT91_MCI_IDR, AT91_MCI_TXBUFE); + at91_mci_write(AT91_MCI_IER, AT91_MCI_NOTBUSY); + + cmd = host->cmd; + if (!cmd) return; + + data = cmd->data; + if (!data) return; + + data->bytes_xfered = host->total_length; +} + +/* + * Enable the controller + */ +static void at91_mci_enable(void) +{ + at91_mci_write(AT91_MCI_CR, AT91_MCI_MCIEN); + at91_mci_write(AT91_MCI_IDR, 0xFFFFFFFF); + at91_mci_write(AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC); + at91_mci_write(AT91_MCI_MR, 0x834A); + at91_mci_write(AT91_MCI_SDCR, 0x0); +} + +/* + * Disable the controller + */ +static void at91_mci_disable(void) +{ + at91_mci_write(AT91_MCI_CR, AT91_MCI_MCIDIS | AT91_MCI_SWRST); +} + +/* + * Send a command + * return the interrupts to enable + */ +static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd) +{ + unsigned int cmdr, mr; + unsigned int block_length; + struct mmc_data *data = cmd->data; + + unsigned int blocks; + unsigned int ier = 0; + + host->cmd = cmd; + + /* Not sure if this is needed */ +#if 0 + if ((at91_mci_read(AT91_MCI_SR) & AT91_MCI_RTOE) && (cmd->opcode == 1)) { + DBG("Clearing timeout\n"); + at91_mci_write(AT91_MCI_ARGR, 0); + at91_mci_write(AT91_MCI_CMDR, AT91_MCI_OPDCMD); + while (!(at91_mci_read(AT91_MCI_SR) & AT91_MCI_CMDRDY)) { + /* spin */ + DBG("Clearing: SR = %08X\n", at91_mci_read(AT91_MCI_SR)); + } + } +#endif + cmdr = cmd->opcode; + + if (mmc_resp_type(cmd) == MMC_RSP_NONE) + cmdr |= AT91_MCI_RSPTYP_NONE; + else { + /* if a response is expected then allow maximum response latancy */ + cmdr |= AT91_MCI_MAXLAT; + /* set 136 bit response for R2, 48 bit response otherwise */ + if (mmc_resp_type(cmd) == MMC_RSP_R2) + cmdr |= AT91_MCI_RSPTYP_136; + else + cmdr |= AT91_MCI_RSPTYP_48; + } + + if (data) { + block_length = 1 << data->blksz_bits; + blocks = data->blocks; + + /* always set data start - also set direction flag for read */ + if (data->flags & MMC_DATA_READ) + cmdr |= (AT91_MCI_TRDIR | AT91_MCI_TRCMD_START); + else if (data->flags & MMC_DATA_WRITE) + cmdr |= AT91_MCI_TRCMD_START; + + if (data->flags & MMC_DATA_STREAM) + cmdr |= AT91_MCI_TRTYP_STREAM; + if (data->flags & MMC_DATA_MULTI) + cmdr |= AT91_MCI_TRTYP_MULTIPLE; + } + else { + block_length = 0; + blocks = 0; + } + + if (cmd->opcode == MMC_STOP_TRANSMISSION) + cmdr |= AT91_MCI_TRCMD_STOP; + + if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) + cmdr |= AT91_MCI_OPDCMD; + + /* + * Set the arguments and send the command + */ + DBG("Sending command %d as %08X, arg = %08X, blocks = %d, length = %d (MR = %08lX)\n", + cmd->opcode, cmdr, cmd->arg, blocks, block_length, at91_mci_read(AT91_MCI_MR)); + + if (!data) { + at91_mci_write(AT91_PDC_PTCR, AT91_PDC_TXTDIS | AT91_PDC_RXTDIS); + at91_mci_write(AT91_PDC_RPR, 0); + at91_mci_write(AT91_PDC_RCR, 0); + at91_mci_write(AT91_PDC_RNPR, 0); + at91_mci_write(AT91_PDC_RNCR, 0); + at91_mci_write(AT91_PDC_TPR, 0); + at91_mci_write(AT91_PDC_TCR, 0); + at91_mci_write(AT91_PDC_TNPR, 0); + at91_mci_write(AT91_PDC_TNCR, 0); + + at91_mci_write(AT91_MCI_ARGR, cmd->arg); + at91_mci_write(AT91_MCI_CMDR, cmdr); + return AT91_MCI_CMDRDY; + } + + mr = at91_mci_read(AT91_MCI_MR) & 0x7fff; /* zero block length and PDC mode */ + at91_mci_write(AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE); + + /* + * Disable the PDC controller + */ + at91_mci_write(AT91_PDC_PTCR, AT91_PDC_RXTDIS | AT91_PDC_TXTDIS); + + if (cmdr & AT91_MCI_TRCMD_START) { + data->bytes_xfered = 0; + host->transfer_index = 0; + host->in_use_index = 0; + if (cmdr & AT91_MCI_TRDIR) { + /* + * Handle a read + */ + host->buffer = NULL; + host->total_length = 0; + + at91mci_pre_dma_read(host); + ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */; + } + else { + /* + * Handle a write + */ + host->total_length = block_length * blocks; + host->buffer = dma_alloc_coherent(NULL, + host->total_length, + &host->physical_address, GFP_KERNEL); + + at91mci_sg_to_dma(host, data); + + DBG("Transmitting %d bytes\n", host->total_length); + + at91_mci_write(AT91_PDC_TPR, host->physical_address); + at91_mci_write(AT91_PDC_TCR, host->total_length / 4); + ier = AT91_MCI_TXBUFE; + } + } + + /* + * Send the command and then enable the PDC - not the other way round as + * the data sheet says + */ + + at91_mci_write(AT91_MCI_ARGR, cmd->arg); + at91_mci_write(AT91_MCI_CMDR, cmdr); + + if (cmdr & AT91_MCI_TRCMD_START) { + if (cmdr & AT91_MCI_TRDIR) + at91_mci_write(AT91_PDC_PTCR, AT91_PDC_RXTEN); + else + at91_mci_write(AT91_PDC_PTCR, AT91_PDC_TXTEN); + } + return ier; +} + +/* + * Wait for a command to complete + */ +static void at91mci_process_command(struct at91mci_host *host, struct mmc_command *cmd) +{ + unsigned int ier; + + ier = at91_mci_send_command(host, cmd); + + DBG("setting ier to %08X\n", ier); + + /* Stop on errors or the required value */ + at91_mci_write(AT91_MCI_IER, 0xffff0000 | ier); +} + +/* + * Process the next step in the request + */ +static void at91mci_process_next(struct at91mci_host *host) +{ + if (!(host->flags & FL_SENT_COMMAND)) { + host->flags |= FL_SENT_COMMAND; + at91mci_process_command(host, host->request->cmd); + } + else if ((!(host->flags & FL_SENT_STOP)) && host->request->stop) { + host->flags |= FL_SENT_STOP; + at91mci_process_command(host, host->request->stop); + } + else + mmc_request_done(host->mmc, host->request); +} + +/* + * Handle a command that has been completed + */ +static void at91mci_completed_command(struct at91mci_host *host) +{ + struct mmc_command *cmd = host->cmd; + unsigned int status; + + at91_mci_write(AT91_MCI_IDR, 0xffffffff); + + cmd->resp[0] = at91_mci_read(AT91_MCI_RSPR(0)); + cmd->resp[1] = at91_mci_read(AT91_MCI_RSPR(1)); + cmd->resp[2] = at91_mci_read(AT91_MCI_RSPR(2)); + cmd->resp[3] = at91_mci_read(AT91_MCI_RSPR(3)); + + if (host->buffer) { + dma_free_coherent(NULL, host->total_length, host->buffer, host->physical_address); + host->buffer = NULL; + } + + status = at91_mci_read(AT91_MCI_SR); + + DBG("Status = %08X [%08X %08X %08X %08X]\n", + status, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); + + if (status & (AT91_MCI_RINDE | AT91_MCI_RDIRE | AT91_MCI_RCRCE | + AT91_MCI_RENDE | AT91_MCI_RTOE | AT91_MCI_DCRCE | + AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE)) { + if ((status & AT91_MCI_RCRCE) && + ((cmd->opcode == MMC_SEND_OP_COND) || (cmd->opcode == SD_APP_OP_COND))) { + cmd->error = MMC_ERR_NONE; + } + else { + if (status & (AT91_MCI_RTOE | AT91_MCI_DTOE)) + cmd->error = MMC_ERR_TIMEOUT; + else if (status & (AT91_MCI_RCRCE | AT91_MCI_DCRCE)) + cmd->error = MMC_ERR_BADCRC; + else if (status & (AT91_MCI_OVRE | AT91_MCI_UNRE)) + cmd->error = MMC_ERR_FIFO; + else + cmd->error = MMC_ERR_FAILED; + + DBG("Error detected and set to %d (cmd = %d, retries = %d)\n", + cmd->error, cmd->opcode, cmd->retries); + } + } + else + cmd->error = MMC_ERR_NONE; + + at91mci_process_next(host); +} + +/* + * Handle an MMC request + */ +static void at91_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct at91mci_host *host = mmc_priv(mmc); + host->request = mrq; + host->flags = 0; + + at91mci_process_next(host); +} + +/* + * Set the IOS + */ +static void at91_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + int clkdiv; + struct at91mci_host *host = mmc_priv(mmc); + unsigned long at91_master_clock = clk_get_rate(mci_clk); + + DBG("Clock %uHz, busmode %u, powermode %u, Vdd %u\n", + ios->clock, ios->bus_mode, ios->power_mode, ios->vdd); + + if (host) + host->bus_mode = ios->bus_mode; + else + printk("MMC: No host for bus_mode\n"); + + if (ios->clock == 0) { + /* Disable the MCI controller */ + at91_mci_write(AT91_MCI_CR, AT91_MCI_MCIDIS); + clkdiv = 0; + } + else { + /* Enable the MCI controller */ + at91_mci_write(AT91_MCI_CR, AT91_MCI_MCIEN); + + if ((at91_master_clock % (ios->clock * 2)) == 0) + clkdiv = ((at91_master_clock / ios->clock) / 2) - 1; + else + clkdiv = (at91_master_clock / ios->clock) / 2; + + DBG("clkdiv = %d. mcck = %ld\n", clkdiv, + at91_master_clock / (2 * (clkdiv + 1))); + } + if (ios->bus_width == MMC_BUS_WIDTH_4 && host->board->wire4) { + DBG("MMC: Setting controller bus width to 4\n"); + at91_mci_write(AT91_MCI_SDCR, at91_mci_read(AT91_MCI_SDCR) | AT91_MCI_SDCBUS); + } + else { + DBG("MMC: Setting controller bus width to 1\n"); + at91_mci_write(AT91_MCI_SDCR, at91_mci_read(AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS); + } + + /* Set the clock divider */ + at91_mci_write(AT91_MCI_MR, (at91_mci_read(AT91_MCI_MR) & ~AT91_MCI_CLKDIV) | clkdiv); + + /* maybe switch power to the card */ + if (host && host->board->vcc_pin) { + switch (ios->power_mode) { + case MMC_POWER_OFF: + at91_set_gpio_output(host->board->vcc_pin, 0); + break; + case MMC_POWER_UP: + case MMC_POWER_ON: + at91_set_gpio_output(host->board->vcc_pin, 1); + break; + } + } +} + +/* + * Handle an interrupt + */ +static irqreturn_t at91_mci_irq(int irq, void *devid, struct pt_regs *regs) +{ + struct at91mci_host *host = devid; + int completed = 0; + + unsigned int int_status; + + if (host == NULL) + return IRQ_HANDLED; + + int_status = at91_mci_read(AT91_MCI_SR); + DBG("MCI irq: status = %08X, %08lX, %08lX\n", int_status, at91_mci_read(AT91_MCI_IMR), + int_status & at91_mci_read(AT91_MCI_IMR)); + + if ((int_status & at91_mci_read(AT91_MCI_IMR)) & 0xffff0000) + completed = 1; + + int_status &= at91_mci_read(AT91_MCI_IMR); + + if (int_status & AT91_MCI_UNRE) + DBG("MMC: Underrun error\n"); + if (int_status & AT91_MCI_OVRE) + DBG("MMC: Overrun error\n"); + if (int_status & AT91_MCI_DTOE) + DBG("MMC: Data timeout\n"); + if (int_status & AT91_MCI_DCRCE) + DBG("MMC: CRC error in data\n"); + if (int_status & AT91_MCI_RTOE) + DBG("MMC: Response timeout\n"); + if (int_status & AT91_MCI_RENDE) + DBG("MMC: Response end bit error\n"); + if (int_status & AT91_MCI_RCRCE) + DBG("MMC: Response CRC error\n"); + if (int_status & AT91_MCI_RDIRE) + DBG("MMC: Response direction error\n"); + if (int_status & AT91_MCI_RINDE) + DBG("MMC: Response index error\n"); + + /* Only continue processing if no errors */ + if (!completed) { + if (int_status & AT91_MCI_TXBUFE) { + DBG("TX buffer empty\n"); + at91_mci_handle_transmitted(host); + } + + if (int_status & AT91_MCI_RXBUFF) { + DBG("RX buffer full\n"); + at91_mci_write(AT91_MCI_IER, AT91_MCI_CMDRDY); + } + + if (int_status & AT91_MCI_ENDTX) { + DBG("Transmit has ended\n"); + } + + if (int_status & AT91_MCI_ENDRX) { + DBG("Receive has ended\n"); + at91mci_post_dma_read(host); + } + + if (int_status & AT91_MCI_NOTBUSY) { + DBG("Card is ready\n"); + at91_mci_write(AT91_MCI_IER, AT91_MCI_CMDRDY); + } + + if (int_status & AT91_MCI_DTIP) { + DBG("Data transfer in progress\n"); + } + + if (int_status & AT91_MCI_BLKE) { + DBG("Block transfer has ended\n"); + } + + if (int_status & AT91_MCI_TXRDY) { + DBG("Ready to transmit\n"); + } + + if (int_status & AT91_MCI_RXRDY) { + DBG("Ready to receive\n"); + } + + if (int_status & AT91_MCI_CMDRDY) { + DBG("Command ready\n"); + completed = 1; + } + } + at91_mci_write(AT91_MCI_IDR, int_status); + + if (completed) { + DBG("Completed command\n"); + at91_mci_write(AT91_MCI_IDR, 0xffffffff); + at91mci_completed_command(host); + } + + return IRQ_HANDLED; +} + +static irqreturn_t at91_mmc_det_irq(int irq, void *_host, struct pt_regs *regs) +{ + struct at91mci_host *host = _host; + int present = !at91_get_gpio_value(irq); + + /* + * we expect this irq on both insert and remove, + * and use a short delay to debounce. + */ + if (present != host->present) { + host->present = present; + DBG("%s: card %s\n", mmc_hostname(host->mmc), + present ? "insert" : "remove"); + if (!present) { + DBG("****** Resetting SD-card bus width ******\n"); + at91_mci_write(AT91_MCI_SDCR, 0); + } + mmc_detect_change(host->mmc, msecs_to_jiffies(100)); + } + return IRQ_HANDLED; +} + +int at91_mci_get_ro(struct mmc_host *mmc) +{ + int read_only = 0; + struct at91mci_host *host = mmc_priv(mmc); + + if (host->board->wp_pin) { + read_only = at91_get_gpio_value(host->board->wp_pin); + printk(KERN_WARNING "%s: card is %s\n", mmc_hostname(mmc), + (read_only ? "read-only" : "read-write") ); + } + else { + printk(KERN_WARNING "%s: host does not support reading read-only " + "switch. Assuming write-enable.\n", mmc_hostname(mmc)); + } + return read_only; +} + +static struct mmc_host_ops at91_mci_ops = { + .request = at91_mci_request, + .set_ios = at91_mci_set_ios, + .get_ro = at91_mci_get_ro, +}; + +/* + * Probe for the device + */ +static int at91_mci_probe(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct at91mci_host *host; + int ret; + + DBG("Probe MCI devices\n"); + at91_mci_disable(); + at91_mci_enable(); + + mmc = mmc_alloc_host(sizeof(struct at91mci_host), &pdev->dev); + if (!mmc) { + DBG("Failed to allocate mmc host\n"); + return -ENOMEM; + } + + mmc->ops = &at91_mci_ops; + mmc->f_min = 375000; + mmc->f_max = 25000000; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + + host = mmc_priv(mmc); + host->mmc = mmc; + host->buffer = NULL; + host->bus_mode = 0; + host->board = pdev->dev.platform_data; + if (host->board->wire4) { +#ifdef SUPPORT_4WIRE + mmc->caps |= MMC_CAP_4_BIT_DATA; +#else + printk("MMC: 4 wire bus mode not supported by this driver - using 1 wire\n"); +#endif + } + + /* + * Get Clock + */ + mci_clk = clk_get(&pdev->dev, "mci_clk"); + if (!mci_clk) { + printk(KERN_ERR "AT91 MMC: no clock defined.\n"); + return -ENODEV; + } + clk_enable(mci_clk); /* Enable the peripheral clock */ + + /* + * Allocate the MCI interrupt + */ + ret = request_irq(AT91_ID_MCI, at91_mci_irq, SA_SHIRQ, DRIVER_NAME, host); + if (ret) { + DBG("Failed to request MCI interrupt\n"); + return ret; + } + + platform_set_drvdata(pdev, mmc); + + /* + * Add host to MMC layer + */ + if (host->board->det_pin) + host->present = !at91_get_gpio_value(host->board->det_pin); + else + host->present = -1; + + mmc_add_host(mmc); + + /* + * monitor card insertion/removal if we can + */ + if (host->board->det_pin) { + ret = request_irq(host->board->det_pin, at91_mmc_det_irq, + SA_SAMPLE_RANDOM, DRIVER_NAME, host); + if (ret) + DBG("couldn't allocate MMC detect irq\n"); + } + + DBG(KERN_INFO "Added MCI driver\n"); + + return 0; +} + +/* + * Remove a device + */ +static int at91_mci_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct at91mci_host *host; + + if (!mmc) + return -1; + + host = mmc_priv(mmc); + + if (host->present != -1) { + free_irq(host->board->det_pin, host); + cancel_delayed_work(&host->mmc->detect); + } + + mmc_remove_host(mmc); + at91_mci_disable(); + free_irq(AT91_ID_MCI, host); + mmc_free_host(mmc); + + clk_disable(mci_clk); /* Disable the peripheral clock */ + clk_put(mci_clk); + + platform_set_drvdata(pdev, NULL); + + DBG("Removed\n"); + + return 0; +} + +#ifdef CONFIG_PM +static int at91_mci_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + int ret = 0; + + if (mmc) + ret = mmc_suspend_host(mmc, state); + + return ret; +} + +static int at91_mci_resume(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + int ret = 0; + + if (mmc) + ret = mmc_resume_host(mmc); + + return ret; +} +#else +#define at91_mci_suspend NULL +#define at91_mci_resume NULL +#endif + +static struct platform_driver at91_mci_driver = { + .probe = at91_mci_probe, + .remove = at91_mci_remove, + .suspend = at91_mci_suspend, + .resume = at91_mci_resume, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init at91_mci_init(void) +{ + return platform_driver_register(&at91_mci_driver); +} + +static void __exit at91_mci_exit(void) +{ + platform_driver_unregister(&at91_mci_driver); +} + +module_init(at91_mci_init); +module_exit(at91_mci_exit); + +MODULE_DESCRIPTION("AT91 Multimedia Card Interface driver"); +MODULE_AUTHOR("Nick Randell"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/au1xmmc.c b/drivers/mmc/au1xmmc.c index 85e89c77bdea..c0326bbc5f28 100644 --- a/drivers/mmc/au1xmmc.c +++ b/drivers/mmc/au1xmmc.c @@ -56,12 +56,11 @@ #define DRIVER_NAME "au1xxx-mmc" /* Set this to enable special debugging macros */ -/* #define MMC_DEBUG */ -#ifdef MMC_DEBUG -#define DEBUG(fmt, idx, args...) printk("au1xx(%d): DEBUG: " fmt, idx, ##args) +#ifdef DEBUG +#define DBG(fmt, idx, args...) printk("au1xx(%d): DEBUG: " fmt, idx, ##args) #else -#define DEBUG(fmt, idx, args...) +#define DBG(fmt, idx, args...) #endif const struct { @@ -424,18 +423,18 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host) break; if (status & SD_STATUS_RC) { - DEBUG("RX CRC Error [%d + %d].\n", host->id, + DBG("RX CRC Error [%d + %d].\n", host->id, host->pio.len, count); break; } if (status & SD_STATUS_RO) { - DEBUG("RX Overrun [%d + %d]\n", host->id, + DBG("RX Overrun [%d + %d]\n", host->id, host->pio.len, count); break; } else if (status & SD_STATUS_RU) { - DEBUG("RX Underrun [%d + %d]\n", host->id, + DBG("RX Underrun [%d + %d]\n", host->id, host->pio.len, count); break; } @@ -721,7 +720,7 @@ static void au1xmmc_set_ios(struct mmc_host* mmc, struct mmc_ios* ios) { struct au1xmmc_host *host = mmc_priv(mmc); - DEBUG("set_ios (power=%u, clock=%uHz, vdd=%u, mode=%u)\n", + DBG("set_ios (power=%u, clock=%uHz, vdd=%u, mode=%u)\n", host->id, ios->power_mode, ios->clock, ios->vdd, ios->bus_mode); @@ -810,7 +809,7 @@ static irqreturn_t au1xmmc_irq(int irq, void *dev_id, struct pt_regs *regs) au1xmmc_receive_pio(host); } else if (status & 0x203FBC70) { - DEBUG("Unhandled status %8.8x\n", host->id, status); + DBG("Unhandled status %8.8x\n", host->id, status); handled = 0; } @@ -839,7 +838,7 @@ static void au1xmmc_poll_event(unsigned long arg) if (host->mrq != NULL) { u32 status = au_readl(HOST_STATUS(host)); - DEBUG("PENDING - %8.8x\n", host->id, status); + DBG("PENDING - %8.8x\n", host->id, status); } mod_timer(&host->timer, jiffies + AU1XMMC_DETECT_TIMEOUT); diff --git a/drivers/mmc/imxmmc.c b/drivers/mmc/imxmmc.c new file mode 100644 index 000000000000..ffb7f55d3467 --- /dev/null +++ b/drivers/mmc/imxmmc.c @@ -0,0 +1,1096 @@ +/* + * linux/drivers/mmc/imxmmc.c - Motorola i.MX MMCI driver + * + * Copyright (C) 2004 Sascha Hauer, Pengutronix + * Copyright (C) 2006 Pavel Pisa, PiKRON + * + * derived from pxamci.c by Russell King + * + * 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. + * + * 2005-04-17 Pavel Pisa + * Changed to conform redesigned i.MX scatter gather DMA interface + * + * 2005-11-04 Pavel Pisa + * Updated for 2.6.14 kernel + * + * 2005-12-13 Jay Monkman + * Found and corrected problems in the write path + * + * 2005-12-30 Pavel Pisa + * The event handling rewritten right way in softirq. + * Added many ugly hacks and delays to overcome SDHC + * deficiencies + * + */ +#include + +#ifdef CONFIG_MMC_DEBUG +#define DEBUG +#else +#undef DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "imxmmc.h" + +#define DRIVER_NAME "imx-mmc" + +#define IMXMCI_INT_MASK_DEFAULT (INT_MASK_BUF_READY | INT_MASK_DATA_TRAN | \ + INT_MASK_WRITE_OP_DONE | INT_MASK_END_CMD_RES | \ + INT_MASK_AUTO_CARD_DETECT | INT_MASK_DAT0_EN | INT_MASK_SDIO) + +struct imxmci_host { + struct mmc_host *mmc; + spinlock_t lock; + struct resource *res; + int irq; + imx_dmach_t dma; + unsigned int clkrt; + unsigned int cmdat; + volatile unsigned int imask; + unsigned int power_mode; + unsigned int present; + struct imxmmc_platform_data *pdata; + + struct mmc_request *req; + struct mmc_command *cmd; + struct mmc_data *data; + + struct timer_list timer; + struct tasklet_struct tasklet; + unsigned int status_reg; + unsigned long pending_events; + /* Next to fields are there for CPU driven transfers to overcome SDHC deficiencies */ + u16 *data_ptr; + unsigned int data_cnt; + atomic_t stuck_timeout; + + unsigned int dma_nents; + unsigned int dma_size; + unsigned int dma_dir; + int dma_allocated; + + unsigned char actual_bus_width; +}; + +#define IMXMCI_PEND_IRQ_b 0 +#define IMXMCI_PEND_DMA_END_b 1 +#define IMXMCI_PEND_DMA_ERR_b 2 +#define IMXMCI_PEND_WAIT_RESP_b 3 +#define IMXMCI_PEND_DMA_DATA_b 4 +#define IMXMCI_PEND_CPU_DATA_b 5 +#define IMXMCI_PEND_CARD_XCHG_b 6 +#define IMXMCI_PEND_SET_INIT_b 7 + +#define IMXMCI_PEND_IRQ_m (1 << IMXMCI_PEND_IRQ_b) +#define IMXMCI_PEND_DMA_END_m (1 << IMXMCI_PEND_DMA_END_b) +#define IMXMCI_PEND_DMA_ERR_m (1 << IMXMCI_PEND_DMA_ERR_b) +#define IMXMCI_PEND_WAIT_RESP_m (1 << IMXMCI_PEND_WAIT_RESP_b) +#define IMXMCI_PEND_DMA_DATA_m (1 << IMXMCI_PEND_DMA_DATA_b) +#define IMXMCI_PEND_CPU_DATA_m (1 << IMXMCI_PEND_CPU_DATA_b) +#define IMXMCI_PEND_CARD_XCHG_m (1 << IMXMCI_PEND_CARD_XCHG_b) +#define IMXMCI_PEND_SET_INIT_m (1 << IMXMCI_PEND_SET_INIT_b) + +static void imxmci_stop_clock(struct imxmci_host *host) +{ + int i = 0; + MMC_STR_STP_CLK &= ~STR_STP_CLK_START_CLK; + while(i < 0x1000) { + if(!(i & 0x7f)) + MMC_STR_STP_CLK |= STR_STP_CLK_STOP_CLK; + + if(!(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN)) { + /* Check twice before cut */ + if(!(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN)) + return; + } + + i++; + } + dev_dbg(mmc_dev(host->mmc), "imxmci_stop_clock blocked, no luck\n"); +} + +static void imxmci_start_clock(struct imxmci_host *host) +{ + int i = 0; + MMC_STR_STP_CLK &= ~STR_STP_CLK_STOP_CLK; + while(i < 0x1000) { + if(!(i & 0x7f)) + MMC_STR_STP_CLK |= STR_STP_CLK_START_CLK; + + if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN) { + /* Check twice before cut */ + if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN) + return; + } + + i++; + } + dev_dbg(mmc_dev(host->mmc), "imxmci_start_clock blocked, no luck\n"); +} + +static void imxmci_softreset(void) +{ + /* reset sequence */ + MMC_STR_STP_CLK = 0x8; + MMC_STR_STP_CLK = 0xD; + MMC_STR_STP_CLK = 0x5; + MMC_STR_STP_CLK = 0x5; + MMC_STR_STP_CLK = 0x5; + MMC_STR_STP_CLK = 0x5; + MMC_STR_STP_CLK = 0x5; + MMC_STR_STP_CLK = 0x5; + MMC_STR_STP_CLK = 0x5; + MMC_STR_STP_CLK = 0x5; + + MMC_RES_TO = 0xff; + MMC_BLK_LEN = 512; + MMC_NOB = 1; +} + +static int imxmci_busy_wait_for_status(struct imxmci_host *host, + unsigned int *pstat, unsigned int stat_mask, + int timeout, const char *where) +{ + int loops=0; + while(!(*pstat & stat_mask)) { + loops+=2; + if(loops >= timeout) { + dev_dbg(mmc_dev(host->mmc), "busy wait timeout in %s, STATUS = 0x%x (0x%x)\n", + where, *pstat, stat_mask); + return -1; + } + udelay(2); + *pstat |= MMC_STATUS; + } + if(!loops) + return 0; + + dev_info(mmc_dev(host->mmc), "busy wait for %d usec in %s, STATUS = 0x%x (0x%x)\n", + loops, where, *pstat, stat_mask); + return loops; +} + +static void imxmci_setup_data(struct imxmci_host *host, struct mmc_data *data) +{ + unsigned int nob = data->blocks; + unsigned int blksz = 1 << data->blksz_bits; + unsigned int datasz = nob * blksz; + int i; + + if (data->flags & MMC_DATA_STREAM) + nob = 0xffff; + + host->data = data; + data->bytes_xfered = 0; + + MMC_NOB = nob; + MMC_BLK_LEN = blksz; + + /* + * DMA cannot be used for small block sizes, we have to use CPU driven transfers otherwise. + * We are in big troubles for non-512 byte transfers according to note in the paragraph + * 20.6.7 of User Manual anyway, but we need to be able to transfer SCR at least. + * The situation is even more complex in reality. The SDHC in not able to handle wll + * partial FIFO fills and reads. The length has to be rounded up to burst size multiple. + * This is required for SCR read at least. + */ + if (datasz < 64) { + host->dma_size = datasz; + if (data->flags & MMC_DATA_READ) { + host->dma_dir = DMA_FROM_DEVICE; + + /* Hack to enable read SCR */ + if(datasz < 16) { + MMC_NOB = 1; + MMC_BLK_LEN = 16; + } + } else { + host->dma_dir = DMA_TO_DEVICE; + } + + /* Convert back to virtual address */ + host->data_ptr = (u16*)(page_address(data->sg->page) + data->sg->offset); + host->data_cnt = 0; + + clear_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events); + set_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events); + + return; + } + + if (data->flags & MMC_DATA_READ) { + host->dma_dir = DMA_FROM_DEVICE; + host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg, + data->sg_len, host->dma_dir); + + imx_dma_setup_sg(host->dma, data->sg, data->sg_len, datasz, + host->res->start + MMC_BUFFER_ACCESS_OFS, DMA_MODE_READ); + + /*imx_dma_setup_mem2dev_ccr(host->dma, DMA_MODE_READ, IMX_DMA_WIDTH_16, CCR_REN);*/ + CCR(host->dma) = CCR_DMOD_LINEAR | CCR_DSIZ_32 | CCR_SMOD_FIFO | CCR_SSIZ_16 | CCR_REN; + } else { + host->dma_dir = DMA_TO_DEVICE; + + host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg, + data->sg_len, host->dma_dir); + + imx_dma_setup_sg(host->dma, data->sg, data->sg_len, datasz, + host->res->start + MMC_BUFFER_ACCESS_OFS, DMA_MODE_WRITE); + + /*imx_dma_setup_mem2dev_ccr(host->dma, DMA_MODE_WRITE, IMX_DMA_WIDTH_16, CCR_REN);*/ + CCR(host->dma) = CCR_SMOD_LINEAR | CCR_SSIZ_32 | CCR_DMOD_FIFO | CCR_DSIZ_16 | CCR_REN; + } + +#if 1 /* This code is there only for consistency checking and can be disabled in future */ + host->dma_size = 0; + for(i=0; idma_nents; i++) + host->dma_size+=data->sg[i].length; + + if (datasz > host->dma_size) { + dev_err(mmc_dev(host->mmc), "imxmci_setup_data datasz 0x%x > 0x%x dm_size\n", + datasz, host->dma_size); + } +#endif + + host->dma_size = datasz; + + wmb(); + + if(host->actual_bus_width == MMC_BUS_WIDTH_4) + BLR(host->dma) = 0; /* burst 64 byte read / 64 bytes write */ + else + BLR(host->dma) = 16; /* burst 16 byte read / 16 bytes write */ + + RSSR(host->dma) = DMA_REQ_SDHC; + + set_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events); + clear_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events); + + /* start DMA engine for read, write is delayed after initial response */ + if (host->dma_dir == DMA_FROM_DEVICE) { + imx_dma_enable(host->dma); + } +} + +static void imxmci_start_cmd(struct imxmci_host *host, struct mmc_command *cmd, unsigned int cmdat) +{ + unsigned long flags; + u32 imask; + + WARN_ON(host->cmd != NULL); + host->cmd = cmd; + + if (cmd->flags & MMC_RSP_BUSY) + cmdat |= CMD_DAT_CONT_BUSY; + + switch (mmc_resp_type(cmd)) { + case MMC_RSP_R1: /* short CRC, OPCODE */ + case MMC_RSP_R1B:/* short CRC, OPCODE, BUSY */ + cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R1; + break; + case MMC_RSP_R2: /* long 136 bit + CRC */ + cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R2; + break; + case MMC_RSP_R3: /* short */ + cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R3; + break; + case MMC_RSP_R6: /* short CRC */ + cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R6; + break; + default: + break; + } + + if ( test_and_clear_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events) ) + cmdat |= CMD_DAT_CONT_INIT; /* This command needs init */ + + if ( host->actual_bus_width == MMC_BUS_WIDTH_4 ) + cmdat |= CMD_DAT_CONT_BUS_WIDTH_4; + + MMC_CMD = cmd->opcode; + MMC_ARGH = cmd->arg >> 16; + MMC_ARGL = cmd->arg & 0xffff; + MMC_CMD_DAT_CONT = cmdat; + + atomic_set(&host->stuck_timeout, 0); + set_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events); + + + imask = IMXMCI_INT_MASK_DEFAULT; + imask &= ~INT_MASK_END_CMD_RES; + if ( cmdat & CMD_DAT_CONT_DATA_ENABLE ) { + /*imask &= ~INT_MASK_BUF_READY;*/ + imask &= ~INT_MASK_DATA_TRAN; + if ( cmdat & CMD_DAT_CONT_WRITE ) + imask &= ~INT_MASK_WRITE_OP_DONE; + if(test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events)) + imask &= ~INT_MASK_BUF_READY; + } + + spin_lock_irqsave(&host->lock, flags); + host->imask = imask; + MMC_INT_MASK = host->imask; + spin_unlock_irqrestore(&host->lock, flags); + + dev_dbg(mmc_dev(host->mmc), "CMD%02d (0x%02x) mask set to 0x%04x\n", + cmd->opcode, cmd->opcode, imask); + + imxmci_start_clock(host); +} + +static void imxmci_finish_request(struct imxmci_host *host, struct mmc_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + + host->pending_events &= ~(IMXMCI_PEND_WAIT_RESP_m | IMXMCI_PEND_DMA_END_m | + IMXMCI_PEND_DMA_DATA_m | IMXMCI_PEND_CPU_DATA_m); + + host->imask = IMXMCI_INT_MASK_DEFAULT; + MMC_INT_MASK = host->imask; + + spin_unlock_irqrestore(&host->lock, flags); + + host->req = NULL; + host->cmd = NULL; + host->data = NULL; + mmc_request_done(host->mmc, req); +} + +static int imxmci_finish_data(struct imxmci_host *host, unsigned int stat) +{ + struct mmc_data *data = host->data; + int data_error; + + if(test_and_clear_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)){ + imx_dma_disable(host->dma); + dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_nents, + host->dma_dir); + } + + if ( stat & STATUS_ERR_MASK ) { + dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n",stat); + if(stat & (STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR)) + data->error = MMC_ERR_BADCRC; + else if(stat & STATUS_TIME_OUT_READ) + data->error = MMC_ERR_TIMEOUT; + else + data->error = MMC_ERR_FAILED; + } else { + data->bytes_xfered = host->dma_size; + } + + data_error = data->error; + + host->data = NULL; + + return data_error; +} + +static int imxmci_cmd_done(struct imxmci_host *host, unsigned int stat) +{ + struct mmc_command *cmd = host->cmd; + int i; + u32 a,b,c; + struct mmc_data *data = host->data; + + if (!cmd) + return 0; + + host->cmd = NULL; + + if (stat & STATUS_TIME_OUT_RESP) { + dev_dbg(mmc_dev(host->mmc), "CMD TIMEOUT\n"); + cmd->error = MMC_ERR_TIMEOUT; + } else if (stat & STATUS_RESP_CRC_ERR && cmd->flags & MMC_RSP_CRC) { + dev_dbg(mmc_dev(host->mmc), "cmd crc error\n"); + cmd->error = MMC_ERR_BADCRC; + } + + if(cmd->flags & MMC_RSP_PRESENT) { + if(cmd->flags & MMC_RSP_136) { + for (i = 0; i < 4; i++) { + u32 a = MMC_RES_FIFO & 0xffff; + u32 b = MMC_RES_FIFO & 0xffff; + cmd->resp[i] = a<<16 | b; + } + } else { + a = MMC_RES_FIFO & 0xffff; + b = MMC_RES_FIFO & 0xffff; + c = MMC_RES_FIFO & 0xffff; + cmd->resp[0] = a<<24 | b<<8 | c>>8; + } + } + + dev_dbg(mmc_dev(host->mmc), "RESP 0x%08x, 0x%08x, 0x%08x, 0x%08x, error %d\n", + cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3], cmd->error); + + if (data && (cmd->error == MMC_ERR_NONE) && !(stat & STATUS_ERR_MASK)) { + if (host->req->data->flags & MMC_DATA_WRITE) { + + /* Wait for FIFO to be empty before starting DMA write */ + + stat = MMC_STATUS; + if(imxmci_busy_wait_for_status(host, &stat, + STATUS_APPL_BUFF_FE, + 40, "imxmci_cmd_done DMA WR") < 0) { + cmd->error = MMC_ERR_FIFO; + imxmci_finish_data(host, stat); + if(host->req) + imxmci_finish_request(host, host->req); + dev_warn(mmc_dev(host->mmc), "STATUS = 0x%04x\n", + stat); + return 0; + } + + if(test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) { + imx_dma_enable(host->dma); + } + } + } else { + struct mmc_request *req; + imxmci_stop_clock(host); + req = host->req; + + if(data) + imxmci_finish_data(host, stat); + + if( req ) { + imxmci_finish_request(host, req); + } else { + dev_warn(mmc_dev(host->mmc), "imxmci_cmd_done: no request to finish\n"); + } + } + + return 1; +} + +static int imxmci_data_done(struct imxmci_host *host, unsigned int stat) +{ + struct mmc_data *data = host->data; + int data_error; + + if (!data) + return 0; + + data_error = imxmci_finish_data(host, stat); + + if (host->req->stop && (data_error == MMC_ERR_NONE)) { + imxmci_stop_clock(host); + imxmci_start_cmd(host, host->req->stop, 0); + } else { + struct mmc_request *req; + req = host->req; + if( req ) { + imxmci_finish_request(host, req); + } else { + dev_warn(mmc_dev(host->mmc), "imxmci_data_done: no request to finish\n"); + } + } + + return 1; +} + +static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat) +{ + int i; + int burst_len; + int flush_len; + int trans_done = 0; + unsigned int stat = *pstat; + + if(host->actual_bus_width == MMC_BUS_WIDTH_4) + burst_len = 16; + else + burst_len = 64; + + /* This is unfortunately required */ + dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data running STATUS = 0x%x\n", + stat); + + if(host->dma_dir == DMA_FROM_DEVICE) { + imxmci_busy_wait_for_status(host, &stat, + STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE, + 20, "imxmci_cpu_driven_data read"); + + while((stat & (STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE)) && + (host->data_cnt < host->dma_size)) { + if(burst_len >= host->dma_size - host->data_cnt) { + flush_len = burst_len; + burst_len = host->dma_size - host->data_cnt; + flush_len -= burst_len; + host->data_cnt = host->dma_size; + trans_done = 1; + } else { + flush_len = 0; + host->data_cnt += burst_len; + } + + for(i = burst_len; i>=2 ; i-=2) { + *(host->data_ptr++) = MMC_BUFFER_ACCESS; + udelay(20); /* required for clocks < 8MHz*/ + } + + if(i == 1) + *(u8*)(host->data_ptr) = MMC_BUFFER_ACCESS; + + stat = MMC_STATUS; + + /* Flush extra bytes from FIFO */ + while(flush_len >= 2){ + flush_len -= 2; + i = MMC_BUFFER_ACCESS; + stat = MMC_STATUS; + stat &= ~STATUS_CRC_READ_ERR; /* Stupid but required there */ + } + + dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data read burst %d STATUS = 0x%x\n", + burst_len, stat); + } + } else { + imxmci_busy_wait_for_status(host, &stat, + STATUS_APPL_BUFF_FE, + 20, "imxmci_cpu_driven_data write"); + + while((stat & STATUS_APPL_BUFF_FE) && + (host->data_cnt < host->dma_size)) { + if(burst_len >= host->dma_size - host->data_cnt) { + burst_len = host->dma_size - host->data_cnt; + host->data_cnt = host->dma_size; + trans_done = 1; + } else { + host->data_cnt += burst_len; + } + + for(i = burst_len; i>0 ; i-=2) + MMC_BUFFER_ACCESS = *(host->data_ptr++); + + stat = MMC_STATUS; + + dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data write burst %d STATUS = 0x%x\n", + burst_len, stat); + } + } + + *pstat = stat; + + return trans_done; +} + +static void imxmci_dma_irq(int dma, void *devid, struct pt_regs *regs) +{ + struct imxmci_host *host = devid; + uint32_t stat = MMC_STATUS; + + atomic_set(&host->stuck_timeout, 0); + host->status_reg = stat; + set_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events); + tasklet_schedule(&host->tasklet); +} + +static irqreturn_t imxmci_irq(int irq, void *devid, struct pt_regs *regs) +{ + struct imxmci_host *host = devid; + uint32_t stat = MMC_STATUS; + int handled = 1; + + MMC_INT_MASK = host->imask | INT_MASK_SDIO | INT_MASK_AUTO_CARD_DETECT; + + atomic_set(&host->stuck_timeout, 0); + host->status_reg = stat; + set_bit(IMXMCI_PEND_IRQ_b, &host->pending_events); + tasklet_schedule(&host->tasklet); + + return IRQ_RETVAL(handled);; +} + +static void imxmci_tasklet_fnc(unsigned long data) +{ + struct imxmci_host *host = (struct imxmci_host *)data; + u32 stat; + unsigned int data_dir_mask = 0; /* STATUS_WR_CRC_ERROR_CODE_MASK */ + int timeout = 0; + + if(atomic_read(&host->stuck_timeout) > 4) { + char *what; + timeout = 1; + stat = MMC_STATUS; + host->status_reg = stat; + if (test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) + if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) + what = "RESP+DMA"; + else + what = "RESP"; + else + if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) + if(test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events)) + what = "DATA"; + else + what = "DMA"; + else + what = "???"; + + dev_err(mmc_dev(host->mmc), "%s TIMEOUT, hardware stucked STATUS = 0x%04x IMASK = 0x%04x\n", + what, stat, MMC_INT_MASK); + dev_err(mmc_dev(host->mmc), "CMD_DAT_CONT = 0x%04x, MMC_BLK_LEN = 0x%04x, MMC_NOB = 0x%04x, DMA_CCR = 0x%08x\n", + MMC_CMD_DAT_CONT, MMC_BLK_LEN, MMC_NOB, CCR(host->dma)); + dev_err(mmc_dev(host->mmc), "CMD%d, bus %d-bit, dma_size = 0x%x\n", + host->cmd?host->cmd->opcode:0, 1<actual_bus_width, host->dma_size); + } + + if(!host->present || timeout) + host->status_reg = STATUS_TIME_OUT_RESP | STATUS_TIME_OUT_READ | + STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR; + + if(test_bit(IMXMCI_PEND_IRQ_b, &host->pending_events) || timeout) { + clear_bit(IMXMCI_PEND_IRQ_b, &host->pending_events); + + stat = MMC_STATUS; + /* + * This is not required in theory, but there is chance to miss some flag + * which clears automatically by mask write, FreeScale original code keeps + * stat from IRQ time so do I + */ + stat |= host->status_reg; + + if(test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) { + imxmci_busy_wait_for_status(host, &stat, + STATUS_END_CMD_RESP | STATUS_ERR_MASK, + 20, "imxmci_tasklet_fnc resp (ERRATUM #4)"); + } + + if(stat & (STATUS_END_CMD_RESP | STATUS_ERR_MASK)) { + if(test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) + imxmci_cmd_done(host, stat); + if(host->data && (stat & STATUS_ERR_MASK)) + imxmci_data_done(host, stat); + } + + if(test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events)) { + stat |= MMC_STATUS; + if(imxmci_cpu_driven_data(host, &stat)){ + if(test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) + imxmci_cmd_done(host, stat); + atomic_clear_mask(IMXMCI_PEND_IRQ_m|IMXMCI_PEND_CPU_DATA_m, + &host->pending_events); + imxmci_data_done(host, stat); + } + } + } + + if(test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events) && + !test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) { + + stat = MMC_STATUS; + /* Same as above */ + stat |= host->status_reg; + + if(host->dma_dir == DMA_TO_DEVICE) { + data_dir_mask = STATUS_WRITE_OP_DONE; + } else { + data_dir_mask = STATUS_DATA_TRANS_DONE; + } + + imxmci_busy_wait_for_status(host, &stat, + data_dir_mask, + 50, "imxmci_tasklet_fnc data"); + + if(stat & data_dir_mask) { + clear_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events); + imxmci_data_done(host, stat); + } + } + + if(test_and_clear_bit(IMXMCI_PEND_CARD_XCHG_b, &host->pending_events)) { + + if(host->cmd) + imxmci_cmd_done(host, STATUS_TIME_OUT_RESP); + + if(host->data) + imxmci_data_done(host, STATUS_TIME_OUT_READ | + STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR); + + if(host->req) + imxmci_finish_request(host, host->req); + + mmc_detect_change(host->mmc, msecs_to_jiffies(100)); + + } +} + +static void imxmci_request(struct mmc_host *mmc, struct mmc_request *req) +{ + struct imxmci_host *host = mmc_priv(mmc); + unsigned int cmdat; + + WARN_ON(host->req != NULL); + + host->req = req; + + cmdat = 0; + + if (req->data) { + imxmci_setup_data(host, req->data); + + cmdat |= CMD_DAT_CONT_DATA_ENABLE; + + if (req->data->flags & MMC_DATA_WRITE) + cmdat |= CMD_DAT_CONT_WRITE; + + if (req->data->flags & MMC_DATA_STREAM) { + cmdat |= CMD_DAT_CONT_STREAM_BLOCK; + } + } + + imxmci_start_cmd(host, req->cmd, cmdat); +} + +#define CLK_RATE 19200000 + +static void imxmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct imxmci_host *host = mmc_priv(mmc); + int prescaler; + + dev_dbg(mmc_dev(host->mmc), "clock %u power %u vdd %u width %u\n", + ios->clock, ios->power_mode, ios->vdd, + (ios->bus_width==MMC_BUS_WIDTH_4)?4:1); + + if( ios->bus_width==MMC_BUS_WIDTH_4 ) { + host->actual_bus_width = MMC_BUS_WIDTH_4; + imx_gpio_mode(PB11_PF_SD_DAT3); + }else{ + host->actual_bus_width = MMC_BUS_WIDTH_1; + imx_gpio_mode(GPIO_PORTB | GPIO_IN | GPIO_PUEN | 11); + } + + if ( host->power_mode != ios->power_mode ) { + switch (ios->power_mode) { + case MMC_POWER_OFF: + break; + case MMC_POWER_UP: + set_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events); + break; + case MMC_POWER_ON: + break; + } + host->power_mode = ios->power_mode; + } + + if ( ios->clock ) { + unsigned int clk; + + /* The prescaler is 5 for PERCLK2 equal to 96MHz + * then 96MHz / 5 = 19.2 MHz + */ + clk=imx_get_perclk2(); + prescaler=(clk+(CLK_RATE*7)/8)/CLK_RATE; + switch(prescaler) { + case 0: + case 1: prescaler = 0; + break; + case 2: prescaler = 1; + break; + case 3: prescaler = 2; + break; + case 4: prescaler = 4; + break; + default: + case 5: prescaler = 5; + break; + } + + dev_dbg(mmc_dev(host->mmc), "PERCLK2 %d MHz -> prescaler %d\n", + clk, prescaler); + + for(clk=0; clk<8; clk++) { + int x; + x = CLK_RATE / (1<clock) + break; + } + + MMC_STR_STP_CLK |= STR_STP_CLK_ENABLE; /* enable controller */ + + imxmci_stop_clock(host); + MMC_CLK_RATE = (prescaler<<3) | clk; + imxmci_start_clock(host); + + dev_dbg(mmc_dev(host->mmc), "MMC_CLK_RATE: 0x%08x\n", MMC_CLK_RATE); + } else { + imxmci_stop_clock(host); + } +} + +static struct mmc_host_ops imxmci_ops = { + .request = imxmci_request, + .set_ios = imxmci_set_ios, +}; + +static struct resource *platform_device_resource(struct platform_device *dev, unsigned int mask, int nr) +{ + int i; + + for (i = 0; i < dev->num_resources; i++) + if (dev->resource[i].flags == mask && nr-- == 0) + return &dev->resource[i]; + return NULL; +} + +static int platform_device_irq(struct platform_device *dev, int nr) +{ + int i; + + for (i = 0; i < dev->num_resources; i++) + if (dev->resource[i].flags == IORESOURCE_IRQ && nr-- == 0) + return dev->resource[i].start; + return NO_IRQ; +} + +static void imxmci_check_status(unsigned long data) +{ + struct imxmci_host *host = (struct imxmci_host *)data; + + if( host->pdata->card_present() != host->present ) { + host->present ^= 1; + dev_info(mmc_dev(host->mmc), "card %s\n", + host->present ? "inserted" : "removed"); + + set_bit(IMXMCI_PEND_CARD_XCHG_b, &host->pending_events); + tasklet_schedule(&host->tasklet); + } + + if(test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events) || + test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) { + atomic_inc(&host->stuck_timeout); + if(atomic_read(&host->stuck_timeout) > 4) + tasklet_schedule(&host->tasklet); + } else { + atomic_set(&host->stuck_timeout, 0); + + } + + mod_timer(&host->timer, jiffies + (HZ>>1)); +} + +static int imxmci_probe(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct imxmci_host *host = NULL; + struct resource *r; + int ret = 0, irq; + + printk(KERN_INFO "i.MX mmc driver\n"); + + r = platform_device_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_device_irq(pdev, 0); + if (!r || irq == NO_IRQ) + return -ENXIO; + + r = request_mem_region(r->start, 0x100, "IMXMCI"); + if (!r) + return -EBUSY; + + mmc = mmc_alloc_host(sizeof(struct imxmci_host), &pdev->dev); + if (!mmc) { + ret = -ENOMEM; + goto out; + } + + mmc->ops = &imxmci_ops; + mmc->f_min = 150000; + mmc->f_max = CLK_RATE/2; + mmc->ocr_avail = MMC_VDD_32_33; + mmc->caps |= MMC_CAP_4_BIT_DATA; + + /* MMC core transfer sizes tunable parameters */ + mmc->max_hw_segs = 64; + mmc->max_phys_segs = 64; + mmc->max_sectors = 64; /* default 1 << (PAGE_CACHE_SHIFT - 9) */ + mmc->max_seg_size = 64*512; /* default PAGE_CACHE_SIZE */ + + host = mmc_priv(mmc); + host->mmc = mmc; + host->dma_allocated = 0; + host->pdata = pdev->dev.platform_data; + + spin_lock_init(&host->lock); + host->res = r; + host->irq = irq; + + imx_gpio_mode(PB8_PF_SD_DAT0); + imx_gpio_mode(PB9_PF_SD_DAT1); + imx_gpio_mode(PB10_PF_SD_DAT2); + /* Configured as GPIO with pull-up to ensure right MCC card mode */ + /* Switched to PB11_PF_SD_DAT3 if 4 bit bus is configured */ + imx_gpio_mode(GPIO_PORTB | GPIO_IN | GPIO_PUEN | 11); + /* imx_gpio_mode(PB11_PF_SD_DAT3); */ + imx_gpio_mode(PB12_PF_SD_CLK); + imx_gpio_mode(PB13_PF_SD_CMD); + + imxmci_softreset(); + + if ( MMC_REV_NO != 0x390 ) { + dev_err(mmc_dev(host->mmc), "wrong rev.no. 0x%08x. aborting.\n", + MMC_REV_NO); + goto out; + } + + MMC_READ_TO = 0x2db4; /* recommended in data sheet */ + + host->imask = IMXMCI_INT_MASK_DEFAULT; + MMC_INT_MASK = host->imask; + + + if(imx_dma_request_by_prio(&host->dma, DRIVER_NAME, DMA_PRIO_LOW)<0){ + dev_err(mmc_dev(host->mmc), "imx_dma_request_by_prio failed\n"); + ret = -EBUSY; + goto out; + } + host->dma_allocated=1; + imx_dma_setup_handlers(host->dma, imxmci_dma_irq, NULL, host); + + tasklet_init(&host->tasklet, imxmci_tasklet_fnc, (unsigned long)host); + host->status_reg=0; + host->pending_events=0; + + ret = request_irq(host->irq, imxmci_irq, 0, DRIVER_NAME, host); + if (ret) + goto out; + + host->present = host->pdata->card_present(); + init_timer(&host->timer); + host->timer.data = (unsigned long)host; + host->timer.function = imxmci_check_status; + add_timer(&host->timer); + mod_timer(&host->timer, jiffies + (HZ>>1)); + + platform_set_drvdata(pdev, mmc); + + mmc_add_host(mmc); + + return 0; + +out: + if (host) { + if(host->dma_allocated){ + imx_dma_free(host->dma); + host->dma_allocated=0; + } + } + if (mmc) + mmc_free_host(mmc); + release_resource(r); + return ret; +} + +static int imxmci_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + if (mmc) { + struct imxmci_host *host = mmc_priv(mmc); + + tasklet_disable(&host->tasklet); + + del_timer_sync(&host->timer); + mmc_remove_host(mmc); + + free_irq(host->irq, host); + if(host->dma_allocated){ + imx_dma_free(host->dma); + host->dma_allocated=0; + } + + tasklet_kill(&host->tasklet); + + release_resource(host->res); + + mmc_free_host(mmc); + } + return 0; +} + +#ifdef CONFIG_PM +static int imxmci_suspend(struct platform_device *dev, pm_message_t state) +{ + struct mmc_host *mmc = platform_get_drvdata(dev); + int ret = 0; + + if (mmc) + ret = mmc_suspend_host(mmc, state); + + return ret; +} + +static int imxmci_resume(struct platform_device *dev) +{ + struct mmc_host *mmc = platform_get_drvdata(dev); + struct imxmci_host *host; + int ret = 0; + + if (mmc) { + host = mmc_priv(mmc); + if(host) + set_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events); + ret = mmc_resume_host(mmc); + } + + return ret; +} +#else +#define imxmci_suspend NULL +#define imxmci_resume NULL +#endif /* CONFIG_PM */ + +static struct platform_driver imxmci_driver = { + .probe = imxmci_probe, + .remove = imxmci_remove, + .suspend = imxmci_suspend, + .resume = imxmci_resume, + .driver = { + .name = DRIVER_NAME, + } +}; + +static int __init imxmci_init(void) +{ + return platform_driver_register(&imxmci_driver); +} + +static void __exit imxmci_exit(void) +{ + platform_driver_unregister(&imxmci_driver); +} + +module_init(imxmci_init); +module_exit(imxmci_exit); + +MODULE_DESCRIPTION("i.MX Multimedia Card Interface Driver"); +MODULE_AUTHOR("Sascha Hauer, Pengutronix"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/imxmmc.h b/drivers/mmc/imxmmc.h new file mode 100644 index 000000000000..e5339e334dbb --- /dev/null +++ b/drivers/mmc/imxmmc.h @@ -0,0 +1,67 @@ + +# define __REG16(x) (*((volatile u16 *)IO_ADDRESS(x))) + +#define MMC_STR_STP_CLK __REG16(IMX_MMC_BASE + 0x00) +#define MMC_STATUS __REG16(IMX_MMC_BASE + 0x04) +#define MMC_CLK_RATE __REG16(IMX_MMC_BASE + 0x08) +#define MMC_CMD_DAT_CONT __REG16(IMX_MMC_BASE + 0x0C) +#define MMC_RES_TO __REG16(IMX_MMC_BASE + 0x10) +#define MMC_READ_TO __REG16(IMX_MMC_BASE + 0x14) +#define MMC_BLK_LEN __REG16(IMX_MMC_BASE + 0x18) +#define MMC_NOB __REG16(IMX_MMC_BASE + 0x1C) +#define MMC_REV_NO __REG16(IMX_MMC_BASE + 0x20) +#define MMC_INT_MASK __REG16(IMX_MMC_BASE + 0x24) +#define MMC_CMD __REG16(IMX_MMC_BASE + 0x28) +#define MMC_ARGH __REG16(IMX_MMC_BASE + 0x2C) +#define MMC_ARGL __REG16(IMX_MMC_BASE + 0x30) +#define MMC_RES_FIFO __REG16(IMX_MMC_BASE + 0x34) +#define MMC_BUFFER_ACCESS __REG16(IMX_MMC_BASE + 0x38) +#define MMC_BUFFER_ACCESS_OFS 0x38 + + +#define STR_STP_CLK_ENDIAN (1<<5) +#define STR_STP_CLK_RESET (1<<3) +#define STR_STP_CLK_ENABLE (1<<2) +#define STR_STP_CLK_START_CLK (1<<1) +#define STR_STP_CLK_STOP_CLK (1<<0) +#define STATUS_CARD_PRESENCE (1<<15) +#define STATUS_SDIO_INT_ACTIVE (1<<14) +#define STATUS_END_CMD_RESP (1<<13) +#define STATUS_WRITE_OP_DONE (1<<12) +#define STATUS_DATA_TRANS_DONE (1<<11) +#define STATUS_WR_CRC_ERROR_CODE_MASK (3<<10) +#define STATUS_CARD_BUS_CLK_RUN (1<<8) +#define STATUS_APPL_BUFF_FF (1<<7) +#define STATUS_APPL_BUFF_FE (1<<6) +#define STATUS_RESP_CRC_ERR (1<<5) +#define STATUS_CRC_READ_ERR (1<<3) +#define STATUS_CRC_WRITE_ERR (1<<2) +#define STATUS_TIME_OUT_RESP (1<<1) +#define STATUS_TIME_OUT_READ (1<<0) +#define STATUS_ERR_MASK 0x2f +#define CLK_RATE_PRESCALER(x) ((x) & 0x7) +#define CLK_RATE_CLK_RATE(x) (((x) & 0x7) << 3) +#define CMD_DAT_CONT_CMD_RESP_LONG_OFF (1<<12) +#define CMD_DAT_CONT_STOP_READWAIT (1<<11) +#define CMD_DAT_CONT_START_READWAIT (1<<10) +#define CMD_DAT_CONT_BUS_WIDTH_1 (0<<8) +#define CMD_DAT_CONT_BUS_WIDTH_4 (2<<8) +#define CMD_DAT_CONT_INIT (1<<7) +#define CMD_DAT_CONT_BUSY (1<<6) +#define CMD_DAT_CONT_STREAM_BLOCK (1<<5) +#define CMD_DAT_CONT_WRITE (1<<4) +#define CMD_DAT_CONT_DATA_ENABLE (1<<3) +#define CMD_DAT_CONT_RESPONSE_FORMAT_R1 (1) +#define CMD_DAT_CONT_RESPONSE_FORMAT_R2 (2) +#define CMD_DAT_CONT_RESPONSE_FORMAT_R3 (3) +#define CMD_DAT_CONT_RESPONSE_FORMAT_R4 (4) +#define CMD_DAT_CONT_RESPONSE_FORMAT_R5 (5) +#define CMD_DAT_CONT_RESPONSE_FORMAT_R6 (6) +#define INT_MASK_AUTO_CARD_DETECT (1<<6) +#define INT_MASK_DAT0_EN (1<<5) +#define INT_MASK_SDIO (1<<4) +#define INT_MASK_BUF_READY (1<<3) +#define INT_MASK_END_CMD_RES (1<<2) +#define INT_MASK_WRITE_OP_DONE (1<<1) +#define INT_MASK_DATA_TRAN (1<<0) +#define INT_ALL (0x7f) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 1888060c5e0c..da6ddd910fc5 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -27,12 +27,6 @@ #include "mmc.h" -#ifdef CONFIG_MMC_DEBUG -#define DBG(x...) printk(KERN_DEBUG x) -#else -#define DBG(x...) do { } while (0) -#endif - #define CMD_RETRIES 3 /* @@ -77,8 +71,9 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) { struct mmc_command *cmd = mrq->cmd; int err = mrq->cmd->error; - DBG("MMC: req done (%02x): %d: %08x %08x %08x %08x\n", cmd->opcode, - err, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); + pr_debug("MMC: req done (%02x): %d: %08x %08x %08x %08x\n", + cmd->opcode, err, cmd->resp[0], cmd->resp[1], + cmd->resp[2], cmd->resp[3]); if (err && cmd->retries) { cmd->retries--; @@ -102,8 +97,8 @@ EXPORT_SYMBOL(mmc_request_done); void mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) { - DBG("MMC: starting cmd %02x arg %08x flags %08x\n", - mrq->cmd->opcode, mrq->cmd->arg, mrq->cmd->flags); + pr_debug("MMC: starting cmd %02x arg %08x flags %08x\n", + mrq->cmd->opcode, mrq->cmd->arg, mrq->cmd->flags); WARN_ON(host->card_busy == NULL); @@ -976,8 +971,8 @@ static unsigned int mmc_calculate_clock(struct mmc_host *host) if (!mmc_card_dead(card) && max_dtr > card->csd.max_dtr) max_dtr = card->csd.max_dtr; - DBG("MMC: selected %d.%03dMHz transfer rate\n", - max_dtr / 1000000, (max_dtr / 1000) % 1000); + pr_debug("MMC: selected %d.%03dMHz transfer rate\n", + max_dtr / 1000000, (max_dtr / 1000) % 1000); return max_dtr; } diff --git a/drivers/mmc/mmci.c b/drivers/mmc/mmci.c index 9fef29d978b5..df7e861e2fc7 100644 --- a/drivers/mmc/mmci.c +++ b/drivers/mmc/mmci.c @@ -33,12 +33,8 @@ #define DRIVER_NAME "mmci-pl18x" -#ifdef CONFIG_MMC_DEBUG #define DBG(host,fmt,args...) \ pr_debug("%s: %s: " fmt, mmc_hostname(host->mmc), __func__ , args) -#else -#define DBG(host,fmt,args...) do { } while (0) -#endif static unsigned int fmax = 515633; diff --git a/drivers/mmc/omap.c b/drivers/mmc/omap.c new file mode 100644 index 000000000000..becb3c68c34d --- /dev/null +++ b/drivers/mmc/omap.c @@ -0,0 +1,1226 @@ +/* + * linux/drivers/media/mmc/omap.c + * + * Copyright (C) 2004 Nokia Corporation + * Written by Tuukka Tikkanen and Juha Yrjölä + * Misc hacks here and there by Tony Lindgren + * Other hacks (DMA, SD, etc) by David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "omap.h" + +#define DRIVER_NAME "mmci-omap" +#define RSP_TYPE(x) ((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE)) + +/* Specifies how often in millisecs to poll for card status changes + * when the cover switch is open */ +#define OMAP_MMC_SWITCH_POLL_DELAY 500 + +static int mmc_omap_enable_poll = 1; + +struct mmc_omap_host { + int initialized; + int suspended; + struct mmc_request * mrq; + struct mmc_command * cmd; + struct mmc_data * data; + struct mmc_host * mmc; + struct device * dev; + unsigned char id; /* 16xx chips have 2 MMC blocks */ + struct clk * iclk; + struct clk * fclk; + void __iomem *base; + int irq; + unsigned char bus_mode; + unsigned char hw_bus_mode; + + unsigned int sg_len; + int sg_idx; + u16 * buffer; + u32 buffer_bytes_left; + u32 total_bytes_left; + + unsigned use_dma:1; + unsigned brs_received:1, dma_done:1; + unsigned dma_is_read:1; + unsigned dma_in_use:1; + int dma_ch; + spinlock_t dma_lock; + struct timer_list dma_timer; + unsigned dma_len; + + short power_pin; + short wp_pin; + + int switch_pin; + struct work_struct switch_work; + struct timer_list switch_timer; + int switch_last_state; +}; + +static inline int +mmc_omap_cover_is_open(struct mmc_omap_host *host) +{ + if (host->switch_pin < 0) + return 0; + return omap_get_gpio_datain(host->switch_pin); +} + +static ssize_t +mmc_omap_show_cover_switch(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mmc_omap_host *host = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", mmc_omap_cover_is_open(host) ? "open" : + "closed"); +} + +static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL); + +static ssize_t +mmc_omap_show_enable_poll(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", mmc_omap_enable_poll); +} + +static ssize_t +mmc_omap_store_enable_poll(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t size) +{ + int enable_poll; + + if (sscanf(buf, "%10d", &enable_poll) != 1) + return -EINVAL; + + if (enable_poll != mmc_omap_enable_poll) { + struct mmc_omap_host *host = dev_get_drvdata(dev); + + mmc_omap_enable_poll = enable_poll; + if (enable_poll && host->switch_pin >= 0) + schedule_work(&host->switch_work); + } + return size; +} + +static DEVICE_ATTR(enable_poll, 0664, + mmc_omap_show_enable_poll, mmc_omap_store_enable_poll); + +static void +mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) +{ + u32 cmdreg; + u32 resptype; + u32 cmdtype; + + host->cmd = cmd; + + resptype = 0; + cmdtype = 0; + + /* Our hardware needs to know exact type */ + switch (RSP_TYPE(mmc_resp_type(cmd))) { + case RSP_TYPE(MMC_RSP_R1): + /* resp 1, resp 1b */ + resptype = 1; + break; + case RSP_TYPE(MMC_RSP_R2): + resptype = 2; + break; + case RSP_TYPE(MMC_RSP_R3): + resptype = 3; + break; + default: + break; + } + + if (mmc_cmd_type(cmd) == MMC_CMD_ADTC) { + cmdtype = OMAP_MMC_CMDTYPE_ADTC; + } else if (mmc_cmd_type(cmd) == MMC_CMD_BC) { + cmdtype = OMAP_MMC_CMDTYPE_BC; + } else if (mmc_cmd_type(cmd) == MMC_CMD_BCR) { + cmdtype = OMAP_MMC_CMDTYPE_BCR; + } else { + cmdtype = OMAP_MMC_CMDTYPE_AC; + } + + cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12); + + if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) + cmdreg |= 1 << 6; + + if (cmd->flags & MMC_RSP_BUSY) + cmdreg |= 1 << 11; + + if (host->data && !(host->data->flags & MMC_DATA_WRITE)) + cmdreg |= 1 << 15; + + clk_enable(host->fclk); + + OMAP_MMC_WRITE(host->base, CTO, 200); + OMAP_MMC_WRITE(host->base, ARGL, cmd->arg & 0xffff); + OMAP_MMC_WRITE(host->base, ARGH, cmd->arg >> 16); + OMAP_MMC_WRITE(host->base, IE, + OMAP_MMC_STAT_A_EMPTY | OMAP_MMC_STAT_A_FULL | + OMAP_MMC_STAT_CMD_CRC | OMAP_MMC_STAT_CMD_TOUT | + OMAP_MMC_STAT_DATA_CRC | OMAP_MMC_STAT_DATA_TOUT | + OMAP_MMC_STAT_END_OF_CMD | OMAP_MMC_STAT_CARD_ERR | + OMAP_MMC_STAT_END_OF_DATA); + OMAP_MMC_WRITE(host->base, CMD, cmdreg); +} + +static void +mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) +{ + if (host->dma_in_use) { + enum dma_data_direction dma_data_dir; + + BUG_ON(host->dma_ch < 0); + if (data->error != MMC_ERR_NONE) + omap_stop_dma(host->dma_ch); + /* Release DMA channel lazily */ + mod_timer(&host->dma_timer, jiffies + HZ); + if (data->flags & MMC_DATA_WRITE) + dma_data_dir = DMA_TO_DEVICE; + else + dma_data_dir = DMA_FROM_DEVICE; + dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len, + dma_data_dir); + } + host->data = NULL; + host->sg_len = 0; + clk_disable(host->fclk); + + /* NOTE: MMC layer will sometimes poll-wait CMD13 next, issuing + * dozens of requests until the card finishes writing data. + * It'd be cheaper to just wait till an EOFB interrupt arrives... + */ + + if (!data->stop) { + host->mrq = NULL; + mmc_request_done(host->mmc, data->mrq); + return; + } + + mmc_omap_start_command(host, data->stop); +} + +static void +mmc_omap_end_of_data(struct mmc_omap_host *host, struct mmc_data *data) +{ + unsigned long flags; + int done; + + if (!host->dma_in_use) { + mmc_omap_xfer_done(host, data); + return; + } + done = 0; + spin_lock_irqsave(&host->dma_lock, flags); + if (host->dma_done) + done = 1; + else + host->brs_received = 1; + spin_unlock_irqrestore(&host->dma_lock, flags); + if (done) + mmc_omap_xfer_done(host, data); +} + +static void +mmc_omap_dma_timer(unsigned long data) +{ + struct mmc_omap_host *host = (struct mmc_omap_host *) data; + + BUG_ON(host->dma_ch < 0); + omap_free_dma(host->dma_ch); + host->dma_ch = -1; +} + +static void +mmc_omap_dma_done(struct mmc_omap_host *host, struct mmc_data *data) +{ + unsigned long flags; + int done; + + done = 0; + spin_lock_irqsave(&host->dma_lock, flags); + if (host->brs_received) + done = 1; + else + host->dma_done = 1; + spin_unlock_irqrestore(&host->dma_lock, flags); + if (done) + mmc_omap_xfer_done(host, data); +} + +static void +mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd) +{ + host->cmd = NULL; + + if (cmd->flags & MMC_RSP_PRESENT) { + if (cmd->flags & MMC_RSP_136) { + /* response type 2 */ + cmd->resp[3] = + OMAP_MMC_READ(host->base, RSP0) | + (OMAP_MMC_READ(host->base, RSP1) << 16); + cmd->resp[2] = + OMAP_MMC_READ(host->base, RSP2) | + (OMAP_MMC_READ(host->base, RSP3) << 16); + cmd->resp[1] = + OMAP_MMC_READ(host->base, RSP4) | + (OMAP_MMC_READ(host->base, RSP5) << 16); + cmd->resp[0] = + OMAP_MMC_READ(host->base, RSP6) | + (OMAP_MMC_READ(host->base, RSP7) << 16); + } else { + /* response types 1, 1b, 3, 4, 5, 6 */ + cmd->resp[0] = + OMAP_MMC_READ(host->base, RSP6) | + (OMAP_MMC_READ(host->base, RSP7) << 16); + } + } + + if (host->data == NULL || cmd->error != MMC_ERR_NONE) { + host->mrq = NULL; + clk_disable(host->fclk); + mmc_request_done(host->mmc, cmd->mrq); + } +} + +/* PIO only */ +static void +mmc_omap_sg_to_buf(struct mmc_omap_host *host) +{ + struct scatterlist *sg; + + sg = host->data->sg + host->sg_idx; + host->buffer_bytes_left = sg->length; + host->buffer = page_address(sg->page) + sg->offset; + if (host->buffer_bytes_left > host->total_bytes_left) + host->buffer_bytes_left = host->total_bytes_left; +} + +/* PIO only */ +static void +mmc_omap_xfer_data(struct mmc_omap_host *host, int write) +{ + int n; + void __iomem *reg; + u16 *p; + + if (host->buffer_bytes_left == 0) { + host->sg_idx++; + BUG_ON(host->sg_idx == host->sg_len); + mmc_omap_sg_to_buf(host); + } + n = 64; + if (n > host->buffer_bytes_left) + n = host->buffer_bytes_left; + host->buffer_bytes_left -= n; + host->total_bytes_left -= n; + host->data->bytes_xfered += n; + + if (write) { + __raw_writesw(host->base + OMAP_MMC_REG_DATA, host->buffer, n); + } else { + __raw_readsw(host->base + OMAP_MMC_REG_DATA, host->buffer, n); + } +} + +static inline void mmc_omap_report_irq(u16 status) +{ + static const char *mmc_omap_status_bits[] = { + "EOC", "CD", "CB", "BRS", "EOFB", "DTO", "DCRC", "CTO", + "CCRC", "CRW", "AF", "AE", "OCRB", "CIRQ", "CERR" + }; + int i, c = 0; + + for (i = 0; i < ARRAY_SIZE(mmc_omap_status_bits); i++) + if (status & (1 << i)) { + if (c) + printk(" "); + printk("%s", mmc_omap_status_bits[i]); + c++; + } +} + +static irqreturn_t mmc_omap_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + struct mmc_omap_host * host = (struct mmc_omap_host *)dev_id; + u16 status; + int end_command; + int end_transfer; + int transfer_error; + + if (host->cmd == NULL && host->data == NULL) { + status = OMAP_MMC_READ(host->base, STAT); + dev_info(mmc_dev(host->mmc),"spurious irq 0x%04x\n", status); + if (status != 0) { + OMAP_MMC_WRITE(host->base, STAT, status); + OMAP_MMC_WRITE(host->base, IE, 0); + } + return IRQ_HANDLED; + } + + end_command = 0; + end_transfer = 0; + transfer_error = 0; + + while ((status = OMAP_MMC_READ(host->base, STAT)) != 0) { + OMAP_MMC_WRITE(host->base, STAT, status); +#ifdef CONFIG_MMC_DEBUG + dev_dbg(mmc_dev(host->mmc), "MMC IRQ %04x (CMD %d): ", + status, host->cmd != NULL ? host->cmd->opcode : -1); + mmc_omap_report_irq(status); + printk("\n"); +#endif + if (host->total_bytes_left) { + if ((status & OMAP_MMC_STAT_A_FULL) || + (status & OMAP_MMC_STAT_END_OF_DATA)) + mmc_omap_xfer_data(host, 0); + if (status & OMAP_MMC_STAT_A_EMPTY) + mmc_omap_xfer_data(host, 1); + } + + if (status & OMAP_MMC_STAT_END_OF_DATA) { + end_transfer = 1; + } + + if (status & OMAP_MMC_STAT_DATA_TOUT) { + dev_dbg(mmc_dev(host->mmc), "data timeout\n"); + if (host->data) { + host->data->error |= MMC_ERR_TIMEOUT; + transfer_error = 1; + } + } + + if (status & OMAP_MMC_STAT_DATA_CRC) { + if (host->data) { + host->data->error |= MMC_ERR_BADCRC; + dev_dbg(mmc_dev(host->mmc), + "data CRC error, bytes left %d\n", + host->total_bytes_left); + transfer_error = 1; + } else { + dev_dbg(mmc_dev(host->mmc), "data CRC error\n"); + } + } + + if (status & OMAP_MMC_STAT_CMD_TOUT) { + /* Timeouts are routine with some commands */ + if (host->cmd) { + if (host->cmd->opcode != MMC_ALL_SEND_CID && + host->cmd->opcode != + MMC_SEND_OP_COND && + host->cmd->opcode != + MMC_APP_CMD && + !mmc_omap_cover_is_open(host)) + dev_err(mmc_dev(host->mmc), + "command timeout, CMD %d\n", + host->cmd->opcode); + host->cmd->error = MMC_ERR_TIMEOUT; + end_command = 1; + } + } + + if (status & OMAP_MMC_STAT_CMD_CRC) { + if (host->cmd) { + dev_err(mmc_dev(host->mmc), + "command CRC error (CMD%d, arg 0x%08x)\n", + host->cmd->opcode, host->cmd->arg); + host->cmd->error = MMC_ERR_BADCRC; + end_command = 1; + } else + dev_err(mmc_dev(host->mmc), + "command CRC error without cmd?\n"); + } + + if (status & OMAP_MMC_STAT_CARD_ERR) { + if (host->cmd && host->cmd->opcode == MMC_STOP_TRANSMISSION) { + u32 response = OMAP_MMC_READ(host->base, RSP6) + | (OMAP_MMC_READ(host->base, RSP7) << 16); + /* STOP sometimes sets must-ignore bits */ + if (!(response & (R1_CC_ERROR + | R1_ILLEGAL_COMMAND + | R1_COM_CRC_ERROR))) { + end_command = 1; + continue; + } + } + + dev_dbg(mmc_dev(host->mmc), "card status error (CMD%d)\n", + host->cmd->opcode); + if (host->cmd) { + host->cmd->error = MMC_ERR_FAILED; + end_command = 1; + } + if (host->data) { + host->data->error = MMC_ERR_FAILED; + transfer_error = 1; + } + } + + /* + * NOTE: On 1610 the END_OF_CMD may come too early when + * starting a write + */ + if ((status & OMAP_MMC_STAT_END_OF_CMD) && + (!(status & OMAP_MMC_STAT_A_EMPTY))) { + end_command = 1; + } + } + + if (end_command) { + mmc_omap_cmd_done(host, host->cmd); + } + if (transfer_error) + mmc_omap_xfer_done(host, host->data); + else if (end_transfer) + mmc_omap_end_of_data(host, host->data); + + return IRQ_HANDLED; +} + +static irqreturn_t mmc_omap_switch_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + struct mmc_omap_host *host = (struct mmc_omap_host *) dev_id; + + schedule_work(&host->switch_work); + + return IRQ_HANDLED; +} + +static void mmc_omap_switch_timer(unsigned long arg) +{ + struct mmc_omap_host *host = (struct mmc_omap_host *) arg; + + schedule_work(&host->switch_work); +} + +/* FIXME: Handle card insertion and removal properly. Maybe use a mask + * for MMC state? */ +static void mmc_omap_switch_callback(unsigned long data, u8 mmc_mask) +{ +} + +static void mmc_omap_switch_handler(void *data) +{ + struct mmc_omap_host *host = (struct mmc_omap_host *) data; + struct mmc_card *card; + static int complained = 0; + int cards = 0, cover_open; + + if (host->switch_pin == -1) + return; + cover_open = mmc_omap_cover_is_open(host); + if (cover_open != host->switch_last_state) { + kobject_uevent(&host->dev->kobj, KOBJ_CHANGE); + host->switch_last_state = cover_open; + } + mmc_detect_change(host->mmc, 0); + list_for_each_entry(card, &host->mmc->cards, node) { + if (mmc_card_present(card)) + cards++; + } + if (mmc_omap_cover_is_open(host)) { + if (!complained) { + dev_info(mmc_dev(host->mmc), "cover is open"); + complained = 1; + } + if (mmc_omap_enable_poll) + mod_timer(&host->switch_timer, jiffies + + msecs_to_jiffies(OMAP_MMC_SWITCH_POLL_DELAY)); + } else { + complained = 0; + } +} + +/* Prepare to transfer the next segment of a scatterlist */ +static void +mmc_omap_prepare_dma(struct mmc_omap_host *host, struct mmc_data *data) +{ + int dma_ch = host->dma_ch; + unsigned long data_addr; + u16 buf, frame; + u32 count; + struct scatterlist *sg = &data->sg[host->sg_idx]; + int src_port = 0; + int dst_port = 0; + int sync_dev = 0; + + data_addr = io_v2p((u32) host->base) + OMAP_MMC_REG_DATA; + frame = 1 << data->blksz_bits; + count = sg_dma_len(sg); + + if ((data->blocks == 1) && (count > (1 << data->blksz_bits))) + count = frame; + + host->dma_len = count; + + /* FIFO is 16x2 bytes on 15xx, and 32x2 bytes on 16xx and 24xx. + * Use 16 or 32 word frames when the blocksize is at least that large. + * Blocksize is usually 512 bytes; but not for some SD reads. + */ + if (cpu_is_omap15xx() && frame > 32) + frame = 32; + else if (frame > 64) + frame = 64; + count /= frame; + frame >>= 1; + + if (!(data->flags & MMC_DATA_WRITE)) { + buf = 0x800f | ((frame - 1) << 8); + + if (cpu_class_is_omap1()) { + src_port = OMAP_DMA_PORT_TIPB; + dst_port = OMAP_DMA_PORT_EMIFF; + } + if (cpu_is_omap24xx()) + sync_dev = OMAP24XX_DMA_MMC1_RX; + + omap_set_dma_src_params(dma_ch, src_port, + OMAP_DMA_AMODE_CONSTANT, + data_addr, 0, 0); + omap_set_dma_dest_params(dma_ch, dst_port, + OMAP_DMA_AMODE_POST_INC, + sg_dma_address(sg), 0, 0); + omap_set_dma_dest_data_pack(dma_ch, 1); + omap_set_dma_dest_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4); + } else { + buf = 0x0f80 | ((frame - 1) << 0); + + if (cpu_class_is_omap1()) { + src_port = OMAP_DMA_PORT_EMIFF; + dst_port = OMAP_DMA_PORT_TIPB; + } + if (cpu_is_omap24xx()) + sync_dev = OMAP24XX_DMA_MMC1_TX; + + omap_set_dma_dest_params(dma_ch, dst_port, + OMAP_DMA_AMODE_CONSTANT, + data_addr, 0, 0); + omap_set_dma_src_params(dma_ch, src_port, + OMAP_DMA_AMODE_POST_INC, + sg_dma_address(sg), 0, 0); + omap_set_dma_src_data_pack(dma_ch, 1); + omap_set_dma_src_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4); + } + + /* Max limit for DMA frame count is 0xffff */ + if (unlikely(count > 0xffff)) + BUG(); + + OMAP_MMC_WRITE(host->base, BUF, buf); + omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S16, + frame, count, OMAP_DMA_SYNC_FRAME, + sync_dev, 0); +} + +/* A scatterlist segment completed */ +static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data) +{ + struct mmc_omap_host *host = (struct mmc_omap_host *) data; + struct mmc_data *mmcdat = host->data; + + if (unlikely(host->dma_ch < 0)) { + dev_err(mmc_dev(host->mmc), "DMA callback while DMA not + enabled\n"); + return; + } + /* FIXME: We really should do something to _handle_ the errors */ + if (ch_status & OMAP_DMA_TOUT_IRQ) { + dev_err(mmc_dev(host->mmc),"DMA timeout\n"); + return; + } + if (ch_status & OMAP_DMA_DROP_IRQ) { + dev_err(mmc_dev(host->mmc), "DMA sync error\n"); + return; + } + if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) { + return; + } + mmcdat->bytes_xfered += host->dma_len; + host->sg_idx++; + if (host->sg_idx < host->sg_len) { + mmc_omap_prepare_dma(host, host->data); + omap_start_dma(host->dma_ch); + } else + mmc_omap_dma_done(host, host->data); +} + +static int mmc_omap_get_dma_channel(struct mmc_omap_host *host, struct mmc_data *data) +{ + const char *dev_name; + int sync_dev, dma_ch, is_read, r; + + is_read = !(data->flags & MMC_DATA_WRITE); + del_timer_sync(&host->dma_timer); + if (host->dma_ch >= 0) { + if (is_read == host->dma_is_read) + return 0; + omap_free_dma(host->dma_ch); + host->dma_ch = -1; + } + + if (is_read) { + if (host->id == 1) { + sync_dev = OMAP_DMA_MMC_RX; + dev_name = "MMC1 read"; + } else { + sync_dev = OMAP_DMA_MMC2_RX; + dev_name = "MMC2 read"; + } + } else { + if (host->id == 1) { + sync_dev = OMAP_DMA_MMC_TX; + dev_name = "MMC1 write"; + } else { + sync_dev = OMAP_DMA_MMC2_TX; + dev_name = "MMC2 write"; + } + } + r = omap_request_dma(sync_dev, dev_name, mmc_omap_dma_cb, + host, &dma_ch); + if (r != 0) { + dev_dbg(mmc_dev(host->mmc), "omap_request_dma() failed with %d\n", r); + return r; + } + host->dma_ch = dma_ch; + host->dma_is_read = is_read; + + return 0; +} + +static inline void set_cmd_timeout(struct mmc_omap_host *host, struct mmc_request *req) +{ + u16 reg; + + reg = OMAP_MMC_READ(host->base, SDIO); + reg &= ~(1 << 5); + OMAP_MMC_WRITE(host->base, SDIO, reg); + /* Set maximum timeout */ + OMAP_MMC_WRITE(host->base, CTO, 0xff); +} + +static inline void set_data_timeout(struct mmc_omap_host *host, struct mmc_request *req) +{ + int timeout; + u16 reg; + + /* Convert ns to clock cycles by assuming 20MHz frequency + * 1 cycle at 20MHz = 500 ns + */ + timeout = req->data->timeout_clks + req->data->timeout_ns / 500; + + /* Check if we need to use timeout multiplier register */ + reg = OMAP_MMC_READ(host->base, SDIO); + if (timeout > 0xffff) { + reg |= (1 << 5); + timeout /= 1024; + } else + reg &= ~(1 << 5); + OMAP_MMC_WRITE(host->base, SDIO, reg); + OMAP_MMC_WRITE(host->base, DTO, timeout); +} + +static void +mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) +{ + struct mmc_data *data = req->data; + int i, use_dma, block_size; + unsigned sg_len; + + host->data = data; + if (data == NULL) { + OMAP_MMC_WRITE(host->base, BLEN, 0); + OMAP_MMC_WRITE(host->base, NBLK, 0); + OMAP_MMC_WRITE(host->base, BUF, 0); + host->dma_in_use = 0; + set_cmd_timeout(host, req); + return; + } + + + block_size = 1 << data->blksz_bits; + + OMAP_MMC_WRITE(host->base, NBLK, data->blocks - 1); + OMAP_MMC_WRITE(host->base, BLEN, block_size - 1); + set_data_timeout(host, req); + + /* cope with calling layer confusion; it issues "single + * block" writes using multi-block scatterlists. + */ + sg_len = (data->blocks == 1) ? 1 : data->sg_len; + + /* Only do DMA for entire blocks */ + use_dma = host->use_dma; + if (use_dma) { + for (i = 0; i < sg_len; i++) { + if ((data->sg[i].length % block_size) != 0) { + use_dma = 0; + break; + } + } + } + + host->sg_idx = 0; + if (use_dma) { + if (mmc_omap_get_dma_channel(host, data) == 0) { + enum dma_data_direction dma_data_dir; + + if (data->flags & MMC_DATA_WRITE) + dma_data_dir = DMA_TO_DEVICE; + else + dma_data_dir = DMA_FROM_DEVICE; + + host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg, + sg_len, dma_data_dir); + host->total_bytes_left = 0; + mmc_omap_prepare_dma(host, req->data); + host->brs_received = 0; + host->dma_done = 0; + host->dma_in_use = 1; + } else + use_dma = 0; + } + + /* Revert to PIO? */ + if (!use_dma) { + OMAP_MMC_WRITE(host->base, BUF, 0x1f1f); + host->total_bytes_left = data->blocks * block_size; + host->sg_len = sg_len; + mmc_omap_sg_to_buf(host); + host->dma_in_use = 0; + } +} + +static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req) +{ + struct mmc_omap_host *host = mmc_priv(mmc); + + WARN_ON(host->mrq != NULL); + + host->mrq = req; + + /* only touch fifo AFTER the controller readies it */ + mmc_omap_prepare_data(host, req); + mmc_omap_start_command(host, req->cmd); + if (host->dma_in_use) + omap_start_dma(host->dma_ch); +} + +static void innovator_fpga_socket_power(int on) +{ +#if defined(CONFIG_MACH_OMAP_INNOVATOR) && defined(CONFIG_ARCH_OMAP15XX) + + if (on) { + fpga_write(fpga_read(OMAP1510_FPGA_POWER) | (1 << 3), + OMAP1510_FPGA_POWER); + } else { + fpga_write(fpga_read(OMAP1510_FPGA_POWER) & ~(1 << 3), + OMAP1510_FPGA_POWER); + } +#endif +} + +/* + * Turn the socket power on/off. Innovator uses FPGA, most boards + * probably use GPIO. + */ +static void mmc_omap_power(struct mmc_omap_host *host, int on) +{ + if (on) { + if (machine_is_omap_innovator()) + innovator_fpga_socket_power(1); + else if (machine_is_omap_h2()) + tps65010_set_gpio_out_value(GPIO3, HIGH); + else if (machine_is_omap_h3()) + /* GPIO 4 of TPS65010 sends SD_EN signal */ + tps65010_set_gpio_out_value(GPIO4, HIGH); + else if (cpu_is_omap24xx()) { + u16 reg = OMAP_MMC_READ(host->base, CON); + OMAP_MMC_WRITE(host->base, CON, reg | (1 << 11)); + } else + if (host->power_pin >= 0) + omap_set_gpio_dataout(host->power_pin, 1); + } else { + if (machine_is_omap_innovator()) + innovator_fpga_socket_power(0); + else if (machine_is_omap_h2()) + tps65010_set_gpio_out_value(GPIO3, LOW); + else if (machine_is_omap_h3()) + tps65010_set_gpio_out_value(GPIO4, LOW); + else if (cpu_is_omap24xx()) { + u16 reg = OMAP_MMC_READ(host->base, CON); + OMAP_MMC_WRITE(host->base, CON, reg & ~(1 << 11)); + } else + if (host->power_pin >= 0) + omap_set_gpio_dataout(host->power_pin, 0); + } +} + +static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct mmc_omap_host *host = mmc_priv(mmc); + int dsor; + int realclock, i; + + realclock = ios->clock; + + if (ios->clock == 0) + dsor = 0; + else { + int func_clk_rate = clk_get_rate(host->fclk); + + dsor = func_clk_rate / realclock; + if (dsor < 1) + dsor = 1; + + if (func_clk_rate / dsor > realclock) + dsor++; + + if (dsor > 250) + dsor = 250; + dsor++; + + if (ios->bus_width == MMC_BUS_WIDTH_4) + dsor |= 1 << 15; + } + + switch (ios->power_mode) { + case MMC_POWER_OFF: + mmc_omap_power(host, 0); + break; + case MMC_POWER_UP: + case MMC_POWER_ON: + mmc_omap_power(host, 1); + dsor |= 1<<11; + break; + } + + host->bus_mode = ios->bus_mode; + host->hw_bus_mode = host->bus_mode; + + clk_enable(host->fclk); + + /* On insanely high arm_per frequencies something sometimes + * goes somehow out of sync, and the POW bit is not being set, + * which results in the while loop below getting stuck. + * Writing to the CON register twice seems to do the trick. */ + for (i = 0; i < 2; i++) + OMAP_MMC_WRITE(host->base, CON, dsor); + if (ios->power_mode == MMC_POWER_UP) { + /* Send clock cycles, poll completion */ + OMAP_MMC_WRITE(host->base, IE, 0); + OMAP_MMC_WRITE(host->base, STAT, 0xffff); + OMAP_MMC_WRITE(host->base, CMD, 1<<7); + while (0 == (OMAP_MMC_READ(host->base, STAT) & 1)); + OMAP_MMC_WRITE(host->base, STAT, 1); + } + clk_disable(host->fclk); +} + +static int mmc_omap_get_ro(struct mmc_host *mmc) +{ + struct mmc_omap_host *host = mmc_priv(mmc); + + return host->wp_pin && omap_get_gpio_datain(host->wp_pin); +} + +static struct mmc_host_ops mmc_omap_ops = { + .request = mmc_omap_request, + .set_ios = mmc_omap_set_ios, + .get_ro = mmc_omap_get_ro, +}; + +static int __init mmc_omap_probe(struct platform_device *pdev) +{ + struct omap_mmc_conf *minfo = pdev->dev.platform_data; + struct mmc_host *mmc; + struct mmc_omap_host *host = NULL; + int ret = 0; + + if (platform_get_resource(pdev, IORESOURCE_MEM, 0) || + platform_get_irq(pdev, IORESOURCE_IRQ, 0)) { + dev_err(&pdev->dev, "mmc_omap_probe: invalid resource type\n"); + return -ENODEV; + } + + if (!request_mem_region(pdev->resource[0].start, + pdev->resource[0].end - pdev->resource[0].start + 1, + pdev->name)) { + dev_dbg(&pdev->dev, "request_mem_region failed\n"); + return -EBUSY; + } + + mmc = mmc_alloc_host(sizeof(struct mmc_omap_host), &pdev->dev); + if (!mmc) { + ret = -ENOMEM; + goto out; + } + + host = mmc_priv(mmc); + host->mmc = mmc; + + spin_lock_init(&host->dma_lock); + init_timer(&host->dma_timer); + host->dma_timer.function = mmc_omap_dma_timer; + host->dma_timer.data = (unsigned long) host; + + host->id = pdev->id; + + if (cpu_is_omap24xx()) { + host->iclk = clk_get(&pdev->dev, "mmc_ick"); + if (IS_ERR(host->iclk)) + goto out; + clk_enable(host->iclk); + } + + if (!cpu_is_omap24xx()) + host->fclk = clk_get(&pdev->dev, "mmc_ck"); + else + host->fclk = clk_get(&pdev->dev, "mmc_fck"); + + if (IS_ERR(host->fclk)) { + ret = PTR_ERR(host->fclk); + goto out; + } + + /* REVISIT: + * Also, use minfo->cover to decide how to manage + * the card detect sensing. + */ + host->power_pin = minfo->power_pin; + host->switch_pin = minfo->switch_pin; + host->wp_pin = minfo->wp_pin; + host->use_dma = 1; + host->dma_ch = -1; + + host->irq = pdev->resource[1].start; + host->base = ioremap(pdev->res.start, SZ_4K); + if (!host->base) { + ret = -ENOMEM; + goto out; + } + + if (minfo->wire4) + mmc->caps |= MMC_CAP_4_BIT_DATA; + + mmc->ops = &mmc_omap_ops; + mmc->f_min = 400000; + mmc->f_max = 24000000; + mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34; + + /* Use scatterlist DMA to reduce per-transfer costs. + * NOTE max_seg_size assumption that small blocks aren't + * normally used (except e.g. for reading SD registers). + */ + mmc->max_phys_segs = 32; + mmc->max_hw_segs = 32; + mmc->max_sectors = 256; /* NBLK max 11-bits, OMAP also limited by DMA */ + mmc->max_seg_size = mmc->max_sectors * 512; + + if (host->power_pin >= 0) { + if ((ret = omap_request_gpio(host->power_pin)) != 0) { + dev_err(mmc_dev(host->mmc), "Unable to get GPIO + pin for MMC power\n"); + goto out; + } + omap_set_gpio_direction(host->power_pin, 0); + } + + ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host); + if (ret) + goto out; + + host->dev = &pdev->dev; + platform_set_drvdata(pdev, host); + + mmc_add_host(mmc); + + if (host->switch_pin >= 0) { + INIT_WORK(&host->switch_work, mmc_omap_switch_handler, host); + init_timer(&host->switch_timer); + host->switch_timer.function = mmc_omap_switch_timer; + host->switch_timer.data = (unsigned long) host; + if (omap_request_gpio(host->switch_pin) != 0) { + dev_warn(mmc_dev(host->mmc), "Unable to get GPIO pin for MMC cover switch\n"); + host->switch_pin = -1; + goto no_switch; + } + + omap_set_gpio_direction(host->switch_pin, 1); + ret = request_irq(OMAP_GPIO_IRQ(host->switch_pin), + mmc_omap_switch_irq, SA_TRIGGER_RISING, DRIVER_NAME, host); + if (ret) { + dev_warn(mmc_dev(host->mmc), "Unable to get IRQ for MMC cover switch\n"); + omap_free_gpio(host->switch_pin); + host->switch_pin = -1; + goto no_switch; + } + ret = device_create_file(&pdev->dev, &dev_attr_cover_switch); + if (ret == 0) { + ret = device_create_file(&pdev->dev, &dev_attr_enable_poll); + if (ret != 0) + device_remove_file(&pdev->dev, &dev_attr_cover_switch); + } + if (ret) { + dev_wan(mmc_dev(host->mmc), "Unable to create sysfs attributes\n"); + free_irq(OMAP_GPIO_IRQ(host->switch_pin), host); + omap_free_gpio(host->switch_pin); + host->switch_pin = -1; + goto no_switch; + } + if (mmc_omap_enable_poll && mmc_omap_cover_is_open(host)) + schedule_work(&host->switch_work); + } + +no_switch: + return 0; + +out: + /* FIXME: Free other resources too. */ + if (host) { + if (host->iclk && !IS_ERR(host->iclk)) + clk_put(host->iclk); + if (host->fclk && !IS_ERR(host->fclk)) + clk_put(host->fclk); + mmc_free_host(host->mmc); + } + return ret; +} + +static int mmc_omap_remove(struct platform_device *pdev) +{ + struct mmc_omap_host *host = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + if (host) { + mmc_remove_host(host->mmc); + free_irq(host->irq, host); + + if (host->power_pin >= 0) + omap_free_gpio(host->power_pin); + if (host->switch_pin >= 0) { + device_remove_file(&pdev->dev, &dev_attr_enable_poll); + device_remove_file(&pdev->dev, &dev_attr_cover_switch); + free_irq(OMAP_GPIO_IRQ(host->switch_pin), host); + omap_free_gpio(host->switch_pin); + host->switch_pin = -1; + del_timer_sync(&host->switch_timer); + flush_scheduled_work(); + } + if (host->iclk && !IS_ERR(host->iclk)) + clk_put(host->iclk); + if (host->fclk && !IS_ERR(host->fclk)) + clk_put(host->fclk); + mmc_free_host(host->mmc); + } + + release_mem_region(pdev->resource[0].start, + pdev->resource[0].end - pdev->resource[0].start + 1); + + return 0; +} + +#ifdef CONFIG_PM +static int mmc_omap_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + int ret = 0; + struct mmc_omap_host *host = platform_get_drvdata(pdev); + + if (host && host->suspended) + return 0; + + if (host) { + ret = mmc_suspend_host(host->mmc, mesg); + if (ret == 0) + host->suspended = 1; + } + return ret; +} + +static int mmc_omap_resume(struct platform_device *pdev) +{ + int ret = 0; + struct mmc_omap_host *host = platform_get_drvdata(pdev); + + if (host && !host->suspended) + return 0; + + if (host) { + ret = mmc_resume_host(host->mmc); + if (ret == 0) + host->suspended = 0; + } + + return ret; +} +#else +#define mmc_omap_suspend NULL +#define mmc_omap_resume NULL +#endif + +static struct platform_driver mmc_omap_driver = { + .probe = mmc_omap_probe, + .remove = mmc_omap_remove, + .suspend = mmc_omap_suspend, + .resume = mmc_omap_resume, + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init mmc_omap_init(void) +{ + return platform_driver_register(&mmc_omap_driver); +} + +static void __exit mmc_omap_exit(void) +{ + platform_driver_unregister(&mmc_omap_driver); +} + +module_init(mmc_omap_init); +module_exit(mmc_omap_exit); + +MODULE_DESCRIPTION("OMAP Multimedia Card driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS(DRIVER_NAME); +MODULE_AUTHOR("Juha Yrjölä"); diff --git a/drivers/mmc/omap.h b/drivers/mmc/omap.h new file mode 100644 index 000000000000..c954d355a5e3 --- /dev/null +++ b/drivers/mmc/omap.h @@ -0,0 +1,55 @@ +#ifndef DRIVERS_MEDIA_MMC_OMAP_H +#define DRIVERS_MEDIA_MMC_OMAP_H + +#define OMAP_MMC_REG_CMD 0x00 +#define OMAP_MMC_REG_ARGL 0x04 +#define OMAP_MMC_REG_ARGH 0x08 +#define OMAP_MMC_REG_CON 0x0c +#define OMAP_MMC_REG_STAT 0x10 +#define OMAP_MMC_REG_IE 0x14 +#define OMAP_MMC_REG_CTO 0x18 +#define OMAP_MMC_REG_DTO 0x1c +#define OMAP_MMC_REG_DATA 0x20 +#define OMAP_MMC_REG_BLEN 0x24 +#define OMAP_MMC_REG_NBLK 0x28 +#define OMAP_MMC_REG_BUF 0x2c +#define OMAP_MMC_REG_SDIO 0x34 +#define OMAP_MMC_REG_REV 0x3c +#define OMAP_MMC_REG_RSP0 0x40 +#define OMAP_MMC_REG_RSP1 0x44 +#define OMAP_MMC_REG_RSP2 0x48 +#define OMAP_MMC_REG_RSP3 0x4c +#define OMAP_MMC_REG_RSP4 0x50 +#define OMAP_MMC_REG_RSP5 0x54 +#define OMAP_MMC_REG_RSP6 0x58 +#define OMAP_MMC_REG_RSP7 0x5c +#define OMAP_MMC_REG_IOSR 0x60 +#define OMAP_MMC_REG_SYSC 0x64 +#define OMAP_MMC_REG_SYSS 0x68 + +#define OMAP_MMC_STAT_CARD_ERR (1 << 14) +#define OMAP_MMC_STAT_CARD_IRQ (1 << 13) +#define OMAP_MMC_STAT_OCR_BUSY (1 << 12) +#define OMAP_MMC_STAT_A_EMPTY (1 << 11) +#define OMAP_MMC_STAT_A_FULL (1 << 10) +#define OMAP_MMC_STAT_CMD_CRC (1 << 8) +#define OMAP_MMC_STAT_CMD_TOUT (1 << 7) +#define OMAP_MMC_STAT_DATA_CRC (1 << 6) +#define OMAP_MMC_STAT_DATA_TOUT (1 << 5) +#define OMAP_MMC_STAT_END_BUSY (1 << 4) +#define OMAP_MMC_STAT_END_OF_DATA (1 << 3) +#define OMAP_MMC_STAT_CARD_BUSY (1 << 2) +#define OMAP_MMC_STAT_END_OF_CMD (1 << 0) + +#define OMAP_MMC_READ(base, reg) __raw_readw((base) + OMAP_MMC_REG_##reg) +#define OMAP_MMC_WRITE(base, reg, val) __raw_writew((val), (base) + OMAP_MMC_REG_##reg) + +/* + * Command types + */ +#define OMAP_MMC_CMDTYPE_BC 0 +#define OMAP_MMC_CMDTYPE_BCR 1 +#define OMAP_MMC_CMDTYPE_AC 2 +#define OMAP_MMC_CMDTYPE_ADTC 3 + +#endif diff --git a/drivers/mmc/pxamci.c b/drivers/mmc/pxamci.c index c32fad1ce51c..eb9a8826e9b5 100644 --- a/drivers/mmc/pxamci.c +++ b/drivers/mmc/pxamci.c @@ -37,12 +37,6 @@ #include "pxamci.h" -#ifdef CONFIG_MMC_DEBUG -#define DBG(x...) printk(KERN_DEBUG x) -#else -#define DBG(x...) do { } while (0) -#endif - #define DRIVER_NAME "pxa2xx-mci" #define NR_SG 1 @@ -206,7 +200,7 @@ static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd, static void pxamci_finish_request(struct pxamci_host *host, struct mmc_request *mrq) { - DBG("PXAMCI: request done\n"); + pr_debug("PXAMCI: request done\n"); host->mrq = NULL; host->cmd = NULL; host->data = NULL; @@ -252,7 +246,7 @@ static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat) if ((cmd->resp[0] & 0x80000000) == 0) cmd->error = MMC_ERR_BADCRC; } else { - DBG("ignoring CRC from command %d - *risky*\n",cmd->opcode); + pr_debug("ignoring CRC from command %d - *risky*\n",cmd->opcode); } #else cmd->error = MMC_ERR_BADCRC; @@ -317,12 +311,12 @@ static irqreturn_t pxamci_irq(int irq, void *devid, struct pt_regs *regs) ireg = readl(host->base + MMC_I_REG); - DBG("PXAMCI: irq %08x\n", ireg); + pr_debug("PXAMCI: irq %08x\n", ireg); if (ireg) { unsigned stat = readl(host->base + MMC_STAT); - DBG("PXAMCI: stat %08x\n", stat); + pr_debug("PXAMCI: stat %08x\n", stat); if (ireg & END_CMD_RES) handled |= pxamci_cmd_done(host, stat); @@ -376,9 +370,9 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct pxamci_host *host = mmc_priv(mmc); - DBG("pxamci_set_ios: clock %u power %u vdd %u.%02u\n", - ios->clock, ios->power_mode, ios->vdd / 100, - ios->vdd % 100); + pr_debug("pxamci_set_ios: clock %u power %u vdd %u.%02u\n", + ios->clock, ios->power_mode, ios->vdd / 100, + ios->vdd % 100); if (ios->clock) { unsigned int clk = CLOCKRATE / ios->clock; @@ -405,8 +399,8 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) host->cmdat |= CMDAT_INIT; } - DBG("pxamci_set_ios: clkrt = %x cmdat = %x\n", - host->clkrt, host->cmdat); + pr_debug("pxamci_set_ios: clkrt = %x cmdat = %x\n", + host->clkrt, host->cmdat); } static struct mmc_host_ops pxamci_ops = { diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 8b811d94371c..bdbfca050029 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -31,12 +31,8 @@ #define BUGMAIL "" -#ifdef CONFIG_MMC_DEBUG #define DBG(f, x...) \ - printk(KERN_DEBUG DRIVER_NAME " [%s()]: " f, __func__,## x) -#else -#define DBG(f, x...) do { } while (0) -#endif + pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x) static const struct pci_device_id pci_ids[] __devinitdata = { /* handle any SD host controller */ diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c index 3be397d436fa..511f7b0b31d2 100644 --- a/drivers/mmc/wbsd.c +++ b/drivers/mmc/wbsd.c @@ -44,15 +44,10 @@ #define DRIVER_NAME "wbsd" #define DRIVER_VERSION "1.5" -#ifdef CONFIG_MMC_DEBUG #define DBG(x...) \ - printk(KERN_DEBUG DRIVER_NAME ": " x) + pr_debug(DRIVER_NAME ": " x) #define DBGF(f, x...) \ - printk(KERN_DEBUG DRIVER_NAME " [%s()]: " f, __func__ , ##x) -#else -#define DBG(x...) do { } while (0) -#define DBGF(x...) do { } while (0) -#endif + pr_debug(DRIVER_NAME " [%s()]: " f, __func__ , ##x) /* * Device resources diff --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig index 0f6bb2e625d8..a7ec5954caf5 100644 --- a/drivers/mtd/chips/Kconfig +++ b/drivers/mtd/chips/Kconfig @@ -200,27 +200,6 @@ config MTD_CFI_AMDSTD provides support for one of those command sets, used on chips including the AMD Am29LV320. -config MTD_CFI_AMDSTD_RETRY - int "Retry failed commands (erase/program)" - depends on MTD_CFI_AMDSTD - default "0" - help - Some chips, when attached to a shared bus, don't properly filter - bus traffic that is destined to other devices. This broken - behavior causes erase and program sequences to be aborted when - the sequences are mixed with traffic for other devices. - - SST49LF040 (and related) chips are know to be broken. - -config MTD_CFI_AMDSTD_RETRY_MAX - int "Max retries of failed commands (erase/program)" - depends on MTD_CFI_AMDSTD_RETRY - default "0" - help - If you have an SST49LF040 (or related chip) then this value should - be set to at least 1. This can also be adjusted at driver load - time with the retry_cmd_max module parameter. - config MTD_CFI_STAA tristate "Support for ST (Advanced Architecture) flash chips" depends on MTD_GEN_PROBE diff --git a/drivers/mtd/chips/amd_flash.c b/drivers/mtd/chips/amd_flash.c index fdb91b6f1d97..57115618c496 100644 --- a/drivers/mtd/chips/amd_flash.c +++ b/drivers/mtd/chips/amd_flash.c @@ -664,7 +664,7 @@ static struct mtd_info *amd_flash_probe(struct map_info *map) printk("%s: Probing for AMD compatible flash...\n", map->name); if ((table_pos[0] = probe_new_chip(mtd, 0, NULL, &temp, table, - sizeof(table)/sizeof(table[0]))) + ARRAY_SIZE(table))) == -1) { printk(KERN_WARNING "%s: Found no AMD compatible device at location zero\n", @@ -696,7 +696,7 @@ static struct mtd_info *amd_flash_probe(struct map_info *map) base += (1 << temp.chipshift)) { int numchips = temp.numchips; table_pos[numchips] = probe_new_chip(mtd, base, chips, - &temp, table, sizeof(table)/sizeof(table[0])); + &temp, table, ARRAY_SIZE(table)); } mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) * diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index edb306c03c0a..517ea33e7260 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -34,6 +34,7 @@ #define MANUFACTURER_MACRONIX 0x00C2 #define MANUFACTURER_NEC 0x0010 #define MANUFACTURER_PMC 0x009D +#define MANUFACTURER_SHARP 0x00b0 #define MANUFACTURER_SST 0x00BF #define MANUFACTURER_ST 0x0020 #define MANUFACTURER_TOSHIBA 0x0098 @@ -124,6 +125,9 @@ #define PM49FL004 0x006E #define PM49FL008 0x006A +/* Sharp */ +#define LH28F640BF 0x00b0 + /* ST - www.st.com */ #define M29W800DT 0x00D7 #define M29W800DB 0x005B @@ -1267,6 +1271,19 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO( 0x01000, 256 ) } + }, { + .mfr_id = MANUFACTURER_SHARP, + .dev_id = LH28F640BF, + .name = "LH28F640BF", + .uaddr = { + [0] = MTD_UADDR_UNNECESSARY, /* x8 */ + }, + .DevSize = SIZE_4MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions= 1, + .regions = { + ERASEINFO(0x40000,16), + } }, { .mfr_id = MANUFACTURER_SST, .dev_id = SST39LF512, @@ -2035,7 +2052,7 @@ static int jedec_probe_chip(struct map_info *map, __u32 base, DEBUG(MTD_DEBUG_LEVEL3, "Search for id:(%02x %02x) interleave(%d) type(%d)\n", cfi->mfr, cfi->id, cfi_interleave(cfi), cfi->device_type); - for (i=0; i\n"); diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c index 6b8bb2e4dcfd..a7a7bfe33879 100644 --- a/drivers/mtd/cmdlinepart.c +++ b/drivers/mtd/cmdlinepart.c @@ -42,7 +42,8 @@ /* special size referring to all the remaining space in a partition */ -#define SIZE_REMAINING 0xffffffff +#define SIZE_REMAINING UINT_MAX +#define OFFSET_CONTINUOUS UINT_MAX struct cmdline_mtd_partition { struct cmdline_mtd_partition *next; @@ -75,7 +76,7 @@ static struct mtd_partition * newpart(char *s, { struct mtd_partition *parts; unsigned long size; - unsigned long offset = 0; + unsigned long offset = OFFSET_CONTINUOUS; char *name; int name_len; unsigned char *extra_mem; @@ -314,7 +315,7 @@ static int parse_cmdline_partitions(struct mtd_info *master, { for(i = 0, offset = 0; i < part->num_parts; i++) { - if (!part->parts[i].offset) + if (part->parts[i].offset == OFFSET_CONTINUOUS) part->parts[i].offset = offset; else offset = part->parts[i].offset; diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index dd628cb51e31..7fac438b5c32 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -129,8 +129,8 @@ config MTDRAM_ABS_POS allocating space from Linux's available memory. Otherwise, leave this set to zero. Most people will want to leave this as zero. -config MTD_BLKMTD - tristate "MTD emulation using block device" +config MTD_BLOCK2MTD + tristate "MTD using block device" depends on MTD help This driver allows a block device to appear as an MTD. It would @@ -141,15 +141,6 @@ config MTD_BLKMTD Testing MTD users (eg JFFS2) on large media and media that might be removed during a write (using the floppy drive). -config MTD_BLOCK2MTD - tristate "MTD using block device (rewrite)" - depends on MTD && EXPERIMENTAL - help - This driver is basically the same at MTD_BLKMTD above, but - experienced some interface changes plus serious speedups. In - the long term, it should replace MTD_BLKMTD. Right now, you - shouldn't entrust important data to it yet. - comment "Disk-On-Chip Device Drivers" config MTD_DOC2000 diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 7c5ed2178380..b6573670316f 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -21,7 +21,6 @@ obj-$(CONFIG_MTD_PMC551) += pmc551.o obj-$(CONFIG_MTD_MS02NV) += ms02-nv.o obj-$(CONFIG_MTD_MTDRAM) += mtdram.o obj-$(CONFIG_MTD_LART) += lart.o -obj-$(CONFIG_MTD_BLKMTD) += blkmtd.o obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o obj-$(CONFIG_MTD_M25P80) += m25p80.o diff --git a/drivers/mtd/devices/blkmtd.c b/drivers/mtd/devices/blkmtd.c deleted file mode 100644 index 04f864d238db..000000000000 --- a/drivers/mtd/devices/blkmtd.c +++ /dev/null @@ -1,820 +0,0 @@ -/* - * $Id: blkmtd.c,v 1.27 2005/11/07 11:14:24 gleixner Exp $ - * - * blkmtd.c - use a block device as a fake MTD - * - * Author: Simon Evans - * - * Copyright (C) 2001,2002 Simon Evans - * - * Licence: GPL - * - * How it works: - * The driver uses raw/io to read/write the device and the page - * cache to cache access. Writes update the page cache with the - * new data and mark it dirty and add the page into a BIO which - * is then written out. - * - * It can be loaded Read-Only to prevent erases and writes to the - * medium. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#define err(format, arg...) printk(KERN_ERR "blkmtd: " format "\n" , ## arg) -#define info(format, arg...) printk(KERN_INFO "blkmtd: " format "\n" , ## arg) -#define warn(format, arg...) printk(KERN_WARNING "blkmtd: " format "\n" , ## arg) -#define crit(format, arg...) printk(KERN_CRIT "blkmtd: " format "\n" , ## arg) - - -/* Default erase size in K, always make it a multiple of PAGE_SIZE */ -#define CONFIG_MTD_BLKDEV_ERASESIZE (128 << 10) /* 128KiB */ -#define VERSION "$Revision: 1.27 $" - -/* Info for the block device */ -struct blkmtd_dev { - struct list_head list; - struct block_device *blkdev; - struct mtd_info mtd_info; - struct semaphore wrbuf_mutex; -}; - - -/* Static info about the MTD, used in cleanup_module */ -static LIST_HEAD(blkmtd_device_list); - - -static void blkmtd_sync(struct mtd_info *mtd); - -#define MAX_DEVICES 4 - -/* Module parameters passed by insmod/modprobe */ -static char *device[MAX_DEVICES]; /* the block device to use */ -static int erasesz[MAX_DEVICES]; /* optional default erase size */ -static int ro[MAX_DEVICES]; /* optional read only flag */ -static int sync; - - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Simon Evans "); -MODULE_DESCRIPTION("Emulate an MTD using a block device"); -module_param_array(device, charp, NULL, 0); -MODULE_PARM_DESC(device, "block device to use"); -module_param_array(erasesz, int, NULL, 0); -MODULE_PARM_DESC(erasesz, "optional erase size to use in KiB. eg 4=4KiB."); -module_param_array(ro, bool, NULL, 0); -MODULE_PARM_DESC(ro, "1=Read only, writes and erases cause errors"); -module_param(sync, bool, 0); -MODULE_PARM_DESC(sync, "1=Synchronous writes"); - - -/* completion handler for BIO reads */ -static int bi_read_complete(struct bio *bio, unsigned int bytes_done, int error) -{ - if (bio->bi_size) - return 1; - - complete((struct completion*)bio->bi_private); - return 0; -} - - -/* completion handler for BIO writes */ -static int bi_write_complete(struct bio *bio, unsigned int bytes_done, int error) -{ - const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); - struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1; - - if (bio->bi_size) - return 1; - - if(!uptodate) - err("bi_write_complete: not uptodate\n"); - - do { - struct page *page = bvec->bv_page; - DEBUG(3, "Cleaning up page %ld\n", page->index); - if (--bvec >= bio->bi_io_vec) - prefetchw(&bvec->bv_page->flags); - - if (uptodate) { - SetPageUptodate(page); - } else { - ClearPageUptodate(page); - SetPageError(page); - } - clear_page_dirty(page); - unlock_page(page); - page_cache_release(page); - } while (bvec >= bio->bi_io_vec); - - complete((struct completion*)bio->bi_private); - return 0; -} - - -/* read one page from the block device */ -static int blkmtd_readpage(struct blkmtd_dev *dev, struct page *page) -{ - struct bio *bio; - struct completion event; - int err = -ENOMEM; - - if(PageUptodate(page)) { - DEBUG(2, "blkmtd: readpage page %ld is already upto date\n", page->index); - unlock_page(page); - return 0; - } - - ClearPageUptodate(page); - ClearPageError(page); - - bio = bio_alloc(GFP_KERNEL, 1); - if(bio) { - init_completion(&event); - bio->bi_bdev = dev->blkdev; - bio->bi_sector = page->index << (PAGE_SHIFT-9); - bio->bi_private = &event; - bio->bi_end_io = bi_read_complete; - if(bio_add_page(bio, page, PAGE_SIZE, 0) == PAGE_SIZE) { - submit_bio(READ_SYNC, bio); - wait_for_completion(&event); - err = test_bit(BIO_UPTODATE, &bio->bi_flags) ? 0 : -EIO; - bio_put(bio); - } - } - - if(err) - SetPageError(page); - else - SetPageUptodate(page); - flush_dcache_page(page); - unlock_page(page); - return err; -} - - -/* write out the current BIO and wait for it to finish */ -static int blkmtd_write_out(struct bio *bio) -{ - struct completion event; - int err; - - if(!bio->bi_vcnt) { - bio_put(bio); - return 0; - } - - init_completion(&event); - bio->bi_private = &event; - bio->bi_end_io = bi_write_complete; - submit_bio(WRITE_SYNC, bio); - wait_for_completion(&event); - DEBUG(3, "submit_bio completed, bi_vcnt = %d\n", bio->bi_vcnt); - err = test_bit(BIO_UPTODATE, &bio->bi_flags) ? 0 : -EIO; - bio_put(bio); - return err; -} - - -/** - * blkmtd_add_page - add a page to the current BIO - * @bio: bio to add to (NULL to alloc initial bio) - * @blkdev: block device - * @page: page to add - * @pagecnt: pages left to add - * - * Adds a page to the current bio, allocating it if necessary. If it cannot be - * added, the current bio is written out and a new one is allocated. Returns - * the new bio to add or NULL on error - */ -static struct bio *blkmtd_add_page(struct bio *bio, struct block_device *blkdev, - struct page *page, int pagecnt) -{ - - retry: - if(!bio) { - bio = bio_alloc(GFP_KERNEL, pagecnt); - if(!bio) - return NULL; - bio->bi_sector = page->index << (PAGE_SHIFT-9); - bio->bi_bdev = blkdev; - } - - if(bio_add_page(bio, page, PAGE_SIZE, 0) != PAGE_SIZE) { - blkmtd_write_out(bio); - bio = NULL; - goto retry; - } - return bio; -} - - -/** - * write_pages - write block of data to device via the page cache - * @dev: device to write to - * @buf: data source or NULL if erase (output is set to 0xff) - * @to: offset into output device - * @len: amount to data to write - * @retlen: amount of data written - * - * Grab pages from the page cache and fill them with the source data. - * Non page aligned start and end result in a readin of the page and - * part of the page being modified. Pages are added to the bio and then written - * out. - */ -static int write_pages(struct blkmtd_dev *dev, const u_char *buf, loff_t to, - size_t len, size_t *retlen) -{ - int pagenr, offset; - size_t start_len = 0, end_len; - int pagecnt = 0; - int err = 0; - struct bio *bio = NULL; - size_t thislen = 0; - - pagenr = to >> PAGE_SHIFT; - offset = to & ~PAGE_MASK; - - DEBUG(2, "blkmtd: write_pages: buf = %p to = %ld len = %zd pagenr = %d offset = %d\n", - buf, (long)to, len, pagenr, offset); - - /* see if we have to do a partial write at the start */ - if(offset) { - start_len = ((offset + len) > PAGE_SIZE) ? PAGE_SIZE - offset : len; - len -= start_len; - } - - /* calculate the length of the other two regions */ - end_len = len & ~PAGE_MASK; - len -= end_len; - - if(start_len) - pagecnt++; - - if(len) - pagecnt += len >> PAGE_SHIFT; - - if(end_len) - pagecnt++; - - down(&dev->wrbuf_mutex); - - DEBUG(3, "blkmtd: write: start_len = %zd len = %zd end_len = %zd pagecnt = %d\n", - start_len, len, end_len, pagecnt); - - if(start_len) { - /* do partial start region */ - struct page *page; - - DEBUG(3, "blkmtd: write: doing partial start, page = %d len = %zd offset = %d\n", - pagenr, start_len, offset); - - BUG_ON(!buf); - page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev); - lock_page(page); - if(PageDirty(page)) { - err("to = %lld start_len = %zd len = %zd end_len = %zd pagenr = %d\n", - to, start_len, len, end_len, pagenr); - BUG(); - } - memcpy(page_address(page)+offset, buf, start_len); - set_page_dirty(page); - SetPageUptodate(page); - buf += start_len; - thislen = start_len; - bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt); - if(!bio) { - err = -ENOMEM; - err("bio_add_page failed\n"); - goto write_err; - } - pagecnt--; - pagenr++; - } - - /* Now do the main loop to a page aligned, n page sized output */ - if(len) { - int pagesc = len >> PAGE_SHIFT; - DEBUG(3, "blkmtd: write: whole pages start = %d, count = %d\n", - pagenr, pagesc); - while(pagesc) { - struct page *page; - - /* see if page is in the page cache */ - DEBUG(3, "blkmtd: write: grabbing page %d from page cache\n", pagenr); - page = grab_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr); - if(PageDirty(page)) { - BUG(); - } - if(!page) { - warn("write: cannot grab cache page %d", pagenr); - err = -ENOMEM; - goto write_err; - } - if(!buf) { - memset(page_address(page), 0xff, PAGE_SIZE); - } else { - memcpy(page_address(page), buf, PAGE_SIZE); - buf += PAGE_SIZE; - } - bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt); - if(!bio) { - err = -ENOMEM; - err("bio_add_page failed\n"); - goto write_err; - } - pagenr++; - pagecnt--; - set_page_dirty(page); - SetPageUptodate(page); - pagesc--; - thislen += PAGE_SIZE; - } - } - - if(end_len) { - /* do the third region */ - struct page *page; - DEBUG(3, "blkmtd: write: doing partial end, page = %d len = %zd\n", - pagenr, end_len); - BUG_ON(!buf); - page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev); - lock_page(page); - if(PageDirty(page)) { - err("to = %lld start_len = %zd len = %zd end_len = %zd pagenr = %d\n", - to, start_len, len, end_len, pagenr); - BUG(); - } - memcpy(page_address(page), buf, end_len); - set_page_dirty(page); - SetPageUptodate(page); - DEBUG(3, "blkmtd: write: writing out partial end\n"); - thislen += end_len; - bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt); - if(!bio) { - err = -ENOMEM; - err("bio_add_page failed\n"); - goto write_err; - } - pagenr++; - } - - DEBUG(3, "blkmtd: write: got %d vectors to write\n", bio->bi_vcnt); - write_err: - if(bio) - blkmtd_write_out(bio); - - DEBUG(2, "blkmtd: write: end, retlen = %zd, err = %d\n", *retlen, err); - up(&dev->wrbuf_mutex); - - if(retlen) - *retlen = thislen; - return err; -} - - -/* erase a specified part of the device */ -static int blkmtd_erase(struct mtd_info *mtd, struct erase_info *instr) -{ - struct blkmtd_dev *dev = mtd->priv; - struct mtd_erase_region_info *einfo = mtd->eraseregions; - int numregions = mtd->numeraseregions; - size_t from; - u_long len; - int err = -EIO; - size_t retlen; - - instr->state = MTD_ERASING; - from = instr->addr; - len = instr->len; - - /* check erase region has valid start and length */ - DEBUG(2, "blkmtd: erase: dev = `%s' from = 0x%zx len = 0x%lx\n", - mtd->name+9, from, len); - while(numregions) { - DEBUG(3, "blkmtd: checking erase region = 0x%08X size = 0x%X num = 0x%x\n", - einfo->offset, einfo->erasesize, einfo->numblocks); - if(from >= einfo->offset - && from < einfo->offset + (einfo->erasesize * einfo->numblocks)) { - if(len == einfo->erasesize - && ( (from - einfo->offset) % einfo->erasesize == 0)) - break; - } - numregions--; - einfo++; - } - - if(!numregions) { - /* Not a valid erase block */ - err("erase: invalid erase request 0x%lX @ 0x%08zX", len, from); - instr->state = MTD_ERASE_FAILED; - err = -EIO; - } - - if(instr->state != MTD_ERASE_FAILED) { - /* do the erase */ - DEBUG(3, "Doing erase from = %zd len = %ld\n", from, len); - err = write_pages(dev, NULL, from, len, &retlen); - if(err || retlen != len) { - err("erase failed err = %d", err); - instr->state = MTD_ERASE_FAILED; - } else { - instr->state = MTD_ERASE_DONE; - } - } - - DEBUG(3, "blkmtd: erase: checking callback\n"); - mtd_erase_callback(instr); - DEBUG(2, "blkmtd: erase: finished (err = %d)\n", err); - return err; -} - - -/* read a range of the data via the page cache */ -static int blkmtd_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) -{ - struct blkmtd_dev *dev = mtd->priv; - int err = 0; - int offset; - int pagenr, pages; - size_t thislen = 0; - - DEBUG(2, "blkmtd: read: dev = `%s' from = %lld len = %zd buf = %p\n", - mtd->name+9, from, len, buf); - - if(from > mtd->size) - return -EINVAL; - if(from + len > mtd->size) - len = mtd->size - from; - - pagenr = from >> PAGE_SHIFT; - offset = from - (pagenr << PAGE_SHIFT); - - pages = (offset+len+PAGE_SIZE-1) >> PAGE_SHIFT; - DEBUG(3, "blkmtd: read: pagenr = %d offset = %d, pages = %d\n", - pagenr, offset, pages); - - while(pages) { - struct page *page; - int cpylen; - - DEBUG(3, "blkmtd: read: looking for page: %d\n", pagenr); - page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev); - if(IS_ERR(page)) { - err = -EIO; - goto readerr; - } - - cpylen = (PAGE_SIZE > len) ? len : PAGE_SIZE; - if(offset+cpylen > PAGE_SIZE) - cpylen = PAGE_SIZE-offset; - - memcpy(buf + thislen, page_address(page) + offset, cpylen); - offset = 0; - len -= cpylen; - thislen += cpylen; - pagenr++; - pages--; - if(!PageDirty(page)) - page_cache_release(page); - } - - readerr: - if(retlen) - *retlen = thislen; - DEBUG(2, "blkmtd: end read: retlen = %zd, err = %d\n", thislen, err); - return err; -} - - -/* write data to the underlying device */ -static int blkmtd_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) -{ - struct blkmtd_dev *dev = mtd->priv; - int err; - - if(!len) - return 0; - - DEBUG(2, "blkmtd: write: dev = `%s' to = %lld len = %zd buf = %p\n", - mtd->name+9, to, len, buf); - - if(to >= mtd->size) { - return -ENOSPC; - } - - if(to + len > mtd->size) { - len = mtd->size - to; - } - - err = write_pages(dev, buf, to, len, retlen); - if(err > 0) - err = 0; - DEBUG(2, "blkmtd: write: end, err = %d\n", err); - return err; -} - - -/* sync the device - wait until the write queue is empty */ -static void blkmtd_sync(struct mtd_info *mtd) -{ - /* Currently all writes are synchronous */ -} - - -static void free_device(struct blkmtd_dev *dev) -{ - DEBUG(2, "blkmtd: free_device() dev = %p\n", dev); - if(dev) { - kfree(dev->mtd_info.eraseregions); - kfree(dev->mtd_info.name); - if(dev->blkdev) { - invalidate_inode_pages(dev->blkdev->bd_inode->i_mapping); - close_bdev_excl(dev->blkdev); - } - kfree(dev); - } -} - - -/* For a given size and initial erase size, calculate the number - * and size of each erase region. Goes round the loop twice, - * once to find out how many regions, then allocates space, - * then round the loop again to fill it in. - */ -static struct mtd_erase_region_info *calc_erase_regions( - size_t erase_size, size_t total_size, int *regions) -{ - struct mtd_erase_region_info *info = NULL; - - DEBUG(2, "calc_erase_regions, es = %zd size = %zd regions = %d\n", - erase_size, total_size, *regions); - /* Make any user specified erasesize be a power of 2 - and at least PAGE_SIZE */ - if(erase_size) { - int es = erase_size; - erase_size = 1; - while(es != 1) { - es >>= 1; - erase_size <<= 1; - } - if(erase_size < PAGE_SIZE) - erase_size = PAGE_SIZE; - } else { - erase_size = CONFIG_MTD_BLKDEV_ERASESIZE; - } - - *regions = 0; - - do { - int tot_size = total_size; - int er_size = erase_size; - int count = 0, offset = 0, regcnt = 0; - - while(tot_size) { - count = tot_size / er_size; - if(count) { - tot_size = tot_size % er_size; - if(info) { - DEBUG(2, "adding to erase info off=%d er=%d cnt=%d\n", - offset, er_size, count); - (info+regcnt)->offset = offset; - (info+regcnt)->erasesize = er_size; - (info+regcnt)->numblocks = count; - (*regions)++; - } - regcnt++; - offset += (count * er_size); - } - while(er_size > tot_size) - er_size >>= 1; - } - if(info == NULL) { - info = kmalloc(regcnt * sizeof(struct mtd_erase_region_info), GFP_KERNEL); - if(!info) - break; - } - } while(!(*regions)); - DEBUG(2, "calc_erase_regions done, es = %zd size = %zd regions = %d\n", - erase_size, total_size, *regions); - return info; -} - - -extern dev_t __init name_to_dev_t(const char *line); - -static struct blkmtd_dev *add_device(char *devname, int readonly, int erase_size) -{ - struct block_device *bdev; - int mode; - struct blkmtd_dev *dev; - - if(!devname) - return NULL; - - /* Get a handle on the device */ - - -#ifdef MODULE - mode = (readonly) ? O_RDONLY : O_RDWR; - bdev = open_bdev_excl(devname, mode, NULL); -#else - mode = (readonly) ? FMODE_READ : FMODE_WRITE; - bdev = open_by_devnum(name_to_dev_t(devname), mode); -#endif - if(IS_ERR(bdev)) { - err("error: cannot open device %s", devname); - DEBUG(2, "blkmtd: opening bdev returned %ld\n", PTR_ERR(bdev)); - return NULL; - } - - DEBUG(1, "blkmtd: found a block device major = %d, minor = %d\n", - MAJOR(bdev->bd_dev), MINOR(bdev->bd_dev)); - - if(MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) { - err("attempting to use an MTD device as a block device"); - blkdev_put(bdev); - return NULL; - } - - dev = kmalloc(sizeof(struct blkmtd_dev), GFP_KERNEL); - if(dev == NULL) { - blkdev_put(bdev); - return NULL; - } - - memset(dev, 0, sizeof(struct blkmtd_dev)); - dev->blkdev = bdev; - if(!readonly) { - init_MUTEX(&dev->wrbuf_mutex); - } - - dev->mtd_info.size = dev->blkdev->bd_inode->i_size & PAGE_MASK; - - /* Setup the MTD structure */ - /* make the name contain the block device in */ - dev->mtd_info.name = kmalloc(sizeof("blkmtd: ") + strlen(devname), GFP_KERNEL); - if(dev->mtd_info.name == NULL) - goto devinit_err; - - sprintf(dev->mtd_info.name, "blkmtd: %s", devname); - dev->mtd_info.eraseregions = calc_erase_regions(erase_size, dev->mtd_info.size, - &dev->mtd_info.numeraseregions); - if(dev->mtd_info.eraseregions == NULL) - goto devinit_err; - - dev->mtd_info.erasesize = dev->mtd_info.eraseregions->erasesize; - DEBUG(1, "blkmtd: init: found %d erase regions\n", - dev->mtd_info.numeraseregions); - - if(readonly) { - dev->mtd_info.type = MTD_ROM; - dev->mtd_info.flags = MTD_CAP_ROM; - } else { - dev->mtd_info.type = MTD_RAM; - dev->mtd_info.flags = MTD_CAP_RAM; - dev->mtd_info.erase = blkmtd_erase; - dev->mtd_info.write = blkmtd_write; - dev->mtd_info.writev = default_mtd_writev; - dev->mtd_info.sync = blkmtd_sync; - } - dev->mtd_info.read = blkmtd_read; - dev->mtd_info.readv = default_mtd_readv; - dev->mtd_info.priv = dev; - dev->mtd_info.owner = THIS_MODULE; - - list_add(&dev->list, &blkmtd_device_list); - if (add_mtd_device(&dev->mtd_info)) { - /* Device didnt get added, so free the entry */ - list_del(&dev->list); - goto devinit_err; - } else { - info("mtd%d: [%s] erase_size = %dKiB %s", - dev->mtd_info.index, dev->mtd_info.name + strlen("blkmtd: "), - dev->mtd_info.erasesize >> 10, - readonly ? "(read-only)" : ""); - } - - return dev; - - devinit_err: - free_device(dev); - return NULL; -} - - -/* Cleanup and exit - sync the device and kill of the kernel thread */ -static void __devexit cleanup_blkmtd(void) -{ - struct list_head *temp1, *temp2; - - /* Remove the MTD devices */ - list_for_each_safe(temp1, temp2, &blkmtd_device_list) { - struct blkmtd_dev *dev = list_entry(temp1, struct blkmtd_dev, - list); - blkmtd_sync(&dev->mtd_info); - del_mtd_device(&dev->mtd_info); - info("mtd%d: [%s] removed", dev->mtd_info.index, - dev->mtd_info.name + strlen("blkmtd: ")); - list_del(&dev->list); - free_device(dev); - } -} - -#ifndef MODULE - -/* Handle kernel boot params */ - - -static int __init param_blkmtd_device(char *str) -{ - int i; - - for(i = 0; i < MAX_DEVICES; i++) { - device[i] = str; - DEBUG(2, "blkmtd: device setup: %d = %s\n", i, device[i]); - strsep(&str, ","); - } - return 1; -} - - -static int __init param_blkmtd_erasesz(char *str) -{ - int i; - for(i = 0; i < MAX_DEVICES; i++) { - char *val = strsep(&str, ","); - if(val) - erasesz[i] = simple_strtoul(val, NULL, 0); - DEBUG(2, "blkmtd: erasesz setup: %d = %d\n", i, erasesz[i]); - } - - return 1; -} - - -static int __init param_blkmtd_ro(char *str) -{ - int i; - for(i = 0; i < MAX_DEVICES; i++) { - char *val = strsep(&str, ","); - if(val) - ro[i] = simple_strtoul(val, NULL, 0); - DEBUG(2, "blkmtd: ro setup: %d = %d\n", i, ro[i]); - } - - return 1; -} - - -static int __init param_blkmtd_sync(char *str) -{ - if(str[0] == '1') - sync = 1; - return 1; -} - -__setup("blkmtd_device=", param_blkmtd_device); -__setup("blkmtd_erasesz=", param_blkmtd_erasesz); -__setup("blkmtd_ro=", param_blkmtd_ro); -__setup("blkmtd_sync=", param_blkmtd_sync); - -#endif - - -/* Startup */ -static int __init init_blkmtd(void) -{ - int i; - - info("version " VERSION); - /* Check args - device[0] is the bare minimum*/ - if(!device[0]) { - err("error: missing `device' name\n"); - return -EINVAL; - } - - for(i = 0; i < MAX_DEVICES; i++) - add_device(device[i], ro[i], erasesz[i] << 10); - - if(list_empty(&blkmtd_device_list)) - return -EINVAL; - - return 0; -} - -module_init(init_blkmtd); -module_exit(cleanup_blkmtd); diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index 7ff403b2a0a0..4160b8334c53 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -18,6 +18,7 @@ #include #include #include +#include #define VERSION "$Revision: 1.30 $" @@ -31,7 +32,7 @@ struct block2mtd_dev { struct list_head list; struct block_device *blkdev; struct mtd_info mtd; - struct semaphore write_mutex; + struct mutex write_mutex; }; @@ -134,9 +135,9 @@ static int block2mtd_erase(struct mtd_info *mtd, struct erase_info *instr) int err; instr->state = MTD_ERASING; - down(&dev->write_mutex); + mutex_lock(&dev->write_mutex); err = _block2mtd_erase(dev, from, len); - up(&dev->write_mutex); + mutex_unlock(&dev->write_mutex); if (err) { ERROR("erase failed err = %d", err); instr->state = MTD_ERASE_FAILED; @@ -249,9 +250,9 @@ static int block2mtd_write(struct mtd_info *mtd, loff_t to, size_t len, if (to + len > mtd->size) len = mtd->size - to; - down(&dev->write_mutex); + mutex_lock(&dev->write_mutex); err = _block2mtd_write(dev, buf, to, len, retlen); - up(&dev->write_mutex); + mutex_unlock(&dev->write_mutex); if (err > 0) err = 0; return err; @@ -310,7 +311,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) goto devinit_err; } - init_MUTEX(&dev->write_mutex); + mutex_init(&dev->write_mutex); /* Setup the MTD structure */ /* make the name contain the block device in */ diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c index e4345cf744a2..23e7a5c7d2c1 100644 --- a/drivers/mtd/devices/doc2000.c +++ b/drivers/mtd/devices/doc2000.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -605,7 +606,7 @@ static void DoC2k_init(struct mtd_info *mtd) this->curfloor = -1; this->curchip = -1; - init_MUTEX(&this->lock); + mutex_init(&this->lock); /* Ident all the chips present. */ DoC_ScanChips(this, maxchips); @@ -645,7 +646,7 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, if (from >= this->totlen) return -EINVAL; - down(&this->lock); + mutex_lock(&this->lock); *retlen = 0; while (left) { @@ -774,7 +775,7 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, buf += len; } - up(&this->lock); + mutex_unlock(&this->lock); return ret; } @@ -803,7 +804,7 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, if (to >= this->totlen) return -EINVAL; - down(&this->lock); + mutex_lock(&this->lock); *retlen = 0; while (left) { @@ -873,7 +874,7 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, printk(KERN_ERR "Error programming flash\n"); /* Error in programming */ *retlen = 0; - up(&this->lock); + mutex_unlock(&this->lock); return -EIO; } @@ -935,7 +936,7 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, printk(KERN_ERR "Error programming flash\n"); /* Error in programming */ *retlen = 0; - up(&this->lock); + mutex_unlock(&this->lock); return -EIO; } @@ -956,7 +957,7 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, ret = doc_write_oob_nolock(mtd, to, 8, &dummy, x); if (ret) { - up(&this->lock); + mutex_unlock(&this->lock); return ret; } } @@ -966,7 +967,7 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, buf += len; } - up(&this->lock); + mutex_unlock(&this->lock); return 0; } @@ -975,13 +976,13 @@ static int doc_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, u_char *eccbuf, struct nand_oobinfo *oobsel) { static char static_buf[512]; - static DECLARE_MUTEX(writev_buf_sem); + static DEFINE_MUTEX(writev_buf_mutex); size_t totretlen = 0; size_t thisvecofs = 0; int ret= 0; - down(&writev_buf_sem); + mutex_lock(&writev_buf_mutex); while(count) { size_t thislen, thisretlen; @@ -1024,7 +1025,7 @@ static int doc_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, to += thislen; } - up(&writev_buf_sem); + mutex_unlock(&writev_buf_mutex); *retlen = totretlen; return ret; } @@ -1037,7 +1038,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, int len256 = 0, ret; struct Nand *mychip; - down(&this->lock); + mutex_lock(&this->lock); mychip = &this->chips[ofs >> this->chipshift]; @@ -1083,7 +1084,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, ret = DoC_WaitReady(this); - up(&this->lock); + mutex_unlock(&this->lock); return ret; } @@ -1197,10 +1198,10 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, struct DiskOnChip *this = mtd->priv; int ret; - down(&this->lock); + mutex_lock(&this->lock); ret = doc_write_oob_nolock(mtd, ofs, len, retlen, buf); - up(&this->lock); + mutex_unlock(&this->lock); return ret; } @@ -1214,10 +1215,10 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *instr) struct Nand *mychip; int status; - down(&this->lock); + mutex_lock(&this->lock); if (ofs & (mtd->erasesize-1) || len & (mtd->erasesize-1)) { - up(&this->lock); + mutex_unlock(&this->lock); return -EINVAL; } @@ -1265,7 +1266,7 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *instr) callback: mtd_erase_callback(instr); - up(&this->lock); + mutex_unlock(&this->lock); return 0; } diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c index 1e876fcb0408..29b0ddaa324e 100644 --- a/drivers/mtd/devices/lart.c +++ b/drivers/mtd/devices/lart.c @@ -581,8 +581,6 @@ static int flash_write (struct mtd_info *mtd,loff_t to,size_t len,size_t *retlen /***************************************************************************************************/ -#define NB_OF(x) (sizeof (x) / sizeof (x[0])) - static struct mtd_info mtd; static struct mtd_erase_region_info erase_regions[] = { @@ -640,7 +638,7 @@ int __init lart_flash_init (void) mtd.flags = MTD_CAP_NORFLASH; mtd.size = FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM + FLASH_BLOCKSIZE_MAIN * FLASH_NUMBLOCKS_16m_MAIN; mtd.erasesize = FLASH_BLOCKSIZE_MAIN; - mtd.numeraseregions = NB_OF (erase_regions); + mtd.numeraseregions = ARRAY_SIZE(erase_regions); mtd.eraseregions = erase_regions; mtd.erase = flash_erase; mtd.read = flash_read; @@ -670,9 +668,9 @@ int __init lart_flash_init (void) result,mtd.eraseregions[result].numblocks); #ifdef HAVE_PARTITIONS - printk ("\npartitions = %d\n",NB_OF (lart_partitions)); + printk ("\npartitions = %d\n", ARRAY_SIZE(lart_partitions)); - for (result = 0; result < NB_OF (lart_partitions); result++) + for (result = 0; result < ARRAY_SIZE(lart_partitions); result++) printk (KERN_DEBUG "\n\n" "lart_partitions[%d].name = %s\n" @@ -687,7 +685,7 @@ int __init lart_flash_init (void) #ifndef HAVE_PARTITIONS result = add_mtd_device (&mtd); #else - result = add_mtd_partitions (&mtd,lart_partitions,NB_OF (lart_partitions)); + result = add_mtd_partitions (&mtd,lart_partitions, ARRAY_SIZE(lart_partitions)); #endif return (result); diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index d5f24089be71..04e65d5dae00 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -186,7 +186,7 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) struct m25p *flash = mtd_to_m25p(mtd); u32 addr,len; - DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n", + DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %d\n", flash->spi->dev.bus_id, __FUNCTION__, "at", (u32)instr->addr, instr->len); diff --git a/drivers/mtd/devices/ms02-nv.c b/drivers/mtd/devices/ms02-nv.c index 0ff2e4378244..485f663493d2 100644 --- a/drivers/mtd/devices/ms02-nv.c +++ b/drivers/mtd/devices/ms02-nv.c @@ -308,7 +308,7 @@ static int __init ms02nv_init(void) break; } - for (i = 0; i < (sizeof(ms02nv_addrs) / sizeof(*ms02nv_addrs)); i++) + for (i = 0; i < ARRAY_SIZE(ms02nv_addrs); i++) if (!ms02nv_init_one(ms02nv_addrs[i] << stride)) count++; diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index 8a544890173d..a3b92479719d 100644 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c @@ -47,9 +47,6 @@ */ #define MAX_LOOPS 10000 -extern void INFTL_dumptables(struct INFTLrecord *inftl); -extern void INFTL_dumpVUchains(struct INFTLrecord *inftl); - static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) { struct INFTLrecord *inftl; @@ -132,7 +129,7 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) return; } #ifdef PSYCHO_DEBUG - printk(KERN_INFO "INFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a'); + printk(KERN_INFO "INFTL: Found new inftl%c\n", inftl->mbd.devnum + 'a'); #endif return; } @@ -885,8 +882,6 @@ static struct mtd_blktrans_ops inftl_tr = { .owner = THIS_MODULE, }; -extern char inftlmountrev[]; - static int __init init_inftl(void) { printk(KERN_INFO "INFTL: inftlcore.c $Revision: 1.19 $, " diff --git a/drivers/mtd/maps/alchemy-flash.c b/drivers/mtd/maps/alchemy-flash.c index a57791a6ce40..b933a2a27b18 100644 --- a/drivers/mtd/maps/alchemy-flash.c +++ b/drivers/mtd/maps/alchemy-flash.c @@ -126,8 +126,6 @@ static struct mtd_partition alchemy_partitions[] = { } }; -#define NB_OF(x) (sizeof(x)/sizeof(x[0])) - static struct mtd_info *mymtd; int __init alchemy_mtd_init(void) @@ -154,7 +152,7 @@ int __init alchemy_mtd_init(void) * Static partition definition selection */ parts = alchemy_partitions; - nb_parts = NB_OF(alchemy_partitions); + nb_parts = ARRAY_SIZE(alchemy_partitions); alchemy_map.size = window_size; /* diff --git a/drivers/mtd/maps/cfi_flagadm.c b/drivers/mtd/maps/cfi_flagadm.c index 6a8c0415bde8..fd0f0d3187de 100644 --- a/drivers/mtd/maps/cfi_flagadm.c +++ b/drivers/mtd/maps/cfi_flagadm.c @@ -86,7 +86,7 @@ struct mtd_partition flagadm_parts[] = { } }; -#define PARTITION_COUNT (sizeof(flagadm_parts)/sizeof(struct mtd_partition)) +#define PARTITION_COUNT ARRAY_SIZE(flagadm_parts) static struct mtd_info *mymtd; diff --git a/drivers/mtd/maps/dbox2-flash.c b/drivers/mtd/maps/dbox2-flash.c index 49d90542fc75..652813cd6c2d 100644 --- a/drivers/mtd/maps/dbox2-flash.c +++ b/drivers/mtd/maps/dbox2-flash.c @@ -57,7 +57,7 @@ static struct mtd_partition partition_info[]= { } }; -#define NUM_PARTITIONS (sizeof(partition_info) / sizeof(partition_info[0])) +#define NUM_PARTITIONS ARRAY_SIZE(partition_info) #define WINDOW_ADDR 0x10000000 #define WINDOW_SIZE 0x800000 diff --git a/drivers/mtd/maps/dilnetpc.c b/drivers/mtd/maps/dilnetpc.c index efb221692641..c299d10b33e6 100644 --- a/drivers/mtd/maps/dilnetpc.c +++ b/drivers/mtd/maps/dilnetpc.c @@ -300,7 +300,7 @@ static struct mtd_partition partition_info[]= }, }; -#define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0])) +#define NUM_PARTITIONS ARRAY_SIZE(partition_info) static struct mtd_info *mymtd; static struct mtd_info *lowlvl_parts[NUM_PARTITIONS]; @@ -345,7 +345,7 @@ static struct mtd_partition higlvl_partition_info[]= }, }; -#define NUM_HIGHLVL_PARTITIONS (sizeof(higlvl_partition_info)/sizeof(partition_info[0])) +#define NUM_HIGHLVL_PARTITIONS ARRAY_SIZE(higlvl_partition_info) static int dnp_adnp_probe(void) diff --git a/drivers/mtd/maps/dmv182.c b/drivers/mtd/maps/dmv182.c index b993ac01a9a5..2bb3c0f0f970 100644 --- a/drivers/mtd/maps/dmv182.c +++ b/drivers/mtd/maps/dmv182.c @@ -99,7 +99,7 @@ static struct mtd_info *this_mtd; static int __init init_svme182(void) { struct mtd_partition *partitions; - int num_parts = sizeof(svme182_partitions) / sizeof(struct mtd_partition); + int num_parts = ARRAY_SIZE(svme182_partitions); partitions = svme182_partitions; diff --git a/drivers/mtd/maps/h720x-flash.c b/drivers/mtd/maps/h720x-flash.c index 319094821101..0667101ccbe1 100644 --- a/drivers/mtd/maps/h720x-flash.c +++ b/drivers/mtd/maps/h720x-flash.c @@ -59,7 +59,7 @@ static struct mtd_partition h720x_partitions[] = { } }; -#define NUM_PARTITIONS (sizeof(h720x_partitions)/sizeof(h720x_partitions[0])) +#define NUM_PARTITIONS ARRAY_SIZE(h720x_partitions) static int nr_mtd_parts; static struct mtd_partition *mtd_parts; diff --git a/drivers/mtd/maps/netsc520.c b/drivers/mtd/maps/netsc520.c index 33060a315722..ed215470158b 100644 --- a/drivers/mtd/maps/netsc520.c +++ b/drivers/mtd/maps/netsc520.c @@ -76,7 +76,7 @@ static struct mtd_partition partition_info[]={ .size = 0x80000 }, }; -#define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0])) +#define NUM_PARTITIONS ARRAY_SIZE(partition_info) #define WINDOW_SIZE 0x00100000 #define WINDOW_ADDR 0x00200000 @@ -88,7 +88,7 @@ static struct map_info netsc520_map = { .phys = WINDOW_ADDR, }; -#define NUM_FLASH_BANKS (sizeof(netsc520_map)/sizeof(struct map_info)) +#define NUM_FLASH_BANKS ARRAY_SIZE(netsc520_map) static struct mtd_info *mymtd; diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c index 632eb2aa968f..54a3102ab19a 100644 --- a/drivers/mtd/maps/nettel.c +++ b/drivers/mtd/maps/nettel.c @@ -128,8 +128,7 @@ static struct mtd_partition nettel_amd_partitions[] = { } }; -#define NUM_AMD_PARTITIONS \ - (sizeof(nettel_amd_partitions)/sizeof(nettel_amd_partitions[0])) +#define NUM_AMD_PARTITIONS ARRAY_SIZE(nettel_amd_partitions) /****************************************************************************/ diff --git a/drivers/mtd/maps/ocotea.c b/drivers/mtd/maps/ocotea.c index c223514ca2eb..a21fcd195ab4 100644 --- a/drivers/mtd/maps/ocotea.c +++ b/drivers/mtd/maps/ocotea.c @@ -58,8 +58,6 @@ static struct mtd_partition ocotea_large_partitions[] = { } }; -#define NB_OF(x) (sizeof(x)/sizeof(x[0])) - int __init init_ocotea(void) { u8 fpga0_reg; @@ -97,7 +95,7 @@ int __init init_ocotea(void) if (flash) { flash->owner = THIS_MODULE; add_mtd_partitions(flash, ocotea_small_partitions, - NB_OF(ocotea_small_partitions)); + ARRAY_SIZE(ocotea_small_partitions)); } else { printk("map probe failed for flash\n"); return -ENXIO; @@ -118,7 +116,7 @@ int __init init_ocotea(void) if (flash) { flash->owner = THIS_MODULE; add_mtd_partitions(flash, ocotea_large_partitions, - NB_OF(ocotea_large_partitions)); + ARRAY_SIZE(ocotea_large_partitions)); } else { printk("map probe failed for flash\n"); return -ENXIO; diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c index 21822c2edbe4..d2ab1bae9c34 100644 --- a/drivers/mtd/maps/pci.c +++ b/drivers/mtd/maps/pci.c @@ -334,9 +334,6 @@ mtd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) return 0; release: - if (mtd) - map_destroy(mtd); - if (map) { map->exit(dev, map); kfree(map); diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index f988c817e196..d27f4129afd3 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -54,7 +54,7 @@ static const int debug = 0; #define MAX_PCMCIA_ADDR 0x4000000 struct pcmciamtd_dev { - dev_link_t link; /* PCMCIA link */ + struct pcmcia_device *p_dev; dev_node_t node; /* device node */ caddr_t win_base; /* ioremapped address of PCMCIA window */ unsigned int win_size; /* size of window */ @@ -111,8 +111,8 @@ static caddr_t remap_window(struct map_info *map, unsigned long to) memreq_t mrq; int ret; - if(!(dev->link.state & DEV_PRESENT)) { - DEBUG(1, "device removed state = 0x%4.4X", dev->link.state); + if (!pcmcia_dev_present(dev->p_dev)) { + DEBUG(1, "device removed"); return 0; } @@ -122,7 +122,7 @@ static caddr_t remap_window(struct map_info *map, unsigned long to) dev->offset, mrq.CardOffset); mrq.Page = 0; if( (ret = pcmcia_map_mem_page(win, &mrq)) != CS_SUCCESS) { - cs_error(dev->link.handle, MapMemPage, ret); + cs_error(dev->p_dev, MapMemPage, ret); return NULL; } dev->offset = mrq.CardOffset; @@ -238,7 +238,7 @@ static void pcmcia_copy_to_remap(struct map_info *map, unsigned long to, const v /* read/write{8,16} copy_{from,to} routines with direct access */ -#define DEV_REMOVED(x) (!(*(u_int *)x->map_priv_1 & DEV_PRESENT)) +#define DEV_REMOVED(x) (!(pcmcia_dev_present(((struct pcmciamtd_dev *)map->map_priv_1)->p_dev))) static map_word pcmcia_read8(struct map_info *map, unsigned long ofs) { @@ -319,7 +319,7 @@ static void pcmcia_copy_to(struct map_info *map, unsigned long to, const void *f static void pcmciamtd_set_vpp(struct map_info *map, int on) { struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; - dev_link_t *link = &dev->link; + struct pcmcia_device *link = dev->p_dev; modconf_t mod; int ret; @@ -328,9 +328,9 @@ static void pcmciamtd_set_vpp(struct map_info *map, int on) mod.Vpp1 = mod.Vpp2 = on ? dev->vpp : 0; DEBUG(2, "dev = %p on = %d vpp = %d\n", dev, on, dev->vpp); - ret = pcmcia_modify_configuration(link->handle, &mod); + ret = pcmcia_modify_configuration(link, &mod); if(ret != CS_SUCCESS) { - cs_error(link->handle, ModifyConfiguration, ret); + cs_error(link, ModifyConfiguration, ret); } } @@ -340,7 +340,7 @@ static void pcmciamtd_set_vpp(struct map_info *map, int on) * still open, this will be postponed until it is closed. */ -static void pcmciamtd_release(dev_link_t *link) +static void pcmciamtd_release(struct pcmcia_device *link) { struct pcmciamtd_dev *dev = link->priv; @@ -353,12 +353,11 @@ static void pcmciamtd_release(dev_link_t *link) } pcmcia_release_window(link->win); } - pcmcia_release_configuration(link->handle); - link->state &= ~DEV_CONFIG; + pcmcia_disable_device(link); } -static void card_settings(struct pcmciamtd_dev *dev, dev_link_t *link, int *new_name) +static void card_settings(struct pcmciamtd_dev *dev, struct pcmcia_device *link, int *new_name) { int rc; tuple_t tuple; @@ -371,16 +370,16 @@ static void card_settings(struct pcmciamtd_dev *dev, dev_link_t *link, int *new_ tuple.TupleOffset = 0; tuple.DesiredTuple = RETURN_FIRST_TUPLE; - rc = pcmcia_get_first_tuple(link->handle, &tuple); + rc = pcmcia_get_first_tuple(link, &tuple); while(rc == CS_SUCCESS) { - rc = pcmcia_get_tuple_data(link->handle, &tuple); + rc = pcmcia_get_tuple_data(link, &tuple); if(rc != CS_SUCCESS) { - cs_error(link->handle, GetTupleData, rc); + cs_error(link, GetTupleData, rc); break; } - rc = pcmcia_parse_tuple(link->handle, &tuple, &parse); + rc = pcmcia_parse_tuple(link, &tuple, &parse); if(rc != CS_SUCCESS) { - cs_error(link->handle, ParseTuple, rc); + cs_error(link, ParseTuple, rc); break; } @@ -451,7 +450,7 @@ static void card_settings(struct pcmciamtd_dev *dev, dev_link_t *link, int *new_ DEBUG(2, "Unknown tuple code %d", tuple.TupleCode); } - rc = pcmcia_get_next_tuple(link->handle, &tuple); + rc = pcmcia_get_next_tuple(link, &tuple); } if(!dev->pcmcia_map.size) dev->pcmcia_map.size = MAX_PCMCIA_ADDR; @@ -488,7 +487,7 @@ static void card_settings(struct pcmciamtd_dev *dev, dev_link_t *link, int *new_ #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) -static void pcmciamtd_config(dev_link_t *link) +static int pcmciamtd_config(struct pcmcia_device *link) { struct pcmciamtd_dev *dev = link->priv; struct mtd_info *mtd = NULL; @@ -504,13 +503,10 @@ static void pcmciamtd_config(dev_link_t *link) DEBUG(3, "link=0x%p", link); - /* Configure card */ - link->state |= DEV_CONFIG; - DEBUG(2, "Validating CIS"); - ret = pcmcia_validate_cis(link->handle, &cisinfo); + ret = pcmcia_validate_cis(link, &cisinfo); if(ret != CS_SUCCESS) { - cs_error(link->handle, GetTupleData, ret); + cs_error(link, GetTupleData, ret); } else { DEBUG(2, "ValidateCIS found %d chains", cisinfo.Chains); } @@ -538,7 +534,7 @@ static void pcmciamtd_config(dev_link_t *link) req.Attributes |= (dev->pcmcia_map.bankwidth == 1) ? WIN_DATA_WIDTH_8 : WIN_DATA_WIDTH_16; req.Base = 0; req.AccessSpeed = mem_speed; - link->win = (window_handle_t)link->handle; + link->win = (window_handle_t)link; req.Size = (force_size) ? force_size << 20 : MAX_PCMCIA_ADDR; dev->win_size = 0; @@ -546,7 +542,7 @@ static void pcmciamtd_config(dev_link_t *link) int ret; DEBUG(2, "requesting window with size = %dKiB memspeed = %d", req.Size >> 10, req.AccessSpeed); - ret = pcmcia_request_window(&link->handle, &req, &link->win); + ret = pcmcia_request_window(&link, &req, &link->win); DEBUG(2, "ret = %d dev->win_size = %d", ret, dev->win_size); if(ret) { req.Size >>= 1; @@ -562,19 +558,19 @@ static void pcmciamtd_config(dev_link_t *link) if(!dev->win_size) { err("Cant allocate memory window"); pcmciamtd_release(link); - return; + return -ENODEV; } DEBUG(1, "Allocated a window of %dKiB", dev->win_size >> 10); /* Get write protect status */ - CS_CHECK(GetStatus, pcmcia_get_status(link->handle, &status)); + CS_CHECK(GetStatus, pcmcia_get_status(link, &status)); DEBUG(2, "status value: 0x%x window handle = 0x%8.8lx", status.CardState, (unsigned long)link->win); dev->win_base = ioremap(req.Base, req.Size); if(!dev->win_base) { err("ioremap(%lu, %u) failed", req.Base, req.Size); pcmciamtd_release(link); - return; + return -ENODEV; } DEBUG(1, "mapped window dev = %p req.base = 0x%lx base = %p size = 0x%x", dev, req.Base, dev->win_base, req.Size); @@ -584,17 +580,14 @@ static void pcmciamtd_config(dev_link_t *link) dev->pcmcia_map.map_priv_2 = (unsigned long)link->win; DEBUG(2, "Getting configuration"); - CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(link->handle, &t)); + CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(link, &t)); DEBUG(2, "Vcc = %d Vpp1 = %d Vpp2 = %d", t.Vcc, t.Vpp1, t.Vpp2); dev->vpp = (vpp) ? vpp : t.Vpp1; link->conf.Attributes = 0; - link->conf.Vcc = t.Vcc; if(setvpp == 2) { - link->conf.Vpp1 = dev->vpp; - link->conf.Vpp2 = dev->vpp; + link->conf.Vpp = dev->vpp; } else { - link->conf.Vpp1 = 0; - link->conf.Vpp2 = 0; + link->conf.Vpp = 0; } link->conf.IntType = INT_MEMORY; @@ -606,9 +599,10 @@ static void pcmciamtd_config(dev_link_t *link) link->conf.ConfigIndex = 0; link->conf.Present = t.Present; DEBUG(2, "Setting Configuration"); - ret = pcmcia_request_configuration(link->handle, &link->conf); + ret = pcmcia_request_configuration(link, &link->conf); if(ret != CS_SUCCESS) { - cs_error(link->handle, RequestConfiguration, ret); + cs_error(link, RequestConfiguration, ret); + return -ENODEV; } if(mem_type == 1) { @@ -616,7 +610,7 @@ static void pcmciamtd_config(dev_link_t *link) } else if(mem_type == 2) { mtd = do_map_probe("map_rom", &dev->pcmcia_map); } else { - for(i = 0; i < sizeof(probes) / sizeof(char *); i++) { + for(i = 0; i < ARRAY_SIZE(probes); i++) { DEBUG(1, "Trying %s", probes[i]); mtd = do_map_probe(probes[i], &dev->pcmcia_map); if(mtd) @@ -629,7 +623,7 @@ static void pcmciamtd_config(dev_link_t *link) if(!mtd) { DEBUG(1, "Cant find an MTD"); pcmciamtd_release(link); - return; + return -ENODEV; } dev->mtd_info = mtd; @@ -654,7 +648,6 @@ static void pcmciamtd_config(dev_link_t *link) use the faster non-remapping read/write functions */ if(mtd->size <= dev->win_size) { DEBUG(1, "Using non remapping memory functions"); - dev->pcmcia_map.map_priv_1 = (unsigned long)&(dev->link.state); dev->pcmcia_map.map_priv_2 = (unsigned long)dev->win_base; if (dev->pcmcia_map.bankwidth == 1) { dev->pcmcia_map.read = pcmcia_read8; @@ -672,19 +665,18 @@ static void pcmciamtd_config(dev_link_t *link) dev->mtd_info = NULL; err("Couldnt register MTD device"); pcmciamtd_release(link); - return; + return -ENODEV; } snprintf(dev->node.dev_name, sizeof(dev->node.dev_name), "mtd%d", mtd->index); info("mtd%d: %s", mtd->index, mtd->name); - link->state &= ~DEV_CONFIG_PENDING; - link->dev = &dev->node; - return; + link->dev_node = &dev->node; + return 0; cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); err("CS Error, exiting"); pcmciamtd_release(link); - return; + return -ENODEV; } @@ -713,21 +705,18 @@ static int pcmciamtd_resume(struct pcmcia_device *dev) * when the device is released. */ -static void pcmciamtd_detach(struct pcmcia_device *p_dev) +static void pcmciamtd_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); + struct pcmciamtd_dev *dev = link->priv; DEBUG(3, "link=0x%p", link); - if(link->state & DEV_CONFIG) { - struct pcmciamtd_dev *dev = link->priv; - if(dev->mtd_info) { - del_mtd_device(dev->mtd_info); - info("mtd%d: Removed", dev->mtd_info->index); - } - - pcmciamtd_release(link); + if(dev->mtd_info) { + del_mtd_device(dev->mtd_info); + info("mtd%d: Removed", dev->mtd_info->index); } + + pcmciamtd_release(link); } @@ -736,10 +725,9 @@ static void pcmciamtd_detach(struct pcmcia_device *p_dev) * with Card Services. */ -static int pcmciamtd_attach(struct pcmcia_device *p_dev) +static int pcmciamtd_probe(struct pcmcia_device *link) { struct pcmciamtd_dev *dev; - dev_link_t *link; /* Create new memory card device */ dev = kmalloc(sizeof(*dev), GFP_KERNEL); @@ -747,20 +735,13 @@ static int pcmciamtd_attach(struct pcmcia_device *p_dev) DEBUG(1, "dev=0x%p", dev); memset(dev, 0, sizeof(*dev)); - link = &dev->link; + dev->p_dev = link; link->priv = dev; link->conf.Attributes = 0; link->conf.IntType = INT_MEMORY; - link->next = NULL; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - pcmciamtd_config(link); - - return 0; + return pcmciamtd_config(link); } static struct pcmcia_device_id pcmciamtd_ids[] = { @@ -794,7 +775,7 @@ static struct pcmcia_driver pcmciamtd_driver = { .drv = { .name = "pcmciamtd" }, - .probe = pcmciamtd_attach, + .probe = pcmciamtd_probe, .remove = pcmciamtd_detach, .owner = THIS_MODULE, .id_table = pcmciamtd_ids, diff --git a/drivers/mtd/maps/redwood.c b/drivers/mtd/maps/redwood.c index 5b76ed886185..50b14033613f 100644 --- a/drivers/mtd/maps/redwood.c +++ b/drivers/mtd/maps/redwood.c @@ -121,8 +121,7 @@ struct map_info redwood_flash_map = { }; -#define NUM_REDWOOD_FLASH_PARTITIONS \ - (sizeof(redwood_flash_partitions)/sizeof(redwood_flash_partitions[0])) +#define NUM_REDWOOD_FLASH_PARTITIONS ARRAY_SIZE(redwood_flash_partitions) static struct mtd_info *redwood_mtd; diff --git a/drivers/mtd/maps/sbc8240.c b/drivers/mtd/maps/sbc8240.c index 225cdd9ba5b2..350286dc1d2e 100644 --- a/drivers/mtd/maps/sbc8240.c +++ b/drivers/mtd/maps/sbc8240.c @@ -66,7 +66,7 @@ static struct map_info sbc8240_map[2] = { } }; -#define NUM_FLASH_BANKS (sizeof(sbc8240_map) / sizeof(struct map_info)) +#define NUM_FLASH_BANKS ARRAY_SIZE(sbc8240_map) /* * The following defines the partition layout of SBC8240 boards. @@ -125,8 +125,6 @@ static struct mtd_partition sbc8240_fs_partitions [] = { } }; -#define NB_OF(x) (sizeof (x) / sizeof (x[0])) - /* trivial struct to describe partition information */ struct mtd_part_def { @@ -190,10 +188,10 @@ int __init init_sbc8240_mtd (void) #ifdef CONFIG_MTD_PARTITIONS sbc8240_part_banks[0].mtd_part = sbc8240_uboot_partitions; sbc8240_part_banks[0].type = "static image"; - sbc8240_part_banks[0].nums = NB_OF(sbc8240_uboot_partitions); + sbc8240_part_banks[0].nums = ARRAY_SIZE(sbc8240_uboot_partitions); sbc8240_part_banks[1].mtd_part = sbc8240_fs_partitions; sbc8240_part_banks[1].type = "static file system"; - sbc8240_part_banks[1].nums = NB_OF(sbc8240_fs_partitions); + sbc8240_part_banks[1].nums = ARRAY_SIZE(sbc8240_fs_partitions); for (i = 0; i < NUM_FLASH_BANKS; i++) { diff --git a/drivers/mtd/maps/sc520cdp.c b/drivers/mtd/maps/sc520cdp.c index ed92afadd8a9..e8c130e1efd3 100644 --- a/drivers/mtd/maps/sc520cdp.c +++ b/drivers/mtd/maps/sc520cdp.c @@ -107,7 +107,7 @@ static struct map_info sc520cdp_map[] = { }, }; -#define NUM_FLASH_BANKS (sizeof(sc520cdp_map)/sizeof(struct map_info)) +#define NUM_FLASH_BANKS ARRAY_SIZE(sc520cdp_map) static struct mtd_info *mymtd[NUM_FLASH_BANKS]; static struct mtd_info *merged_mtd; diff --git a/drivers/mtd/maps/scx200_docflash.c b/drivers/mtd/maps/scx200_docflash.c index 2c91dff8bb60..28b8a571a91a 100644 --- a/drivers/mtd/maps/scx200_docflash.c +++ b/drivers/mtd/maps/scx200_docflash.c @@ -70,7 +70,7 @@ static struct mtd_partition partition_info[] = { .size = 0x80000 }, }; -#define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0])) +#define NUM_PARTITIONS ARRAY_SIZE(partition_info) #endif diff --git a/drivers/mtd/maps/sharpsl-flash.c b/drivers/mtd/maps/sharpsl-flash.c index 999f4bb3d845..12fe53c0d2fc 100644 --- a/drivers/mtd/maps/sharpsl-flash.c +++ b/drivers/mtd/maps/sharpsl-flash.c @@ -49,8 +49,6 @@ static struct mtd_partition sharpsl_partitions[1] = { } }; -#define NB_OF(x) (sizeof(x)/sizeof(x[0])) - int __init init_sharpsl(void) { struct mtd_partition *parts; @@ -92,7 +90,7 @@ int __init init_sharpsl(void) } parts = sharpsl_partitions; - nb_parts = NB_OF(sharpsl_partitions); + nb_parts = ARRAY_SIZE(sharpsl_partitions); printk(KERN_NOTICE "Using %s partision definition\n", part_type); add_mtd_partitions(mymtd, parts, nb_parts); diff --git a/drivers/mtd/maps/ts5500_flash.c b/drivers/mtd/maps/ts5500_flash.c index 4b372bcb17f1..a7422c200567 100644 --- a/drivers/mtd/maps/ts5500_flash.c +++ b/drivers/mtd/maps/ts5500_flash.c @@ -64,7 +64,7 @@ static struct mtd_partition ts5500_partitions[] = { } }; -#define NUM_PARTITIONS (sizeof(ts5500_partitions)/sizeof(struct mtd_partition)) +#define NUM_PARTITIONS ARRAY_SIZE(ts5500_partitions) static struct mtd_info *mymtd; diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c index 79d92808b766..f7264dc2ac9b 100644 --- a/drivers/mtd/maps/uclinux.c +++ b/drivers/mtd/maps/uclinux.c @@ -37,7 +37,7 @@ struct mtd_partition uclinux_romfs[] = { { .name = "ROMfs" } }; -#define NUM_PARTITIONS (sizeof(uclinux_romfs) / sizeof(uclinux_romfs[0])) +#define NUM_PARTITIONS ARRAY_SIZE(uclinux_romfs) /****************************************************************************/ diff --git a/drivers/mtd/maps/vmax301.c b/drivers/mtd/maps/vmax301.c index e0063941c0df..b3e487395435 100644 --- a/drivers/mtd/maps/vmax301.c +++ b/drivers/mtd/maps/vmax301.c @@ -182,7 +182,7 @@ int __init init_vmax301(void) } } - if (!vmax_mtd[1] && !vmax_mtd[2]) { + if (!vmax_mtd[0] && !vmax_mtd[1]) { iounmap((void *)iomapadr); return -ENXIO; } diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 840dd66ce2dc..458d3c8ae1ee 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -19,12 +19,12 @@ #include #include #include -#include +#include #include static LIST_HEAD(blktrans_majors); -extern struct semaphore mtd_table_mutex; +extern struct mutex mtd_table_mutex; extern struct mtd_info *mtd_table[]; struct mtd_blkcore_priv { @@ -122,9 +122,9 @@ static int mtd_blktrans_thread(void *arg) spin_unlock_irq(rq->queue_lock); - down(&dev->sem); + mutex_lock(&dev->lock); res = do_blktrans_request(tr, dev, req); - up(&dev->sem); + mutex_unlock(&dev->lock); spin_lock_irq(rq->queue_lock); @@ -235,8 +235,8 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) int last_devnum = -1; struct gendisk *gd; - if (!down_trylock(&mtd_table_mutex)) { - up(&mtd_table_mutex); + if (!!mutex_trylock(&mtd_table_mutex)) { + mutex_unlock(&mtd_table_mutex); BUG(); } @@ -267,7 +267,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) return -EBUSY; } - init_MUTEX(&new->sem); + mutex_init(&new->lock); list_add_tail(&new->list, &tr->devs); added: if (!tr->writesect) @@ -313,8 +313,8 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) { - if (!down_trylock(&mtd_table_mutex)) { - up(&mtd_table_mutex); + if (!!mutex_trylock(&mtd_table_mutex)) { + mutex_unlock(&mtd_table_mutex); BUG(); } @@ -378,14 +378,14 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr) memset(tr->blkcore_priv, 0, sizeof(*tr->blkcore_priv)); - down(&mtd_table_mutex); + mutex_lock(&mtd_table_mutex); ret = register_blkdev(tr->major, tr->name); if (ret) { printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n", tr->name, tr->major, ret); kfree(tr->blkcore_priv); - up(&mtd_table_mutex); + mutex_unlock(&mtd_table_mutex); return ret; } spin_lock_init(&tr->blkcore_priv->queue_lock); @@ -396,7 +396,7 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr) if (!tr->blkcore_priv->rq) { unregister_blkdev(tr->major, tr->name); kfree(tr->blkcore_priv); - up(&mtd_table_mutex); + mutex_unlock(&mtd_table_mutex); return -ENOMEM; } @@ -407,7 +407,7 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr) blk_cleanup_queue(tr->blkcore_priv->rq); unregister_blkdev(tr->major, tr->name); kfree(tr->blkcore_priv); - up(&mtd_table_mutex); + mutex_unlock(&mtd_table_mutex); return ret; } @@ -419,7 +419,7 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr) tr->add_mtd(tr, mtd_table[i]); } - up(&mtd_table_mutex); + mutex_unlock(&mtd_table_mutex); return 0; } @@ -428,7 +428,7 @@ int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr) { struct list_head *this, *next; - down(&mtd_table_mutex); + mutex_lock(&mtd_table_mutex); /* Clean up the kernel thread */ tr->blkcore_priv->exiting = 1; @@ -446,7 +446,7 @@ int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr) blk_cleanup_queue(tr->blkcore_priv->rq); unregister_blkdev(tr->major, tr->name); - up(&mtd_table_mutex); + mutex_unlock(&mtd_table_mutex); kfree(tr->blkcore_priv); diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index e84756644fd1..2cef280e388c 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -19,11 +19,13 @@ #include #include +#include + static struct mtdblk_dev { struct mtd_info *mtd; int count; - struct semaphore cache_sem; + struct mutex cache_mutex; unsigned char *cache_data; unsigned long cache_offset; unsigned int cache_size; @@ -284,7 +286,7 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd) mtdblk->count = 1; mtdblk->mtd = mtd; - init_MUTEX (&mtdblk->cache_sem); + mutex_init(&mtdblk->cache_mutex); mtdblk->cache_state = STATE_EMPTY; if ((mtdblk->mtd->flags & MTD_CAP_RAM) != MTD_CAP_RAM && mtdblk->mtd->erasesize) { @@ -306,9 +308,9 @@ static int mtdblock_release(struct mtd_blktrans_dev *mbd) DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n"); - down(&mtdblk->cache_sem); + mutex_lock(&mtdblk->cache_mutex); write_cached_data(mtdblk); - up(&mtdblk->cache_sem); + mutex_unlock(&mtdblk->cache_mutex); if (!--mtdblk->count) { /* It was the last usage. Free the device */ @@ -327,9 +329,9 @@ static int mtdblock_flush(struct mtd_blktrans_dev *dev) { struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; - down(&mtdblk->cache_sem); + mutex_lock(&mtdblk->cache_mutex); write_cached_data(mtdblk); - up(&mtdblk->cache_sem); + mutex_unlock(&mtdblk->cache_mutex); if (mtdblk->mtd->sync) mtdblk->mtd->sync(mtdblk->mtd); diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index dade02ab0687..9905870f56e5 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -19,15 +19,13 @@ #include #include #include -#ifdef CONFIG_PROC_FS #include -#endif #include /* These are exported solely for the purpose of mtd_blkdevs.c. You should not use them for _anything_ else */ -DECLARE_MUTEX(mtd_table_mutex); +DEFINE_MUTEX(mtd_table_mutex); struct mtd_info *mtd_table[MAX_MTD_DEVICES]; EXPORT_SYMBOL_GPL(mtd_table_mutex); @@ -49,7 +47,7 @@ int add_mtd_device(struct mtd_info *mtd) { int i; - down(&mtd_table_mutex); + mutex_lock(&mtd_table_mutex); for (i=0; i < MAX_MTD_DEVICES; i++) if (!mtd_table[i]) { @@ -67,7 +65,7 @@ int add_mtd_device(struct mtd_info *mtd) not->add(mtd); } - up(&mtd_table_mutex); + mutex_unlock(&mtd_table_mutex); /* We _know_ we aren't being removed, because our caller is still holding us here. So none of this try_ nonsense, and no bitching about it @@ -76,7 +74,7 @@ int add_mtd_device(struct mtd_info *mtd) return 0; } - up(&mtd_table_mutex); + mutex_unlock(&mtd_table_mutex); return 1; } @@ -94,7 +92,7 @@ int del_mtd_device (struct mtd_info *mtd) { int ret; - down(&mtd_table_mutex); + mutex_lock(&mtd_table_mutex); if (mtd_table[mtd->index] != mtd) { ret = -ENODEV; @@ -118,7 +116,7 @@ int del_mtd_device (struct mtd_info *mtd) ret = 0; } - up(&mtd_table_mutex); + mutex_unlock(&mtd_table_mutex); return ret; } @@ -135,7 +133,7 @@ void register_mtd_user (struct mtd_notifier *new) { int i; - down(&mtd_table_mutex); + mutex_lock(&mtd_table_mutex); list_add(&new->list, &mtd_notifiers); @@ -145,7 +143,7 @@ void register_mtd_user (struct mtd_notifier *new) if (mtd_table[i]) new->add(mtd_table[i]); - up(&mtd_table_mutex); + mutex_unlock(&mtd_table_mutex); } /** @@ -162,7 +160,7 @@ int unregister_mtd_user (struct mtd_notifier *old) { int i; - down(&mtd_table_mutex); + mutex_lock(&mtd_table_mutex); module_put(THIS_MODULE); @@ -171,7 +169,7 @@ int unregister_mtd_user (struct mtd_notifier *old) old->remove(mtd_table[i]); list_del(&old->list); - up(&mtd_table_mutex); + mutex_unlock(&mtd_table_mutex); return 0; } @@ -193,7 +191,7 @@ struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) struct mtd_info *ret = NULL; int i; - down(&mtd_table_mutex); + mutex_lock(&mtd_table_mutex); if (num == -1) { for (i=0; i< MAX_MTD_DEVICES; i++) @@ -211,7 +209,7 @@ struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) if (ret) ret->usecount++; - up(&mtd_table_mutex); + mutex_unlock(&mtd_table_mutex); return ret; } @@ -219,9 +217,9 @@ void put_mtd_device(struct mtd_info *mtd) { int c; - down(&mtd_table_mutex); + mutex_lock(&mtd_table_mutex); c = --mtd->usecount; - up(&mtd_table_mutex); + mutex_unlock(&mtd_table_mutex); BUG_ON(c < 0); module_put(mtd->owner); @@ -296,10 +294,11 @@ EXPORT_SYMBOL(unregister_mtd_user); EXPORT_SYMBOL(default_mtd_writev); EXPORT_SYMBOL(default_mtd_readv); +#ifdef CONFIG_PROC_FS + /*====================================================================*/ /* Support for /proc/mtd */ -#ifdef CONFIG_PROC_FS static struct proc_dir_entry *proc_mtd; static inline int mtd_proc_info (char *buf, int i) @@ -319,7 +318,7 @@ static int mtd_read_proc (char *page, char **start, off_t off, int count, int len, l, i; off_t begin = 0; - down(&mtd_table_mutex); + mutex_lock(&mtd_table_mutex); len = sprintf(page, "dev: size erasesize name\n"); for (i=0; i< MAX_MTD_DEVICES; i++) { @@ -337,38 +336,34 @@ static int mtd_read_proc (char *page, char **start, off_t off, int count, *eof = 1; done: - up(&mtd_table_mutex); + mutex_unlock(&mtd_table_mutex); if (off >= len+begin) return 0; *start = page + (off-begin); return ((count < begin+len-off) ? count : begin+len-off); } -#endif /* CONFIG_PROC_FS */ - /*====================================================================*/ /* Init code */ static int __init init_mtd(void) { -#ifdef CONFIG_PROC_FS if ((proc_mtd = create_proc_entry( "mtd", 0, NULL ))) proc_mtd->read_proc = mtd_read_proc; -#endif return 0; } static void __exit cleanup_mtd(void) { -#ifdef CONFIG_PROC_FS if (proc_mtd) remove_proc_entry( "mtd", NULL); -#endif } module_init(init_mtd); module_exit(cleanup_mtd); +#endif /* CONFIG_PROC_FS */ + MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Woodhouse "); diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 1fc4c134d939..cfe288a6e853 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -178,17 +178,16 @@ config MTD_NAND_DISKONCHIP_BBTWRITE Even if you leave this disabled, you can enable BBT writes at module load time (assuming you build diskonchip as a module) with the module parameter "inftl_bbt_write=1". - - config MTD_NAND_SHARPSL - bool "Support for NAND Flash on Sharp SL Series (C7xx + others)" - depends on MTD_NAND && ARCH_PXA - - config MTD_NAND_NANDSIM - bool "Support for NAND Flash Simulator" - depends on MTD_NAND && MTD_PARTITIONS +config MTD_NAND_SHARPSL + tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)" + depends on MTD_NAND && ARCH_PXA + +config MTD_NAND_NANDSIM + tristate "Support for NAND Flash Simulator" + depends on MTD_NAND && MTD_PARTITIONS help The simulator may simulate verious NAND flash chips for the MTD nand layer. - + endmenu diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 201e1362da14..bde3550910a2 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -55,8 +55,6 @@ static const struct mtd_partition partition_info[] = { .size = MTDPART_SIZ_FULL } }; -#define NB_OF(x) (sizeof(x)/sizeof(x[0])) - /** * au_read_byte - read one byte from the chip @@ -462,7 +460,7 @@ int __init au1xxx_nand_init (void) } /* Register the partitions */ - add_mtd_partitions(au1550_mtd, partition_info, NB_OF(partition_info)); + add_mtd_partitions(au1550_mtd, partition_info, ARRAY_SIZE(partition_info)); return 0; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 5d222460b42a..95e96fa1fceb 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -80,6 +80,7 @@ #include #include #include +#include #include #ifdef CONFIG_MTD_PARTITIONS @@ -515,6 +516,8 @@ static int nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip, i return nand_isbad_bbt (mtd, ofs, allowbbt); } +DEFINE_LED_TRIGGER(nand_led_trigger); + /* * Wait for the ready pin, after a command * The timeout is catched later. @@ -524,12 +527,14 @@ static void nand_wait_ready(struct mtd_info *mtd) struct nand_chip *this = mtd->priv; unsigned long timeo = jiffies + 2; + led_trigger_event(nand_led_trigger, LED_FULL); /* wait until command is processed or timeout occures */ do { if (this->dev_ready(mtd)) - return; + break; touch_softlockup_watchdog(); } while (time_before(jiffies, timeo)); + led_trigger_event(nand_led_trigger, LED_OFF); } /** @@ -817,6 +822,8 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state) else timeo += (HZ * 20) / 1000; + led_trigger_event(nand_led_trigger, LED_FULL); + /* Apply this short delay always to ensure that we do wait tWB in * any case on any machine. */ ndelay (100); @@ -840,6 +847,8 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state) } cond_resched(); } + led_trigger_event(nand_led_trigger, LED_OFF); + status = (int) this->read_byte(mtd); return status; } @@ -2724,6 +2733,21 @@ void nand_release (struct mtd_info *mtd) EXPORT_SYMBOL_GPL (nand_scan); EXPORT_SYMBOL_GPL (nand_release); + +static int __init nand_base_init(void) +{ + led_trigger_register_simple("nand-disk", &nand_led_trigger); + return 0; +} + +static void __exit nand_base_exit(void) +{ + led_trigger_unregister_simple(nand_led_trigger); +} + +module_init(nand_base_init); +module_exit(nand_base_exit); + MODULE_LICENSE ("GPL"); MODULE_AUTHOR ("Steven J. Hill , Thomas Gleixner "); MODULE_DESCRIPTION ("Generic NAND flash driver code"); diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c index 8815c8dbef2d..c077d2ec9cdd 100644 --- a/drivers/mtd/redboot.c +++ b/drivers/mtd/redboot.c @@ -85,10 +85,6 @@ static int parse_redboot_partitions(struct mtd_info *master, numslots = (master->erasesize / sizeof(struct fis_image_desc)); for (i = 0; i < numslots; i++) { - if (buf[i].name[0] == 0xff) { - i = numslots; - break; - } if (!memcmp(buf[i].name, "FIS directory", 14)) { /* This is apparently the FIS directory entry for the * FIS directory itself. The FIS directory size is @@ -128,7 +124,7 @@ static int parse_redboot_partitions(struct mtd_info *master, struct fis_list *new_fl, **prev; if (buf[i].name[0] == 0xff) - break; + continue; if (!redboot_checksum(&buf[i])) break; diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c index 70f63891b19c..274b0138d442 100644 --- a/drivers/net/3c59x.c +++ b/drivers/net/3c59x.c @@ -788,7 +788,7 @@ struct vortex_private { int options; /* User-settable misc. driver options. */ unsigned int media_override:4, /* Passed-in media type. */ default_media:4, /* Read from the EEPROM/Wn3_Config. */ - full_duplex:1, force_fd:1, autoselect:1, + full_duplex:1, autoselect:1, bus_master:1, /* Vortex can only do a fragment bus-m. */ full_bus_master_tx:1, full_bus_master_rx:2, /* Boomerang */ flow_ctrl:1, /* Use 802.3x flow control (PAUSE only) */ @@ -1633,12 +1633,6 @@ vortex_set_duplex(struct net_device *dev) ((vp->full_duplex && vp->flow_ctrl && vp->partner_flow_ctrl) ? 0x100 : 0), ioaddr + Wn3_MAC_Ctrl); - - issue_and_wait(dev, TxReset); - /* - * Don't reset the PHY - that upsets autonegotiation during DHCP operations. - */ - issue_and_wait(dev, RxReset|0x04); } static void vortex_check_media(struct net_device *dev, unsigned int init) @@ -1663,7 +1657,7 @@ vortex_up(struct net_device *dev) struct vortex_private *vp = netdev_priv(dev); void __iomem *ioaddr = vp->ioaddr; unsigned int config; - int i; + int i, mii_reg1, mii_reg5; if (VORTEX_PCI(vp)) { pci_set_power_state(VORTEX_PCI(vp), PCI_D0); /* Go active */ @@ -1723,14 +1717,23 @@ vortex_up(struct net_device *dev) printk(KERN_DEBUG "vortex_up(): writing 0x%x to InternalConfig\n", config); iowrite32(config, ioaddr + Wn3_Config); - netif_carrier_off(dev); if (dev->if_port == XCVR_MII || dev->if_port == XCVR_NWAY) { EL3WINDOW(4); + mii_reg1 = mdio_read(dev, vp->phys[0], MII_BMSR); + mii_reg5 = mdio_read(dev, vp->phys[0], MII_LPA); + vp->partner_flow_ctrl = ((mii_reg5 & 0x0400) != 0); + vortex_check_media(dev, 1); } else vortex_set_duplex(dev); + issue_and_wait(dev, TxReset); + /* + * Don't reset the PHY - that upsets autonegotiation during DHCP operations. + */ + issue_and_wait(dev, RxReset|0x04); + iowrite16(SetStatusEnb | 0x00, ioaddr + EL3_CMD); @@ -2083,16 +2086,14 @@ vortex_error(struct net_device *dev, int status) } if (tx_status & 0x14) vp->stats.tx_fifo_errors++; if (tx_status & 0x38) vp->stats.tx_aborted_errors++; + if (tx_status & 0x08) vp->xstats.tx_max_collisions++; iowrite8(0, ioaddr + TxStatus); if (tx_status & 0x30) { /* txJabber or txUnderrun */ do_tx_reset = 1; - } else if (tx_status & 0x08) { /* maxCollisions */ - vp->xstats.tx_max_collisions++; - if (vp->drv_flags & MAX_COLLISION_RESET) { - do_tx_reset = 1; - reset_mask = 0x0108; /* Reset interface logic, but not download logic */ - } - } else { /* Merely re-enable the transmitter. */ + } else if ((tx_status & 0x08) && (vp->drv_flags & MAX_COLLISION_RESET)) { /* maxCollisions */ + do_tx_reset = 1; + reset_mask = 0x0108; /* Reset interface logic, but not download logic */ + } else { /* Merely re-enable the transmitter. */ iowrite16(TxEnable, ioaddr + EL3_CMD); } } diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c index ce99845d8266..066e22b01a94 100644 --- a/drivers/net/8139cp.c +++ b/drivers/net/8139cp.c @@ -539,8 +539,7 @@ rx_status_loop: unsigned buflen; skb = cp->rx_skb[rx_tail].skb; - if (!skb) - BUG(); + BUG_ON(!skb); desc = &cp->rx_ring[rx_tail]; status = le32_to_cpu(desc->opts1); @@ -723,8 +722,7 @@ static void cp_tx (struct cp_private *cp) break; skb = cp->tx_skb[tx_tail].skb; - if (!skb) - BUG(); + BUG_ON(!skb); pci_unmap_single(cp->pdev, cp->tx_skb[tx_tail].mapping, cp->tx_skb[tx_tail].len, PCI_DMA_TODEVICE); @@ -1550,8 +1548,7 @@ static void cp_get_ethtool_stats (struct net_device *dev, tmp_stats[i++] = le16_to_cpu(nic_stats->tx_abort); tmp_stats[i++] = le16_to_cpu(nic_stats->tx_underrun); tmp_stats[i++] = cp->cp_stats.rx_frags; - if (i != CP_NUM_STATS) - BUG(); + BUG_ON(i != CP_NUM_STATS); pci_free_consistent(cp->pdev, sizeof(*nic_stats), nic_stats, dma); } @@ -1856,8 +1853,7 @@ static void cp_remove_one (struct pci_dev *pdev) struct net_device *dev = pci_get_drvdata(pdev); struct cp_private *cp = netdev_priv(dev); - if (!dev) - BUG(); + BUG_ON(!dev); unregister_netdev(dev); iounmap(cp->regs); if (cp->wol_enabled) pci_set_power_state (pdev, PCI_D0); diff --git a/drivers/net/8390.h b/drivers/net/8390.h index 599b68d8c45f..51e39dcd0603 100644 --- a/drivers/net/8390.h +++ b/drivers/net/8390.h @@ -134,7 +134,7 @@ struct ei_device { #define inb_p(_p) inb(_p) #define outb_p(_v,_p) outb(_v,_p) -#elif defined(CONFIG_NET_CBUS) || defined(CONFIG_NE_H8300) || defined(CONFIG_NE_H8300_MODULE) +#elif defined(CONFIG_NE_H8300) || defined(CONFIG_NE_H8300_MODULE) #define EI_SHIFT(x) (ei_local->reg_offset[x]) #else #define EI_SHIFT(x) (x) diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index e20b849a22e8..bdaaad8f2123 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2313,13 +2313,11 @@ config S2IO_NAPI endmenu -if !UML source "drivers/net/tokenring/Kconfig" source "drivers/net/wireless/Kconfig" source "drivers/net/pcmcia/Kconfig" -endif source "drivers/net/wan/Kconfig" diff --git a/drivers/net/acenic_firmware.h b/drivers/net/acenic_firmware.h index 6d625d595622..d7882dd783c8 100644 --- a/drivers/net/acenic_firmware.h +++ b/drivers/net/acenic_firmware.h @@ -4397,7 +4397,7 @@ static u32 tigonFwText[(MAX_TEXT_LEN/4) + 1] __devinitdata = { 0x3c010001, 0x220821, 0xac317e30, 0x8fbf0024, 0x8fb40020, 0x8fb3001c, 0x8fb20018, 0x8fb10014, 0x8fb00010, 0x3e00008, 0x27bd0028, 0x0 }; -static u32 tigonFwRodata[(MAX_RODATA_LEN/4) + 1] __initdata = { +static u32 tigonFwRodata[(MAX_RODATA_LEN/4) + 1] __devinitdata = { 0x24486561, 0x6465723a, 0x202f7072, 0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 0x2f2e2f6e, 0x69632f66, 0x772f636f, 0x6d6d6f6e, @@ -4571,7 +4571,7 @@ static u32 tigonFwRodata[(MAX_RODATA_LEN/4) + 1] __initdata = { 0x0, 0x14c38, 0x14c38, 0x14b80, 0x14bc4, 0x14c38, 0x14c38, 0x0, 0x0, 0x0 }; -static u32 tigonFwData[(MAX_DATA_LEN/4) + 1] __initdata = { +static u32 tigonFwData[(MAX_DATA_LEN/4) + 1] __devinitdata = { 0x416c7465, 0x6f6e2041, 0x63654e49, 0x43205600, 0x416c7465, 0x6f6e2041, 0x63654e49, 0x43205600, 0x42424242, @@ -4612,7 +4612,7 @@ static u32 tigonFwData[(MAX_DATA_LEN/4) + 1] __initdata = { #define tigon2FwSbssLen 0xcc #define tigon2FwBssAddr 0x00016f50 #define tigon2FwBssLen 0x20c0 -static u32 tigon2FwText[(MAX_TEXT_LEN/4) + 1] __initdata = { +static u32 tigon2FwText[(MAX_TEXT_LEN/4) + 1] __devinitdata = { 0x0, 0x10000003, 0x0, 0xd, 0xd, 0x3c1d0001, 0x8fbd6d20, 0x3a0f021, 0x3c100000, @@ -9154,7 +9154,7 @@ static u32 tigon2FwText[(MAX_TEXT_LEN/4) + 1] __initdata = { 0x24020001, 0x8f430328, 0x1021, 0x24630001, 0x3e00008, 0xaf430328, 0x3e00008, 0x0, 0x0, 0x0, 0x0, 0x0 }; -static u32 tigon2FwRodata[(MAX_RODATA_LEN/4) + 1] __initdata = { +static u32 tigon2FwRodata[(MAX_RODATA_LEN/4) + 1] __devinitdata = { 0x24486561, 0x6465723a, 0x202f7072, 0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765, 0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f, @@ -9425,7 +9425,7 @@ static u32 tigon2FwRodata[(MAX_RODATA_LEN/4) + 1] __initdata = { 0x14ed8, 0x14b8c, 0x14bd8, 0x14c24, 0x14ed8, 0x7365746d, 0x61636163, 0x74000000, 0x0, 0x0 }; -static u32 tigon2FwData[(MAX_DATA_LEN/4) + 1] __initdata = { +static u32 tigon2FwData[(MAX_DATA_LEN/4) + 1] __devinitdata = { 0x1, 0x1, 0x1, 0xc001fc, 0x3ffc, 0xc00000, 0x416c7465, 0x6f6e2041, 0x63654e49, diff --git a/drivers/net/arcnet/arcnet.c b/drivers/net/arcnet/arcnet.c index 64e2caf3083d..fabc0607b0f1 100644 --- a/drivers/net/arcnet/arcnet.c +++ b/drivers/net/arcnet/arcnet.c @@ -765,8 +765,7 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id, struct pt_regs *regs) BUGMSG(D_DURING, "in arcnet_interrupt\n"); lp = dev->priv; - if (!lp) - BUG(); + BUG_ON(!lp); spin_lock(&lp->lock); diff --git a/drivers/net/arcnet/com90xx.c b/drivers/net/arcnet/com90xx.c index 43150b2bd13f..0d45553ff75c 100644 --- a/drivers/net/arcnet/com90xx.c +++ b/drivers/net/arcnet/com90xx.c @@ -125,11 +125,11 @@ static void __init com90xx_probe(void) if (!io && !irq && !shmem && !*device && com90xx_skip_probe) return; - shmems = kzalloc(((0x10000-0xa0000) / 0x800) * sizeof(unsigned long), + shmems = kzalloc(((0x100000-0xa0000) / 0x800) * sizeof(unsigned long), GFP_KERNEL); if (!shmems) return; - iomem = kzalloc(((0x10000-0xa0000) / 0x800) * sizeof(void __iomem *), + iomem = kzalloc(((0x100000-0xa0000) / 0x800) * sizeof(void __iomem *), GFP_KERNEL); if (!iomem) { kfree(shmems); diff --git a/drivers/net/b44.c b/drivers/net/b44.c index c3267e4e1bb0..c4e12b5cbb92 100644 --- a/drivers/net/b44.c +++ b/drivers/net/b44.c @@ -608,8 +608,7 @@ static void b44_tx(struct b44 *bp) struct ring_info *rp = &bp->tx_buffers[cons]; struct sk_buff *skb = rp->skb; - if (unlikely(skb == NULL)) - BUG(); + BUG_ON(skb == NULL); pci_unmap_single(bp->pdev, pci_unmap_addr(rp, mapping), @@ -1339,6 +1338,9 @@ static int b44_set_mac_addr(struct net_device *dev, void *p) if (netif_running(dev)) return -EBUSY; + if (!is_valid_ether_addr(addr->sa_data)) + return -EINVAL; + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); spin_lock_irq(&bp->lock); @@ -1876,6 +1878,12 @@ static int __devinit b44_get_invariants(struct b44 *bp) bp->dev->dev_addr[3] = eeprom[80]; bp->dev->dev_addr[4] = eeprom[83]; bp->dev->dev_addr[5] = eeprom[82]; + + if (!is_valid_ether_addr(&bp->dev->dev_addr[0])){ + printk(KERN_ERR PFX "Invalid MAC address found in EEPROM\n"); + return -EINVAL; + } + memcpy(bp->dev->perm_addr, bp->dev->dev_addr, bp->dev->addr_len); bp->phy_addr = eeprom[90] & 0x1f; @@ -2033,6 +2041,11 @@ static int __devinit b44_init_one(struct pci_dev *pdev, pci_save_state(bp->pdev); + /* Chip reset provides power to the b44 MAC & PCI cores, which + * is necessary for MAC register access. + */ + b44_chip_reset(bp); + printk(KERN_INFO "%s: Broadcom 4400 10/100BaseT Ethernet ", dev->name); for (i = 0; i < 6; i++) printk("%2.2x%c", dev->dev_addr[i], diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index f3f5825469d6..6a407070c2e8 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -2294,6 +2294,34 @@ void bond_3ad_handle_link_change(struct slave *slave, char link) port->sm_vars |= AD_PORT_BEGIN; } +/* + * set link state for bonding master: if we have an active partnered + * aggregator, we're up, if not, we're down. Presumes that we cannot + * have an active aggregator if there are no slaves with link up. + * + * Called by bond_set_carrier(). Return zero if carrier state does not + * change, nonzero if it does. + */ +int bond_3ad_set_carrier(struct bonding *bond) +{ + struct aggregator *agg; + + agg = __get_active_agg(&(SLAVE_AD_INFO(bond->first_slave).aggregator)); + if (agg && MAC_ADDRESS_COMPARE(&agg->partner_system, &null_mac_addr)) { + if (!netif_carrier_ok(bond->dev)) { + netif_carrier_on(bond->dev); + return 1; + } + return 0; + } + + if (netif_carrier_ok(bond->dev)) { + netif_carrier_off(bond->dev); + return 1; + } + return 0; +} + /** * bond_3ad_get_active_agg_info - get information of the active aggregator * @bond: bonding struct to work on diff --git a/drivers/net/bonding/bond_3ad.h b/drivers/net/bonding/bond_3ad.h index 5ee2cef5b037..6ad5ad6e65d5 100644 --- a/drivers/net/bonding/bond_3ad.h +++ b/drivers/net/bonding/bond_3ad.h @@ -283,5 +283,6 @@ void bond_3ad_handle_link_change(struct slave *slave, char link); int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info); int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev); int bond_3ad_lacpdu_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type* ptype, struct net_device *orig_dev); +int bond_3ad_set_carrier(struct bonding *bond); #endif //__BOND_3AD_H__ diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index f13a539dc169..55d236726d11 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -558,6 +558,42 @@ out: /*------------------------------- Link status -------------------------------*/ +/* + * Set the carrier state for the master according to the state of its + * slaves. If any slaves are up, the master is up. In 802.3ad mode, + * do special 802.3ad magic. + * + * Returns zero if carrier state does not change, nonzero if it does. + */ +static int bond_set_carrier(struct bonding *bond) +{ + struct slave *slave; + int i; + + if (bond->slave_cnt == 0) + goto down; + + if (bond->params.mode == BOND_MODE_8023AD) + return bond_3ad_set_carrier(bond); + + bond_for_each_slave(bond, slave, i) { + if (slave->link == BOND_LINK_UP) { + if (!netif_carrier_ok(bond->dev)) { + netif_carrier_on(bond->dev); + return 1; + } + return 0; + } + } + +down: + if (netif_carrier_ok(bond->dev)) { + netif_carrier_off(bond->dev); + return 1; + } + return 0; +} + /* * Get link speed and duplex from the slave's base driver * using ethtool. If for some reason the call fails or the @@ -1074,10 +1110,24 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active) void bond_select_active_slave(struct bonding *bond) { struct slave *best_slave; + int rv; best_slave = bond_find_best_slave(bond); if (best_slave != bond->curr_active_slave) { bond_change_active_slave(bond, best_slave); + rv = bond_set_carrier(bond); + if (!rv) + return; + + if (netif_carrier_ok(bond->dev)) { + printk(KERN_INFO DRV_NAME + ": %s: first active interface up!\n", + bond->dev->name); + } else { + printk(KERN_INFO DRV_NAME ": %s: " + "now running without any active interface !\n", + bond->dev->name); + } } } @@ -1458,10 +1508,14 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) if (((!bond->curr_active_slave) || (bond->curr_active_slave->dev->priv_flags & IFF_SLAVE_INACTIVE)) && (new_slave->link != BOND_LINK_DOWN)) { - dprintk("This is the first active slave\n"); /* first slave or no active slave yet, and this link is OK, so make this interface the active one */ bond_change_active_slave(bond, new_slave); + printk(KERN_INFO DRV_NAME + ": %s: first active interface up!\n", + bond->dev->name); + netif_carrier_on(bond->dev); + } else { dprintk("This is just a backup slave\n"); bond_set_slave_inactive_flags(new_slave); @@ -1517,6 +1571,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) break; } /* switch(bond_mode) */ + bond_set_carrier(bond); + write_unlock_bh(&bond->lock); res = bond_create_slave_symlinks(bond_dev, slave_dev); @@ -1656,18 +1712,12 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev) bond_alb_deinit_slave(bond, slave); } - if (oldcurrent == slave) { + if (oldcurrent == slave) bond_select_active_slave(bond); - if (!bond->curr_active_slave) { - printk(KERN_INFO DRV_NAME - ": %s: now running without any active " - "interface !\n", - bond_dev->name); - } - } - if (bond->slave_cnt == 0) { + bond_set_carrier(bond); + /* if the last slave was removed, zero the mac address * of the master so it will be set by the application * to the mac address of the first slave @@ -1751,6 +1801,8 @@ static int bond_release_all(struct net_device *bond_dev) write_lock_bh(&bond->lock); + netif_carrier_off(bond_dev); + if (bond->slave_cnt == 0) { goto out; } @@ -2187,15 +2239,9 @@ void bond_mii_monitor(struct net_device *bond_dev) bond_select_active_slave(bond); - if (oldcurrent && !bond->curr_active_slave) { - printk(KERN_INFO DRV_NAME - ": %s: now running without any active " - "interface !\n", - bond_dev->name); - } - write_unlock(&bond->curr_slave_lock); - } + } else + bond_set_carrier(bond); re_arm: if (bond->params.miimon) { @@ -2499,13 +2545,6 @@ void bond_loadbalance_arp_mon(struct net_device *bond_dev) bond_select_active_slave(bond); - if (oldcurrent && !bond->curr_active_slave) { - printk(KERN_INFO DRV_NAME - ": %s: now running without any active " - "interface !\n", - bond_dev->name); - } - write_unlock(&bond->curr_slave_lock); } @@ -2579,12 +2618,15 @@ void bond_activebackup_arp_mon(struct net_device *bond_dev) bond->current_arp_slave = NULL; } + bond_set_carrier(bond); + if (slave == bond->curr_active_slave) { printk(KERN_INFO DRV_NAME ": %s: %s is up and now the " "active interface\n", bond_dev->name, slave->dev->name); + netif_carrier_on(bond->dev); } else { printk(KERN_INFO DRV_NAME ": %s: backup interface %s is " @@ -2844,7 +2886,8 @@ static void bond_info_show_master(struct seq_file *seq) (curr) ? curr->dev->name : "None"); } - seq_printf(seq, "MII Status: %s\n", (curr) ? "up" : "down"); + seq_printf(seq, "MII Status: %s\n", netif_carrier_ok(bond->dev) ? + "up" : "down"); seq_printf(seq, "MII Polling Interval (ms): %d\n", bond->params.miimon); seq_printf(seq, "Up Delay (ms): %d\n", bond->params.updelay * bond->params.miimon); @@ -4531,6 +4574,8 @@ int bond_create(char *name, struct bond_params *params, struct bonding **newbond if (newbond) *newbond = bond_dev->priv; + netif_carrier_off(bond_dev); + rtnl_unlock(); /* allows sysfs registration of net device */ res = bond_create_sysfs_entry(bond_dev->priv); goto done; diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index ce9dc9b4e2dc..0bdfe2c71453 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h @@ -22,8 +22,8 @@ #include "bond_3ad.h" #include "bond_alb.h" -#define DRV_VERSION "3.0.2" -#define DRV_RELDATE "February 21, 2006" +#define DRV_VERSION "3.0.3" +#define DRV_RELDATE "March 23, 2006" #define DRV_NAME "bonding" #define DRV_DESCRIPTION "Ethernet Channel Bonding Driver" diff --git a/drivers/net/chelsio/Makefile b/drivers/net/chelsio/Makefile index 91e927827c43..54c78d94f48b 100644 --- a/drivers/net/chelsio/Makefile +++ b/drivers/net/chelsio/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_CHELSIO_T1) += cxgb.o -EXTRA_CFLAGS += -I$(TOPDIR)/drivers/net/chelsio $(DEBUG_FLAGS) +EXTRA_CFLAGS += -Idrivers/net/chelsio $(DEBUG_FLAGS) cxgb-objs := cxgb2.o espi.o pm3393.o sge.o subr.o mv88x201x.o diff --git a/drivers/net/chelsio/sge.c b/drivers/net/chelsio/sge.c index 30ff8ea1a402..4391bf4bf573 100644 --- a/drivers/net/chelsio/sge.c +++ b/drivers/net/chelsio/sge.c @@ -1093,8 +1093,7 @@ static int process_responses(struct adapter *adapter, int budget) if (likely(e->DataValid)) { struct freelQ *fl = &sge->freelQ[e->FreelistQid]; - if (unlikely(!e->Sop || !e->Eop)) - BUG(); + BUG_ON(!e->Sop || !e->Eop); if (unlikely(e->Offload)) unexpected_offload(adapter, fl); else diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index 49cd096a3c3d..add8dc4aa7b0 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -3308,8 +3308,7 @@ e1000_clean(struct net_device *poll_dev, int *budget) while (poll_dev != &adapter->polling_netdev[i]) { i++; - if (unlikely(i == adapter->num_rx_queues)) - BUG(); + BUG_ON(i == adapter->num_rx_queues); } if (likely(adapter->num_tx_queues == 1)) { diff --git a/drivers/net/eql.c b/drivers/net/eql.c index aa1569182fd6..815436c6170f 100644 --- a/drivers/net/eql.c +++ b/drivers/net/eql.c @@ -203,8 +203,7 @@ static int eql_open(struct net_device *dev) printk(KERN_INFO "%s: remember to turn off Van-Jacobson compression on " "your slave devices.\n", dev->name); - if (!list_empty(&eql->queue.all_slaves)) - BUG(); + BUG_ON(!list_empty(&eql->queue.all_slaves)); eql->min_slaves = 1; eql->max_slaves = EQL_DEFAULT_MAX_SLAVES; /* 4 usually... */ diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c index ceb98fd398af..52d01027d9e7 100644 --- a/drivers/net/ibmveth.c +++ b/drivers/net/ibmveth.c @@ -235,7 +235,7 @@ static void ibmveth_replenish_buffer_pool(struct ibmveth_adapter *adapter, struc lpar_rc = h_add_logical_lan_buffer(adapter->vdev->unit_address, desc.desc); - if(lpar_rc != H_Success) { + if(lpar_rc != H_SUCCESS) { pool->free_map[free_index] = index; pool->skbuff[index] = NULL; pool->consumer_index--; @@ -373,7 +373,7 @@ static void ibmveth_rxq_recycle_buffer(struct ibmveth_adapter *adapter) lpar_rc = h_add_logical_lan_buffer(adapter->vdev->unit_address, desc.desc); - if(lpar_rc != H_Success) { + 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); } @@ -511,7 +511,7 @@ static int ibmveth_open(struct net_device *netdev) adapter->filter_list_dma, mac_address); - if(lpar_rc != H_Success) { + if(lpar_rc != H_SUCCESS) { ibmveth_error_printk("h_register_logical_lan failed with %ld\n", lpar_rc); ibmveth_error_printk("buffer TCE:0x%lx filter TCE:0x%lx rxq desc:0x%lx MAC:0x%lx\n", adapter->buffer_list_dma, @@ -527,7 +527,7 @@ static int ibmveth_open(struct net_device *netdev) ibmveth_error_printk("unable to request irq 0x%x, rc %d\n", netdev->irq, rc); do { rc = h_free_logical_lan(adapter->vdev->unit_address); - } while (H_isLongBusy(rc) || (rc == H_Busy)); + } while (H_IS_LONG_BUSY(rc) || (rc == H_BUSY)); ibmveth_cleanup(adapter); return rc; @@ -556,9 +556,9 @@ static int ibmveth_close(struct net_device *netdev) do { lpar_rc = h_free_logical_lan(adapter->vdev->unit_address); - } while (H_isLongBusy(lpar_rc) || (lpar_rc == H_Busy)); + } while (H_IS_LONG_BUSY(lpar_rc) || (lpar_rc == H_BUSY)); - if(lpar_rc != H_Success) + if(lpar_rc != H_SUCCESS) { ibmveth_error_printk("h_free_logical_lan failed with %lx, continuing with close\n", lpar_rc); @@ -693,9 +693,9 @@ static int ibmveth_start_xmit(struct sk_buff *skb, struct net_device *netdev) desc[4].desc, desc[5].desc, correlator); - } while ((lpar_rc == H_Busy) && (retry_count--)); + } while ((lpar_rc == H_BUSY) && (retry_count--)); - if(lpar_rc != H_Success && lpar_rc != H_Dropped) { + 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++) { @@ -786,14 +786,14 @@ static int ibmveth_poll(struct net_device *netdev, int *budget) /* 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, VIO_IRQ_ENABLE); - ibmveth_assert(lpar_rc == H_Success); + 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, VIO_IRQ_DISABLE); - ibmveth_assert(lpar_rc == H_Success); + ibmveth_assert(lpar_rc == H_SUCCESS); more_work = 1; goto restart_poll; } @@ -813,7 +813,7 @@ static irqreturn_t ibmveth_interrupt(int irq, void *dev_instance, struct pt_regs if(netif_rx_schedule_prep(netdev)) { lpar_rc = h_vio_signal(adapter->vdev->unit_address, VIO_IRQ_DISABLE); - ibmveth_assert(lpar_rc == H_Success); + ibmveth_assert(lpar_rc == H_SUCCESS); __netif_rx_schedule(netdev); } return IRQ_HANDLED; @@ -835,7 +835,7 @@ static void ibmveth_set_multicast_list(struct net_device *netdev) IbmVethMcastEnableRecv | IbmVethMcastDisableFiltering, 0); - if(lpar_rc != H_Success) { + if(lpar_rc != H_SUCCESS) { ibmveth_error_printk("h_multicast_ctrl rc=%ld when entering promisc mode\n", lpar_rc); } } else { @@ -847,7 +847,7 @@ static void ibmveth_set_multicast_list(struct net_device *netdev) IbmVethMcastDisableFiltering | IbmVethMcastClearFilterTable, 0); - if(lpar_rc != H_Success) { + 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 */ @@ -858,7 +858,7 @@ static void ibmveth_set_multicast_list(struct net_device *netdev) lpar_rc = h_multicast_ctrl(adapter->vdev->unit_address, IbmVethMcastAddFilter, mcast_addr); - if(lpar_rc != H_Success) { + if(lpar_rc != H_SUCCESS) { ibmveth_error_printk("h_multicast_ctrl rc=%ld when adding an entry to the filter table\n", lpar_rc); } } @@ -867,7 +867,7 @@ static void ibmveth_set_multicast_list(struct net_device *netdev) lpar_rc = h_multicast_ctrl(adapter->vdev->unit_address, IbmVethMcastEnableFiltering, 0); - if(lpar_rc != H_Success) { + if(lpar_rc != H_SUCCESS) { ibmveth_error_printk("h_multicast_ctrl rc=%ld when enabling filtering\n", lpar_rc); } } diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c index 6e2ec56cde0b..606243d11793 100644 --- a/drivers/net/irda/irda-usb.c +++ b/drivers/net/irda/irda-usb.c @@ -1,7 +1,7 @@ /***************************************************************************** * * Filename: irda-usb.c - * Version: 0.9b + * Version: 0.10 * Description: IrDA-USB Driver * Status: Experimental * Author: Dag Brattli @@ -9,6 +9,9 @@ * Copyright (C) 2000, Roman Weissgaerber * Copyright (C) 2001, Dag Brattli * Copyright (C) 2001, Jean Tourrilhes + * Copyright (C) 2004, SigmaTel, Inc. + * Copyright (C) 2005, Milan Beno + * Copyright (C) 2006, Nick Fedchik * * 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 @@ -61,6 +64,7 @@ #include #include #include +#include #include "irda-usb.h" @@ -78,8 +82,12 @@ static struct usb_device_id dongles[] = { { USB_DEVICE(0x50f, 0x180), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW }, /* Extended Systems, Inc., XTNDAccess IrDA USB (ESI-9685) */ { USB_DEVICE(0x8e9, 0x100), .driver_info = IUC_SPEED_BUG | IUC_NO_WINDOW }, + /* SigmaTel STIR4210/4220/4116 USB IrDA (VFIR) Bridge */ + { USB_DEVICE(0x66f, 0x4210), .driver_info = IUC_STIR_4210 | IUC_SPEED_BUG }, + { USB_DEVICE(0x66f, 0x4220), .driver_info = IUC_STIR_4210 | IUC_SPEED_BUG }, + { USB_DEVICE(0x66f, 0x4116), .driver_info = IUC_STIR_4210 | IUC_SPEED_BUG }, { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, + USB_DEVICE_ID_MATCH_INT_SUBCLASS, .bInterfaceClass = USB_CLASS_APP_SPEC, .bInterfaceSubClass = USB_CLASS_IRDA, .driver_info = IUC_DEFAULT, }, @@ -99,6 +107,7 @@ MODULE_DEVICE_TABLE(usb, dongles); /*------------------------------------------------------------------*/ +static void irda_usb_init_qos(struct irda_usb_cb *self) ; static struct irda_class_desc *irda_usb_find_class_desc(struct usb_interface *intf); static void irda_usb_disconnect(struct usb_interface *intf); static void irda_usb_change_speed_xbofs(struct irda_usb_cb *self); @@ -141,7 +150,24 @@ static void irda_usb_build_header(struct irda_usb_cb *self, __u8 *header, int force) { - /* Set the negotiated link speed */ + /* Here we check if we have an STIR421x chip, + * and if either speed or xbofs (or both) needs + * to be changed. + */ + if (self->capability & IUC_STIR_4210 && + ((self->new_speed != -1) || (self->new_xbofs != -1))) { + + /* With STIR421x, speed and xBOFs must be set at the same + * time, even if only one of them changes. + */ + if (self->new_speed == -1) + self->new_speed = self->speed ; + + if (self->new_xbofs == -1) + self->new_xbofs = self->xbofs ; + } + + /* Set the link speed */ if (self->new_speed != -1) { /* Hum... Ugly hack :-( * Some device are not compliant with the spec and change @@ -191,7 +217,11 @@ static void irda_usb_build_header(struct irda_usb_cb *self, *header = SPEED_4000000; self->new_xbofs = 0; break; - } + case 16000000: + *header = SPEED_16000000; + self->new_xbofs = 0; + break; + } } else /* No change */ *header = 0; @@ -235,6 +265,32 @@ static void irda_usb_build_header(struct irda_usb_cb *self, } } +/* +* calculate turnaround time for SigmaTel header +*/ +static __u8 get_turnaround_time(struct sk_buff *skb) +{ + int turnaround_time = irda_get_mtt(skb); + + if ( turnaround_time == 0 ) + return 0; + else if ( turnaround_time <= 10 ) + return 1; + else if ( turnaround_time <= 50 ) + return 2; + else if ( turnaround_time <= 100 ) + return 3; + else if ( turnaround_time <= 500 ) + return 4; + else if ( turnaround_time <= 1000 ) + return 5; + else if ( turnaround_time <= 5000 ) + return 6; + else + return 7; +} + + /*------------------------------------------------------------------*/ /* * Send a command to change the speed of the dongle @@ -262,12 +318,18 @@ static void irda_usb_change_speed_xbofs(struct irda_usb_cb *self) /* Set the new speed and xbofs in this fake frame */ irda_usb_build_header(self, frame, 1); + if ( self->capability & IUC_STIR_4210 ) { + if (frame[0] == 0) return ; // do nothing if no change + frame[1] = 0; // other parameters don't change here + frame[2] = 0; + } + /* Submit the 0 length IrDA frame to trigger new speed settings */ usb_fill_bulk_urb(urb, self->usbdev, usb_sndbulkpipe(self->usbdev, self->bulk_out_ep), frame, IRDA_USB_SPEED_MTU, speed_bulk_callback, self); - urb->transfer_buffer_length = USB_IRDA_HEADER; + urb->transfer_buffer_length = self->header_length; urb->transfer_flags = 0; /* Irq disabled -> GFP_ATOMIC */ @@ -383,16 +445,35 @@ static int irda_usb_hard_xmit(struct sk_buff *skb, struct net_device *netdev) * allocation will be done lower in skb_push(). * Also, we don't use directly skb_cow(), because it require * headroom >= 16, which force unnecessary copies - Jean II */ - if (skb_headroom(skb) < USB_IRDA_HEADER) { + if (skb_headroom(skb) < self->header_length) { IRDA_DEBUG(0, "%s(), Insuficient skb headroom.\n", __FUNCTION__); - if (skb_cow(skb, USB_IRDA_HEADER)) { + if (skb_cow(skb, self->header_length)) { IRDA_WARNING("%s(), failed skb_cow() !!!\n", __FUNCTION__); goto drop; } } /* Change setting for next frame */ - irda_usb_build_header(self, skb_push(skb, USB_IRDA_HEADER), 0); + + if ( self->capability & IUC_STIR_4210 ) { + __u8 turnaround_time; + __u8* frame; + turnaround_time = get_turnaround_time( skb ); + frame= skb_push(skb, self->header_length); + irda_usb_build_header(self, frame, 0); + frame[2] = turnaround_time; + if ((skb->len != 0) && + ((skb->len % 128) == 0) && + ((skb->len % 512) != 0)) { + /* add extra byte for special SigmaTel feature */ + frame[1] = 1; + skb_put(skb, 1); + } else { + frame[1] = 0; + } + } else { + irda_usb_build_header(self, skb_push(skb, self->header_length), 0); + } /* FIXME: Make macro out of this one */ ((struct irda_skb_cb *)skb->cb)->context = self; @@ -795,7 +876,7 @@ static void irda_usb_receive(struct urb *urb, struct pt_regs *regs) } /* Check for empty frames */ - if (urb->actual_length <= USB_IRDA_HEADER) { + if (urb->actual_length <= self->header_length) { IRDA_WARNING("%s(), empty frame!\n", __FUNCTION__); goto done; } @@ -816,7 +897,11 @@ static void irda_usb_receive(struct urb *urb, struct pt_regs *regs) docopy = (urb->actual_length < IRDA_RX_COPY_THRESHOLD); /* Allocate a new skb */ - newskb = dev_alloc_skb(docopy ? urb->actual_length : IRDA_SKB_MAX_MTU); + if ( self->capability & IUC_STIR_4210 ) + newskb = dev_alloc_skb(docopy ? urb->actual_length : IRDA_SKB_MAX_MTU + USB_IRDA_SIGMATEL_HEADER); + else + newskb = dev_alloc_skb(docopy ? urb->actual_length : IRDA_SKB_MAX_MTU); + if (!newskb) { self->stats.rx_dropped++; /* We could deliver the current skb, but this would stall @@ -845,7 +930,7 @@ static void irda_usb_receive(struct urb *urb, struct pt_regs *regs) /* Set proper length on skb & remove USB-IrDA header */ skb_put(dataskb, urb->actual_length); - skb_pull(dataskb, USB_IRDA_HEADER); + skb_pull(dataskb, self->header_length); /* Ask the networking layer to queue the packet for the IrDA stack */ dataskb->dev = self->netdev; @@ -937,6 +1022,191 @@ static int irda_usb_is_receiving(struct irda_usb_cb *self) return 0; /* For now */ } + +#define STIR421X_PATCH_PRODUCT_VERSION_STR "Product Version: " +#define STIR421X_PATCH_COMPONENT_VERSION_STR "Component Version: " +#define STIR421X_PATCH_DATA_TAG_STR "STMP" +#define STIR421X_PATCH_FILE_VERSION_MAX_OFFSET 512 /* version info is before here */ +#define STIR421X_PATCH_FILE_IMAGE_MAX_OFFSET 512 /* patch image starts before here */ +#define STIR421X_PATCH_FILE_END_OF_HEADER_TAG 0x1A /* marks end of patch file header (PC DOS text file EOF character) */ + +/* + * Known firmware patches for STIR421x dongles + */ +static char * stir421x_patches[] = { + "42101001.sb", + "42101002.sb", +}; + +static int stir421x_get_patch_version(unsigned char * patch, const unsigned long patch_len) +{ + unsigned int version_offset; + unsigned long version_major, version_minor, version_build; + unsigned char * version_start; + int version_found = 0; + + for (version_offset = 0; + version_offset < STIR421X_PATCH_FILE_END_OF_HEADER_TAG; + version_offset++) { + if (!memcmp(patch + version_offset, + STIR421X_PATCH_PRODUCT_VERSION_STR, + sizeof(STIR421X_PATCH_PRODUCT_VERSION_STR) - 1)) { + version_found = 1; + version_start = patch + + version_offset + + sizeof(STIR421X_PATCH_PRODUCT_VERSION_STR) - 1; + break; + } + } + + /* We couldn't find a product version on this patch */ + if (!version_found) + return -EINVAL; + + /* Let's check if the product version is dotted */ + if (version_start[3] != '.' || + version_start[7] != '.') + return -EINVAL; + + version_major = simple_strtoul(version_start, NULL, 10); + version_minor = simple_strtoul(version_start + 4, NULL, 10); + version_build = simple_strtoul(version_start + 8, NULL, 10); + + IRDA_DEBUG(2, "%s(), Major: %ld Minor: %ld Build: %ld\n", + __FUNCTION__, + version_major, version_minor, version_build); + + return (((version_major) << 12) + + ((version_minor) << 8) + + ((version_build / 10) << 4) + + (version_build % 10)); + +} + + +static int stir421x_upload_patch (struct irda_usb_cb *self, + unsigned char * patch, + const unsigned int patch_len) +{ + int retval = 0; + int actual_len; + unsigned int i = 0, download_amount = 0; + unsigned char * patch_chunk; + + IRDA_DEBUG (2, "%s(), Uploading STIR421x Patch\n", __FUNCTION__); + + patch_chunk = kzalloc(STIR421X_MAX_PATCH_DOWNLOAD_SIZE, GFP_KERNEL); + if (patch_chunk == NULL) + return -ENOMEM; + + /* break up patch into 1023-byte sections */ + for (i = 0; retval >= 0 && i < patch_len; i += download_amount) { + download_amount = patch_len - i; + if (download_amount > STIR421X_MAX_PATCH_DOWNLOAD_SIZE) + download_amount = STIR421X_MAX_PATCH_DOWNLOAD_SIZE; + + /* download the patch section */ + memcpy(patch_chunk, patch + i, download_amount); + + retval = usb_bulk_msg (self->usbdev, + usb_sndbulkpipe (self->usbdev, + self->bulk_out_ep), + patch_chunk, download_amount, + &actual_len, msecs_to_jiffies (500)); + IRDA_DEBUG (2, "%s(), Sent %u bytes\n", __FUNCTION__, + actual_len); + if (retval == 0) + mdelay(10); + } + + kfree(patch_chunk); + + if (i != patch_len) { + IRDA_ERROR ("%s(), Pushed %d bytes (!= patch_len (%d))\n", + __FUNCTION__, i, patch_len); + retval = -EIO; + } + + if (retval < 0) + /* todo - mark device as not ready */ + IRDA_ERROR ("%s(), STIR421x patch upload failed (%d)\n", + __FUNCTION__, retval); + + return retval; +} + + +static int stir421x_patch_device(struct irda_usb_cb *self) +{ + unsigned int i, patch_found = 0, data_found = 0, data_offset; + int patch_version, ret = 0; + const struct firmware *fw_entry; + + for (i = 0; i < ARRAY_SIZE(stir421x_patches); i++) { + if(request_firmware(&fw_entry, stir421x_patches[i], &self->usbdev->dev) != 0) { + IRDA_ERROR( "%s(), Patch %s is not available\n", __FUNCTION__, stir421x_patches[i]); + continue; + } + + /* We found a patch from userspace */ + patch_version = stir421x_get_patch_version (fw_entry->data, fw_entry->size); + + if (patch_version < 0) { + /* Couldn't fetch a version, let's move on to the next file */ + IRDA_ERROR("%s(), version parsing failed\n", __FUNCTION__); + ret = patch_version; + release_firmware(fw_entry); + continue; + } + + if (patch_version != self->usbdev->descriptor.bcdDevice) { + /* Patch version and device don't match */ + IRDA_ERROR ("%s(), wrong patch version (%d <-> %d)\n", + __FUNCTION__, + patch_version, self->usbdev->descriptor.bcdDevice); + ret = -EINVAL; + release_firmware(fw_entry); + continue; + } + + /* If we're here, we've found a correct patch */ + patch_found = 1; + break; + + } + + /* We couldn't find a valid firmware, let's leave */ + if (!patch_found) + return ret; + + /* The actual image starts after the "STMP" keyword */ + for (data_offset = 0; data_offset < STIR421X_PATCH_FILE_IMAGE_MAX_OFFSET; data_offset++) { + if (!memcmp(fw_entry->data + data_offset, + STIR421X_PATCH_DATA_TAG_STR, + sizeof(STIR421X_PATCH_FILE_IMAGE_MAX_OFFSET))) { + IRDA_DEBUG(2, "%s(), found patch data for STIR421x at offset %d\n", + __FUNCTION__, data_offset); + data_found = 1; + break; + } + } + + /* We couldn't find "STMP" from the header */ + if (!data_found) + return -EINVAL; + + /* Let's upload the patch to the target */ + ret = stir421x_upload_patch(self, + &fw_entry->data[data_offset + sizeof(STIR421X_PATCH_FILE_IMAGE_MAX_OFFSET)], + fw_entry->size - (data_offset + sizeof(STIR421X_PATCH_FILE_IMAGE_MAX_OFFSET))); + + release_firmware(fw_entry); + + return ret; + +} + + /********************** IRDA DEVICE CALLBACKS **********************/ /* * Main calls from the IrDA/Network subsystem. @@ -972,6 +1242,11 @@ static int irda_usb_net_open(struct net_device *netdev) return -1; } + if(self->needspatch) { + IRDA_WARNING("%s(), device needs patch\n", __FUNCTION__) ; + return -EIO ; + } + /* Initialise default speed and xbofs value * (IrLAP will change that soon) */ self->speed = -1; @@ -1050,7 +1325,7 @@ static int irda_usb_net_close(struct net_device *netdev) del_timer(&self->rx_defer_timer); /* Deallocate all the Rx path buffers (URBs and skb) */ - for (i = 0; i < IU_MAX_RX_URBS; i++) { + for (i = 0; i < self->max_rx_urb; i++) { struct urb *urb = self->rx_urb[i]; struct sk_buff *skb = (struct sk_buff *) urb->context; /* Cancel the receive command */ @@ -1426,8 +1701,22 @@ static int irda_usb_probe(struct usb_interface *intf, spin_lock_init(&self->lock); init_timer(&self->rx_defer_timer); + self->capability = id->driver_info; + self->needspatch = ((self->capability & IUC_STIR_4210) != 0) ; + /* Create all of the needed urbs */ - for (i = 0; i < IU_MAX_RX_URBS; i++) { + if (self->capability & IUC_STIR_4210) { + self->max_rx_urb = IU_SIGMATEL_MAX_RX_URBS; + self->header_length = USB_IRDA_SIGMATEL_HEADER; + } else { + self->max_rx_urb = IU_MAX_RX_URBS; + self->header_length = USB_IRDA_HEADER; + } + + self->rx_urb = kzalloc(self->max_rx_urb * sizeof(struct urb *), + GFP_KERNEL); + + for (i = 0; i < self->max_rx_urb; i++) { self->rx_urb[i] = usb_alloc_urb(0, GFP_KERNEL); if (!self->rx_urb[i]) { goto err_out_1; @@ -1479,17 +1768,28 @@ static int irda_usb_probe(struct usb_interface *intf, goto err_out_3; } + self->usbdev = dev; + /* Find IrDA class descriptor */ irda_desc = irda_usb_find_class_desc(intf); ret = -ENODEV; if (irda_desc == NULL) goto err_out_3; + if (self->needspatch) { + ret = usb_control_msg (self->usbdev, usb_sndctrlpipe (self->usbdev, 0), + 0x02, 0x40, 0, 0, 0, 0, msecs_to_jiffies(500)); + if (ret < 0) { + IRDA_DEBUG (0, "usb_control_msg failed %d\n", ret); + goto err_out_3; + } else { + mdelay(10); + } + } + self->irda_desc = irda_desc; self->present = 1; self->netopen = 0; - self->capability = id->driver_info; - self->usbdev = dev; self->usbintf = intf; /* Allocate the buffer for speed changes */ @@ -1508,6 +1808,28 @@ static int irda_usb_probe(struct usb_interface *intf, IRDA_MESSAGE("IrDA: Registered device %s\n", net->name); usb_set_intfdata(intf, self); + + if (self->needspatch) { + /* Now we fetch and upload the firmware patch */ + ret = stir421x_patch_device(self); + self->needspatch = (ret < 0); + if (ret < 0) { + printk("patch_device failed\n"); + goto err_out_4; + } + + /* replace IrDA class descriptor with what patched device is now reporting */ + irda_desc = irda_usb_find_class_desc (self->usbintf); + if (irda_desc == NULL) { + ret = -ENODEV; + goto err_out_4; + } + if (self->irda_desc) + kfree (self->irda_desc); + self->irda_desc = irda_desc; + irda_usb_init_qos(self); + } + return 0; err_out_4: @@ -1518,7 +1840,7 @@ err_out_3: err_out_2: usb_free_urb(self->tx_urb); err_out_1: - for (i = 0; i < IU_MAX_RX_URBS; i++) { + for (i = 0; i < self->max_rx_urb; i++) { if (self->rx_urb[i]) usb_free_urb(self->rx_urb[i]); } @@ -1571,7 +1893,7 @@ static void irda_usb_disconnect(struct usb_interface *intf) /*netif_device_detach(self->netdev);*/ netif_stop_queue(self->netdev); /* Stop all the receive URBs. Must be synchronous. */ - for (i = 0; i < IU_MAX_RX_URBS; i++) + for (i = 0; i < self->max_rx_urb; i++) usb_kill_urb(self->rx_urb[i]); /* Cancel Tx and speed URB. * Make sure it's synchronous to avoid races. */ @@ -1586,8 +1908,9 @@ static void irda_usb_disconnect(struct usb_interface *intf) self->usbintf = NULL; /* Clean up our urbs */ - for (i = 0; i < IU_MAX_RX_URBS; i++) + for (i = 0; i < self->max_rx_urb; i++) usb_free_urb(self->rx_urb[i]); + kfree(self->rx_urb); /* Clean up Tx and speed URB */ usb_free_urb(self->tx_urb); usb_free_urb(self->speed_urb); @@ -1648,6 +1971,6 @@ module_exit(usb_irda_cleanup); */ module_param(qos_mtt_bits, int, 0); MODULE_PARM_DESC(qos_mtt_bits, "Minimum Turn Time"); -MODULE_AUTHOR("Roman Weissgaerber , Dag Brattli and Jean Tourrilhes "); -MODULE_DESCRIPTION("IrDA-USB Dongle Driver"); +MODULE_AUTHOR("Roman Weissgaerber , Dag Brattli , Jean Tourrilhes and Nick Fedchik "); +MODULE_DESCRIPTION("IrDA-USB Dongle Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/irda/irda-usb.h b/drivers/net/irda/irda-usb.h index 4026af42dd47..d833db52cebf 100644 --- a/drivers/net/irda/irda-usb.h +++ b/drivers/net/irda/irda-usb.h @@ -1,7 +1,7 @@ /***************************************************************************** * * Filename: irda-usb.h - * Version: 0.9b + * Version: 0.10 * Description: IrDA-USB Driver * Status: Experimental * Author: Dag Brattli @@ -9,6 +9,9 @@ * Copyright (C) 2001, Roman Weissgaerber * Copyright (C) 2000, Dag Brattli * Copyright (C) 2001, Jean Tourrilhes + * Copyright (C) 2004, SigmaTel, Inc. + * Copyright (C) 2005, Milan Beno + * Copyright (C) 2006, Nick FEdchik * * 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 @@ -31,6 +34,9 @@ #include #include /* struct irlap_cb */ +#define PATCH_FILE_SIZE_MAX 65536 +#define PATCH_FILE_SIZE_MIN 80 + #define RX_COPY_THRESHOLD 200 #define IRDA_USB_MAX_MTU 2051 #define IRDA_USB_SPEED_MTU 64 /* Weird, but work like this */ @@ -79,15 +85,16 @@ /* Inbound header */ #define MEDIA_BUSY 0x80 -#define SPEED_2400 0x01 -#define SPEED_9600 0x02 -#define SPEED_19200 0x03 -#define SPEED_38400 0x04 -#define SPEED_57600 0x05 -#define SPEED_115200 0x06 -#define SPEED_576000 0x07 -#define SPEED_1152000 0x08 -#define SPEED_4000000 0x09 +#define SPEED_2400 0x01 +#define SPEED_9600 0x02 +#define SPEED_19200 0x03 +#define SPEED_38400 0x04 +#define SPEED_57600 0x05 +#define SPEED_115200 0x06 +#define SPEED_576000 0x07 +#define SPEED_1152000 0x08 +#define SPEED_4000000 0x09 +#define SPEED_16000000 0x0a /* Basic capabilities */ #define IUC_DEFAULT 0x00 /* Basic device compliant with 1.0 spec */ @@ -100,11 +107,14 @@ #define IUC_SMALL_PKT 0x10 /* Device doesn't behave with big Rx packets */ #define IUC_MAX_WINDOW 0x20 /* Device underestimate the Rx window */ #define IUC_MAX_XBOFS 0x40 /* Device need more xbofs than advertised */ +#define IUC_STIR_4210 0x80 /* SigmaTel 4210/4220/4116 VFIR */ /* USB class definitions */ -#define USB_IRDA_HEADER 0x01 -#define USB_CLASS_IRDA 0x02 /* USB_CLASS_APP_SPEC subclass */ -#define USB_DT_IRDA 0x21 +#define USB_IRDA_HEADER 0x01 +#define USB_CLASS_IRDA 0x02 /* USB_CLASS_APP_SPEC subclass */ +#define USB_DT_IRDA 0x21 +#define USB_IRDA_SIGMATEL_HEADER 0x03 +#define IU_SIGMATEL_MAX_RX_URBS (IU_MAX_ACTIVE_RX_URBS + USB_IRDA_SIGMATEL_HEADER) struct irda_class_desc { __u8 bLength; @@ -123,6 +133,7 @@ struct irda_class_desc { * (6.2.5, USB-IrDA class spec 1.0) */ #define IU_REQ_GET_CLASS_DESC 0x06 +#define STIR421X_MAX_PATCH_DOWNLOAD_SIZE 1023 struct irda_usb_cb { struct irda_class_desc *irda_desc; @@ -136,7 +147,8 @@ struct irda_usb_cb { __u16 bulk_out_mtu; /* Max Tx packet size in bytes */ __u8 bulk_int_ep; /* Interrupt Endpoint assignments */ - struct urb *rx_urb[IU_MAX_RX_URBS]; /* URBs used to receive data frames */ + __u8 max_rx_urb; + struct urb **rx_urb; /* URBs used to receive data frames */ struct urb *idle_rx_urb; /* Pointer to idle URB in Rx path */ struct urb *tx_urb; /* URB used to send data frames */ struct urb *speed_urb; /* URB used to send speed commands */ @@ -157,6 +169,9 @@ struct irda_usb_cb { __u32 speed; /* Current speed */ __s32 new_speed; /* speed we need to set */ + __u8 header_length; /* USB-IrDA frame header size */ + int needspatch; /* device needs firmware patch */ + struct timer_list rx_defer_timer; /* Wait for Rx error to clear */ }; diff --git a/drivers/net/irda/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c index 63d38fbbd04e..f530686bd09f 100644 --- a/drivers/net/irda/sa1100_ir.c +++ b/drivers/net/irda/sa1100_ir.c @@ -695,8 +695,7 @@ static int sa1100_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) /* * We must not be transmitting... */ - if (si->txskb) - BUG(); + BUG_ON(si->txskb); netif_stop_queue(dev); diff --git a/drivers/net/irda/smsc-ircc2.c b/drivers/net/irda/smsc-ircc2.c index ec94ecdb103d..bbcfc8ec35a1 100644 --- a/drivers/net/irda/smsc-ircc2.c +++ b/drivers/net/irda/smsc-ircc2.c @@ -11,6 +11,7 @@ * Copyright (c) 2002 Daniele Peri * All Rights Reserved. * Copyright (c) 2002 Jean Tourrilhes + * Copyright (c) 2006 Linus Walleij * * * Based on smc-ircc.c: @@ -61,6 +62,9 @@ #include #include +#ifdef CONFIG_PCI +#include +#endif #include #include @@ -100,6 +104,22 @@ MODULE_PARM_DESC(ircc_transceiver, "Transceiver type"); /* Types */ +#ifdef CONFIG_PCI +struct smsc_ircc_subsystem_configuration { + unsigned short vendor; /* PCI vendor ID */ + unsigned short device; /* PCI vendor ID */ + unsigned short subvendor; /* PCI subsystem vendor ID */ + unsigned short subdevice; /* PCI sybsystem device ID */ + unsigned short sir_io; /* I/O port for SIR */ + unsigned short fir_io; /* I/O port for FIR */ + unsigned char fir_irq; /* FIR IRQ */ + unsigned char fir_dma; /* FIR DMA */ + unsigned short cfg_base; /* I/O port for chip configuration */ + int (*preconfigure)(struct pci_dev *dev, struct smsc_ircc_subsystem_configuration *conf); /* Preconfig function */ + const char *name; /* name shown as info */ +}; +#endif + struct smsc_transceiver { char *name; void (*set_for_speed)(int fir_base, u32 speed); @@ -202,6 +222,16 @@ static int __init smsc_superio_flat(const struct smsc_chip *chips, unsigned shor static int __init smsc_superio_paged(const struct smsc_chip *chips, unsigned short cfg_base, char *type); static int __init smsc_superio_fdc(unsigned short cfg_base); static int __init smsc_superio_lpc(unsigned short cfg_base); +#ifdef CONFIG_PCI +static int __init preconfigure_smsc_chip(struct smsc_ircc_subsystem_configuration *conf); +static int __init preconfigure_through_82801(struct pci_dev *dev, struct smsc_ircc_subsystem_configuration *conf); +static int __init preconfigure_through_ali(struct pci_dev *dev, struct smsc_ircc_subsystem_configuration *conf); +static int __init smsc_ircc_preconfigure_subsystems(unsigned short ircc_cfg, + unsigned short ircc_fir, + unsigned short ircc_sir, + unsigned char ircc_dma, + unsigned char ircc_irq); +#endif /* Transceivers specific functions */ @@ -353,6 +383,13 @@ static int __init smsc_ircc_init(void) return ret; } +#ifdef CONFIG_PCI + if (smsc_ircc_preconfigure_subsystems(ircc_cfg, ircc_fir, ircc_sir, ircc_dma, ircc_irq) < 0) { + /* Ignore errors from preconfiguration */ + IRDA_ERROR("%s, Preconfiguration failed !\n", driver_name); + } +#endif + dev_count = 0; if (ircc_fir > 0 && ircc_sir > 0) { @@ -2285,6 +2322,280 @@ static int __init smsc_superio_lpc(unsigned short cfg_base) return ret; } +/* + * Look for some specific subsystem setups that need + * pre-configuration not properly done by the BIOS (especially laptops) + * This code is based in part on smcinit.c, tosh1800-smcinit.c + * and tosh2450-smcinit.c. The table lists the device entries + * for ISA bridges with an LPC (Local Peripheral Configurator) + * that are in turn used to configure the SMSC device with default + * SIR and FIR I/O ports, DMA and IRQ. + */ +#ifdef CONFIG_PCI +#define PCIID_VENDOR_INTEL 0x8086 +#define PCIID_VENDOR_ALI 0x10b9 +static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __devinitdata = { + { + .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */ + .device = 0x24cc, + .subvendor = 0x103c, + .subdevice = 0x088c, + .sir_io = 0x02f8, /* Quite certain these are the same for nc8000 as for nc6000 */ + .fir_io = 0x0130, + .fir_irq = 0x09, + .fir_dma = 0x03, + .cfg_base = 0x004e, + .preconfigure = preconfigure_through_82801, + .name = "HP nc8000", + }, + { + .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */ + .device = 0x24cc, + .subvendor = 0x103c, + .subdevice = 0x0890, + .sir_io = 0x02f8, + .fir_io = 0x0130, + .fir_irq = 0x09, + .fir_dma = 0x03, + .cfg_base = 0x004e, + .preconfigure = preconfigure_through_82801, + .name = "HP nc6000", + }, + { + .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DB/DBL (ICH4/ICH4-L) LPC Interface Bridge */ + .device = 0x24c0, + .subvendor = 0x1179, + .subdevice = 0xffff, /* 0xffff is "any", Not sure, 0x0001 or 0x0002 */ + .sir_io = 0x03f8, + .fir_io = 0x0130, + .fir_irq = 0x07, + .fir_dma = 0x01, + .cfg_base = 0x002e, + .preconfigure = preconfigure_through_82801, + .name = "Toshiba Satellite 2450", + }, + { + .vendor = PCIID_VENDOR_INTEL, /* Intel 82801CAM ISA bridge */ + .device = 0x248c, /* Some use 24cc? */ + .subvendor = 0x1179, + .subdevice = 0xffff, /* 0xffff is "any", Not sure, 0x0001 or 0x0002 */ + .sir_io = 0x03f8, + .fir_io = 0x0130, + .fir_irq = 0x03, + .fir_dma = 0x03, + .cfg_base = 0x002e, + .preconfigure = preconfigure_through_82801, + .name = "Toshiba Satellite 5100/5200, Tecra 9100", + }, + { + .vendor = PCIID_VENDOR_ALI, /* ALi M1533/M1535 PCI to ISA Bridge [Aladdin IV/V/V+] */ + .device = 0x1533, + .subvendor = 0x1179, + .subdevice = 0xffff, /* 0xffff is "any", Not sure, 0x0001 or 0x0002 */ + .sir_io = 0x02e8, + .fir_io = 0x02f8, + .fir_irq = 0x07, + .fir_dma = 0x03, + .cfg_base = 0x002e, + .preconfigure = preconfigure_through_ali, + .name = "Toshiba Satellite 1800", + }, + { } // Terminator +}; + + +/* + * This sets up the basic SMSC parameters (FIR port, SIR port, FIR DMA, FIR IRQ) + * through the chip configuration port. + */ +static int __init preconfigure_smsc_chip(struct smsc_ircc_subsystem_configuration *conf) +{ + unsigned short iobase = conf->cfg_base; + unsigned char tmpbyte; + + outb(LPC47N227_CFGACCESSKEY, iobase); // enter configuration state + outb(SMSCSIOFLAT_DEVICEID_REG, iobase); // set for device ID + tmpbyte = inb(iobase +1); // Read device ID + IRDA_DEBUG(0, "Detected Chip id: 0x%02x, setting up registers...\n",tmpbyte); + + /* Disable UART1 and set up SIR I/O port */ + outb(0x24, iobase); // select CR24 - UART1 base addr + outb(0x00, iobase + 1); // disable UART1 + outb(SMSCSIOFLAT_UART2BASEADDR_REG, iobase); // select CR25 - UART2 base addr + outb( (conf->sir_io >> 2), iobase + 1); // bits 2-9 of 0x3f8 + tmpbyte = inb(iobase + 1); + if (tmpbyte != (conf->sir_io >> 2) ) { + IRDA_WARNING("ERROR: could not configure SIR ioport.\n"); + return -ENXIO; + } + + /* Set up FIR IRQ channel for UART2 */ + outb(SMSCSIOFLAT_UARTIRQSELECT_REG, iobase); // select CR28 - UART1,2 IRQ select + tmpbyte = inb(iobase + 1); + tmpbyte &= SMSCSIOFLAT_UART1IRQSELECT_MASK; // Do not touch the UART1 portion + tmpbyte |= (conf->fir_irq & SMSCSIOFLAT_UART2IRQSELECT_MASK); + outb(tmpbyte, iobase + 1); + tmpbyte = inb(iobase + 1) & SMSCSIOFLAT_UART2IRQSELECT_MASK; + if (tmpbyte != conf->fir_irq) { + IRDA_WARNING("ERROR: could not configure FIR IRQ channel.\n"); + return -ENXIO; + } + + /* Set up FIR I/O port */ + outb(SMSCSIOFLAT_FIRBASEADDR_REG, iobase); // CR2B - SCE (FIR) base addr + outb((conf->fir_io >> 3), iobase + 1); + tmpbyte = inb(iobase + 1); + if (tmpbyte != (conf->fir_io >> 3) ) { + IRDA_WARNING("ERROR: could not configure FIR I/O port.\n"); + return -ENXIO; + } + + /* Set up FIR DMA channel */ + outb(SMSCSIOFLAT_FIRDMASELECT_REG, iobase); // CR2C - SCE (FIR) DMA select + outb((conf->fir_dma & LPC47N227_FIRDMASELECT_MASK), iobase + 1); // DMA + tmpbyte = inb(iobase + 1) & LPC47N227_FIRDMASELECT_MASK; + if (tmpbyte != (conf->fir_dma & LPC47N227_FIRDMASELECT_MASK)) { + IRDA_WARNING("ERROR: could not configure FIR DMA channel.\n"); + return -ENXIO; + } + + outb(SMSCSIOFLAT_UARTMODE0C_REG, iobase); // CR0C - UART mode + tmpbyte = inb(iobase + 1); + tmpbyte &= ~SMSCSIOFLAT_UART2MODE_MASK | SMSCSIOFLAT_UART2MODE_VAL_IRDA; + outb(tmpbyte, iobase + 1); // enable IrDA (HPSIR) mode, high speed + + outb(LPC47N227_APMBOOTDRIVE_REG, iobase); // CR07 - Auto Pwr Mgt/boot drive sel + tmpbyte = inb(iobase + 1); + outb(tmpbyte | LPC47N227_UART2AUTOPWRDOWN_MASK, iobase + 1); // enable UART2 autopower down + + /* This one was not part of tosh1800 */ + outb(0x0a, iobase); // CR0a - ecp fifo / ir mux + tmpbyte = inb(iobase + 1); + outb(tmpbyte | 0x40, iobase + 1); // send active device to ir port + + outb(LPC47N227_UART12POWER_REG, iobase); // CR02 - UART 1,2 power + tmpbyte = inb(iobase + 1); + outb(tmpbyte | LPC47N227_UART2POWERDOWN_MASK, iobase + 1); // UART2 power up mode, UART1 power down + + outb(LPC47N227_FDCPOWERVALIDCONF_REG, iobase); // CR00 - FDC Power/valid config cycle + tmpbyte = inb(iobase + 1); + outb(tmpbyte | LPC47N227_VALID_MASK, iobase + 1); // valid config cycle done + + outb(LPC47N227_CFGEXITKEY, iobase); // Exit configuration + + return 0; +} + +/* 82801CAM registers */ +#define VID 0x00 +#define DID 0x02 +#define PIRQA_ROUT 0x60 +#define PCI_DMA_C 0x90 +#define COM_DEC 0xe0 +#define LPC_EN 0xe6 +#define GEN2_DEC 0xec +/* + * Sets up the I/O range using the 82801CAM ISA bridge, 82801DBM LPC bridge or + * Intel 82801DB/DBL (ICH4/ICH4-L) LPC Interface Bridge. They all work the same way! + */ +static int __init preconfigure_through_82801(struct pci_dev *dev, + struct smsc_ircc_subsystem_configuration *conf) +{ + unsigned short tmpword; + int ret; + + IRDA_MESSAGE("Setting up the SMSC device via the 82801 controller.\n"); + pci_write_config_byte(dev, COM_DEC, 0x10); + + /* Enable LPC */ + pci_read_config_word(dev, LPC_EN, &tmpword); /* LPC_EN register */ + tmpword &= 0xfffd; /* mask bit 1 */ + tmpword |= 0x0001; /* set bit 0 : COMA addr range enable */ + pci_write_config_word(dev, LPC_EN, tmpword); + + /* Setup DMA */ + pci_write_config_word(dev, PCI_DMA_C, 0xc0c0); /* LPC I/F DMA on, channel 3 -- rtm (?? PCI DMA ?) */ + pci_write_config_word(dev, GEN2_DEC, 0x131); /* LPC I/F 2nd decode range */ + + /* Pre-configure chip */ + ret = preconfigure_smsc_chip(conf); + + /* Disable LPC */ + pci_read_config_word(dev, LPC_EN, &tmpword); /* LPC_EN register */ + tmpword &= 0xfffc; /* mask bit 1 and bit 0, COMA addr range disable */ + pci_write_config_word(dev, LPC_EN, tmpword); + return ret; +} + +static int __init preconfigure_through_ali(struct pci_dev *dev, + struct smsc_ircc_subsystem_configuration *conf) +{ + /* TODO: put in ALi 1533 configuration here. */ + IRDA_MESSAGE("SORRY: %s has an unsupported bridge controller (ALi): not pre-configured.\n", conf->name); + return -ENODEV; +} + +static int __init smsc_ircc_preconfigure_subsystems(unsigned short ircc_cfg, + unsigned short ircc_fir, + unsigned short ircc_sir, + unsigned char ircc_dma, + unsigned char ircc_irq) +{ + struct pci_dev *dev = NULL; + unsigned short ss_vendor = 0x0000; + unsigned short ss_device = 0x0000; + int ret = 0; + + dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev); + + while (dev != NULL) { + struct smsc_ircc_subsystem_configuration *conf; + + /* + * Cache the subsystem vendor/device: some manufacturers fail to set + * this for all components, so we save it in case there is just + * 0x0000 0x0000 on the device we want to check. + */ + if (dev->subsystem_vendor != 0x0000U) { + ss_vendor = dev->subsystem_vendor; + ss_device = dev->subsystem_device; + } + conf = subsystem_configurations; + for( ; conf->subvendor; conf++) { + if(conf->vendor == dev->vendor && + conf->device == dev->device && + conf->subvendor == ss_vendor && /* Sometimes these are cached values */ + (conf->subdevice == ss_device || conf->subdevice == 0xffff)) { + struct smsc_ircc_subsystem_configuration tmpconf; + + memcpy(&tmpconf, conf, sizeof(struct smsc_ircc_subsystem_configuration)); + + /* Override the default values with anything passed in as parameter */ + if (ircc_cfg != 0) + tmpconf.cfg_base = ircc_cfg; + if (ircc_fir != 0) + tmpconf.fir_io = ircc_fir; + if (ircc_sir != 0) + tmpconf.sir_io = ircc_sir; + if (ircc_dma != 0xff) + tmpconf.fir_dma = ircc_dma; + if (ircc_irq != 0xff) + tmpconf.fir_irq = ircc_irq; + + IRDA_MESSAGE("Detected unconfigured %s SMSC IrDA chip, pre-configuring device.\n", conf->name); + if (conf->preconfigure) + ret = conf->preconfigure(dev, &tmpconf); + else + ret = -ENODEV; + } + } + dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev); + } + + return ret; +} +#endif // CONFIG_PCI + /************************************************ * * Transceivers specific functions diff --git a/drivers/net/ixp2000/ixpdev.c b/drivers/net/ixp2000/ixpdev.c index 77f104a005f3..fbc2d21020f4 100644 --- a/drivers/net/ixp2000/ixpdev.c +++ b/drivers/net/ixp2000/ixpdev.c @@ -299,10 +299,7 @@ int ixpdev_init(int __nds_count, struct net_device **__nds, int i; int err; - if (RX_BUF_COUNT > 192 || TX_BUF_COUNT > 192) { - static void __too_many_rx_or_tx_buffers(void); - __too_many_rx_or_tx_buffers(); - } + BUILD_BUG_ON(RX_BUF_COUNT > 192 || TX_BUF_COUNT > 192); printk(KERN_INFO "IXP2000 MSF ethernet driver %s\n", DRV_MODULE_VERSION); diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c index 8d4999837b65..7826afbb9db9 100644 --- a/drivers/net/natsemi.c +++ b/drivers/net/natsemi.c @@ -226,7 +226,7 @@ static int full_duplex[MAX_UNITS]; NATSEMI_PG1_NREGS) #define NATSEMI_REGS_VER 1 /* v1 added RFDR registers */ #define NATSEMI_REGS_SIZE (NATSEMI_NREGS * sizeof(u32)) -#define NATSEMI_EEPROM_SIZE 24 /* 12 16-bit values */ +#define NATSEMI_DEF_EEPROM_SIZE 24 /* 12 16-bit values */ /* Buffer sizes: * The nic writes 32-bit values, even if the upper bytes of @@ -714,6 +714,8 @@ struct netdev_private { unsigned int iosize; spinlock_t lock; u32 msg_enable; + /* EEPROM data */ + int eeprom_size; }; static void move_int_phy(struct net_device *dev, int addr); @@ -890,6 +892,7 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev, np->msg_enable = (debug >= 0) ? (1<hands_off = 0; np->intr_status = 0; + np->eeprom_size = NATSEMI_DEF_EEPROM_SIZE; /* Initial port: * - If the nic was configured to use an external phy and if find_mii @@ -2582,7 +2585,8 @@ static int get_regs_len(struct net_device *dev) static int get_eeprom_len(struct net_device *dev) { - return NATSEMI_EEPROM_SIZE; + struct netdev_private *np = netdev_priv(dev); + return np->eeprom_size; } static int get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) @@ -2669,15 +2673,20 @@ static u32 get_link(struct net_device *dev) static int get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 *data) { struct netdev_private *np = netdev_priv(dev); - u8 eebuf[NATSEMI_EEPROM_SIZE]; + u8 *eebuf; int res; + eebuf = kmalloc(np->eeprom_size, GFP_KERNEL); + if (!eebuf) + return -ENOMEM; + eeprom->magic = PCI_VENDOR_ID_NS | (PCI_DEVICE_ID_NS_83815<<16); spin_lock_irq(&np->lock); res = netdev_get_eeprom(dev, eebuf); spin_unlock_irq(&np->lock); if (!res) memcpy(data, eebuf+eeprom->offset, eeprom->len); + kfree(eebuf); return res; } @@ -3033,9 +3042,10 @@ static int netdev_get_eeprom(struct net_device *dev, u8 *buf) int i; u16 *ebuf = (u16 *)buf; void __iomem * ioaddr = ns_ioaddr(dev); + struct netdev_private *np = netdev_priv(dev); /* eeprom_read reads 16 bits, and indexes by 16 bits */ - for (i = 0; i < NATSEMI_EEPROM_SIZE/2; i++) { + for (i = 0; i < np->eeprom_size/2; i++) { ebuf[i] = eeprom_read(ioaddr, i); /* The EEPROM itself stores data bit-swapped, but eeprom_read * reads it back "sanely". So we swap it back here in order to diff --git a/drivers/net/ne2k-pci.c b/drivers/net/ne2k-pci.c index d11821dd86ed..ced9fdb8335c 100644 --- a/drivers/net/ne2k-pci.c +++ b/drivers/net/ne2k-pci.c @@ -645,9 +645,7 @@ static void __devexit ne2k_pci_remove_one (struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); - if (!dev) - BUG(); - + BUG_ON(!dev); unregister_netdev(dev); release_region(dev->base_addr, NE_IO_EXTENT); free_netdev(dev); diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index edd1b5306b16..66e74f740261 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -87,6 +87,7 @@ static void write_msg(struct console *con, const char *msg, unsigned int len) } static struct console netconsole = { + .name = "netcon", .flags = CON_ENABLED | CON_PRINTBUFFER, .write = write_msg }; @@ -94,7 +95,7 @@ static struct console netconsole = { static int option_setup(char *opt) { configured = !netpoll_parse_options(&np, opt); - return 0; + return 1; } __setup("netconsole=", option_setup); diff --git a/drivers/net/ns83820.c b/drivers/net/ns83820.c index 8e9b1a537dee..706aed7d717f 100644 --- a/drivers/net/ns83820.c +++ b/drivers/net/ns83820.c @@ -568,8 +568,7 @@ static inline int ns83820_add_rx_skb(struct ns83820 *dev, struct sk_buff *skb) #endif sg = dev->rx_info.descs + (next_empty * DESC_SIZE); - if (unlikely(NULL != dev->rx_info.skbs[next_empty])) - BUG(); + BUG_ON(NULL != dev->rx_info.skbs[next_empty]); dev->rx_info.skbs[next_empty] = skb; dev->rx_info.next_empty = (next_empty + 1) % NR_RX_DESC; diff --git a/drivers/net/pcmcia/3c574_cs.c b/drivers/net/pcmcia/3c574_cs.c index ce90becb8bdf..fab93360f017 100644 --- a/drivers/net/pcmcia/3c574_cs.c +++ b/drivers/net/pcmcia/3c574_cs.c @@ -204,7 +204,7 @@ enum Window4 { /* Window 4: Xcvr/media bits. */ #define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */ struct el3_private { - dev_link_t link; + struct pcmcia_device *p_dev; dev_node_t node; struct net_device_stats stats; u16 advertising, partner; /* NWay media advertisement */ @@ -225,8 +225,8 @@ static char mii_preamble_required = 0; /* Index of functions. */ -static void tc574_config(dev_link_t *link); -static void tc574_release(dev_link_t *link); +static int tc574_config(struct pcmcia_device *link); +static void tc574_release(struct pcmcia_device *link); static void mdio_sync(kio_addr_t ioaddr, int bits); static int mdio_read(kio_addr_t ioaddr, int phy_id, int location); @@ -256,10 +256,9 @@ static void tc574_detach(struct pcmcia_device *p_dev); with Card Services. */ -static int tc574_attach(struct pcmcia_device *p_dev) +static int tc574_probe(struct pcmcia_device *link) { struct el3_private *lp; - dev_link_t *link; struct net_device *dev; DEBUG(0, "3c574_attach()\n"); @@ -269,8 +268,8 @@ static int tc574_attach(struct pcmcia_device *p_dev) if (!dev) return -ENOMEM; lp = netdev_priv(dev); - link = &lp->link; link->priv = dev; + lp->p_dev = link; spin_lock_init(&lp->window_lock); link->io.NumPorts1 = 32; @@ -280,7 +279,6 @@ static int tc574_attach(struct pcmcia_device *p_dev) link->irq.Handler = &el3_interrupt; link->irq.Instance = dev; link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; link->conf.ConfigIndex = 1; link->conf.Present = PRESENT_OPTION; @@ -298,13 +296,7 @@ static int tc574_attach(struct pcmcia_device *p_dev) dev->watchdog_timeo = TX_TIMEOUT; #endif - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - tc574_config(link); - - return 0; + return tc574_config(link); } /* tc574_attach */ /* @@ -316,18 +308,16 @@ static int tc574_attach(struct pcmcia_device *p_dev) */ -static void tc574_detach(struct pcmcia_device *p_dev) +static void tc574_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; DEBUG(0, "3c574_detach(0x%p)\n", link); - if (link->dev) + if (link->dev_node) unregister_netdev(dev); - if (link->state & DEV_CONFIG) - tc574_release(link); + tc574_release(link); free_netdev(dev); } /* tc574_detach */ @@ -343,9 +333,8 @@ static void tc574_detach(struct pcmcia_device *p_dev) static const char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"}; -static void tc574_config(dev_link_t *link) +static int tc574_config(struct pcmcia_device *link) { - client_handle_t handle = link->handle; struct net_device *dev = link->priv; struct el3_private *lp = netdev_priv(dev); tuple_t tuple; @@ -363,30 +352,27 @@ static void tc574_config(dev_link_t *link) tuple.Attributes = 0; tuple.DesiredTuple = CISTPL_CONFIG; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); tuple.TupleData = (cisdata_t *)buf; tuple.TupleDataMax = 64; tuple.TupleOffset = 0; - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; - /* Configure card */ - link->state |= DEV_CONFIG; - link->io.IOAddrLines = 16; for (i = j = 0; j < 0x400; j += 0x20) { link->io.BasePort1 = j ^ 0x300; - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) break; } if (i != CS_SUCCESS) { - cs_error(link->handle, RequestIO, i); + cs_error(link, RequestIO, i); goto failed; } - CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq)); - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); dev->irq = link->irq.AssignedIRQ; dev->base_addr = link->io.BasePort1; @@ -397,8 +383,8 @@ static void tc574_config(dev_link_t *link) the hardware address. The future products may include a modem chip and put the address in the CIS. */ tuple.DesiredTuple = 0x88; - if (pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) { - pcmcia_get_tuple_data(handle, &tuple); + if (pcmcia_get_first_tuple(link, &tuple) == CS_SUCCESS) { + pcmcia_get_tuple_data(link, &tuple); for (i = 0; i < 3; i++) phys_addr[i] = htons(buf[i]); } else { @@ -412,9 +398,9 @@ static void tc574_config(dev_link_t *link) } } tuple.DesiredTuple = CISTPL_VERS_1; - if (pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS && - pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS && - pcmcia_parse_tuple(handle, &tuple, &parse) == CS_SUCCESS) { + if (pcmcia_get_first_tuple(link, &tuple) == CS_SUCCESS && + pcmcia_get_tuple_data(link, &tuple) == CS_SUCCESS && + pcmcia_parse_tuple(link, &tuple, &parse) == CS_SUCCESS) { cardname = parse.version_1.str + parse.version_1.ofs[1]; } else cardname = "3Com 3c574"; @@ -473,13 +459,12 @@ static void tc574_config(dev_link_t *link) } } - link->state &= ~DEV_CONFIG_PENDING; - link->dev = &lp->node; - SET_NETDEV_DEV(dev, &handle_to_dev(handle)); + link->dev_node = &lp->node; + SET_NETDEV_DEV(dev, &handle_to_dev(link)); if (register_netdev(dev) != 0) { printk(KERN_NOTICE "3c574_cs: register_netdev() failed\n"); - link->dev = NULL; + link->dev_node = NULL; goto failed; } @@ -493,13 +478,13 @@ static void tc574_config(dev_link_t *link) 8 << config.u.ram_size, ram_split[config.u.ram_split], config.u.autoselect ? "autoselect " : ""); - return; + return 0; cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); failed: tc574_release(link); - return; + return -ENODEV; } /* tc574_config */ @@ -509,44 +494,28 @@ failed: still open, this will be postponed until it is closed. */ -static void tc574_release(dev_link_t *link) +static void tc574_release(struct pcmcia_device *link) { - DEBUG(0, "3c574_release(0x%p)\n", link); - - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - - link->state &= ~DEV_CONFIG; + pcmcia_disable_device(link); } -static int tc574_suspend(struct pcmcia_device *p_dev) +static int tc574_suspend(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - if (link->open) - netif_device_detach(dev); - pcmcia_release_configuration(link->handle); - } + if (link->open) + netif_device_detach(dev); return 0; } -static int tc574_resume(struct pcmcia_device *p_dev) +static int tc574_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - pcmcia_request_configuration(link->handle, &link->conf); - if (link->open) { - tc574_reset(dev); - netif_device_attach(dev); - } + if (link->open) { + tc574_reset(dev); + netif_device_attach(dev); } return 0; @@ -757,9 +726,9 @@ static void tc574_reset(struct net_device *dev) static int el3_open(struct net_device *dev) { struct el3_private *lp = netdev_priv(dev); - dev_link_t *link = &lp->link; + struct pcmcia_device *link = lp->p_dev; - if (!DEV_OK(link)) + if (!pcmcia_dev_present(link)) return -ENODEV; link->open++; @@ -1203,11 +1172,11 @@ static int el3_close(struct net_device *dev) { kio_addr_t ioaddr = dev->base_addr; struct el3_private *lp = netdev_priv(dev); - dev_link_t *link = &lp->link; + struct pcmcia_device *link = lp->p_dev; DEBUG(2, "%s: shutting down ethercard.\n", dev->name); - if (DEV_OK(link)) { + if (pcmcia_dev_present(link)) { unsigned long flags; /* Turn off statistics ASAP. We update lp->stats below. */ @@ -1246,7 +1215,7 @@ static struct pcmcia_driver tc574_driver = { .drv = { .name = "3c574_cs", }, - .probe = tc574_attach, + .probe = tc574_probe, .remove = tc574_detach, .id_table = tc574_ids, .suspend = tc574_suspend, diff --git a/drivers/net/pcmcia/3c589_cs.c b/drivers/net/pcmcia/3c589_cs.c index 3dba50849da7..875a0fe251e7 100644 --- a/drivers/net/pcmcia/3c589_cs.c +++ b/drivers/net/pcmcia/3c589_cs.c @@ -105,7 +105,7 @@ enum RxFilter { #define TX_TIMEOUT ((400*HZ)/1000) struct el3_private { - dev_link_t link; + struct pcmcia_device *p_dev; dev_node_t node; struct net_device_stats stats; /* For transceiver monitoring */ @@ -142,8 +142,8 @@ DRV_NAME ".c " DRV_VERSION " 2001/10/13 00:08:50 (David Hinds)"; /*====================================================================*/ -static void tc589_config(dev_link_t *link); -static void tc589_release(dev_link_t *link); +static int tc589_config(struct pcmcia_device *link); +static void tc589_release(struct pcmcia_device *link); static u16 read_eeprom(kio_addr_t ioaddr, int index); static void tc589_reset(struct net_device *dev); @@ -170,10 +170,9 @@ static void tc589_detach(struct pcmcia_device *p_dev); ======================================================================*/ -static int tc589_attach(struct pcmcia_device *p_dev) +static int tc589_probe(struct pcmcia_device *link) { struct el3_private *lp; - dev_link_t *link; struct net_device *dev; DEBUG(0, "3c589_attach()\n"); @@ -183,8 +182,8 @@ static int tc589_attach(struct pcmcia_device *p_dev) if (!dev) return -ENOMEM; lp = netdev_priv(dev); - link = &lp->link; link->priv = dev; + lp->p_dev = link; spin_lock_init(&lp->lock); link->io.NumPorts1 = 16; @@ -194,7 +193,6 @@ static int tc589_attach(struct pcmcia_device *p_dev) link->irq.Handler = &el3_interrupt; link->irq.Instance = dev; link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; link->conf.ConfigIndex = 1; link->conf.Present = PRESENT_OPTION; @@ -213,13 +211,7 @@ static int tc589_attach(struct pcmcia_device *p_dev) #endif SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops); - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - tc589_config(link); - - return 0; + return tc589_config(link); } /* tc589_attach */ /*====================================================================== @@ -231,18 +223,16 @@ static int tc589_attach(struct pcmcia_device *p_dev) ======================================================================*/ -static void tc589_detach(struct pcmcia_device *p_dev) +static void tc589_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; DEBUG(0, "3c589_detach(0x%p)\n", link); - if (link->dev) + if (link->dev_node) unregister_netdev(dev); - if (link->state & DEV_CONFIG) - tc589_release(link); + tc589_release(link); free_netdev(dev); } /* tc589_detach */ @@ -258,9 +248,8 @@ static void tc589_detach(struct pcmcia_device *p_dev) #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) -static void tc589_config(dev_link_t *link) +static int tc589_config(struct pcmcia_device *link) { - client_handle_t handle = link->handle; struct net_device *dev = link->priv; struct el3_private *lp = netdev_priv(dev); tuple_t tuple; @@ -275,43 +264,40 @@ static void tc589_config(dev_link_t *link) phys_addr = (u16 *)dev->dev_addr; tuple.Attributes = 0; tuple.DesiredTuple = CISTPL_CONFIG; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); tuple.TupleData = (cisdata_t *)buf; tuple.TupleDataMax = sizeof(buf); tuple.TupleOffset = 0; - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; /* Is this a 3c562? */ tuple.DesiredTuple = CISTPL_MANFID; tuple.Attributes = TUPLE_RETURN_COMMON; - if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) && - (pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS)) { + if ((pcmcia_get_first_tuple(link, &tuple) == CS_SUCCESS) && + (pcmcia_get_tuple_data(link, &tuple) == CS_SUCCESS)) { if (le16_to_cpu(buf[0]) != MANFID_3COM) printk(KERN_INFO "3c589_cs: hmmm, is this really a " "3Com card??\n"); multi = (le16_to_cpu(buf[1]) == PRODID_3COM_3C562); } - - /* Configure card */ - link->state |= DEV_CONFIG; /* For the 3c562, the base address must be xx00-xx7f */ link->io.IOAddrLines = 16; for (i = j = 0; j < 0x400; j += 0x10) { if (multi && (j & 0x80)) continue; link->io.BasePort1 = j ^ 0x300; - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) break; } if (i != CS_SUCCESS) { - cs_error(link->handle, RequestIO, i); + cs_error(link, RequestIO, i); goto failed; } - CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq)); - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); dev->irq = link->irq.AssignedIRQ; dev->base_addr = link->io.BasePort1; @@ -321,8 +307,8 @@ static void tc589_config(dev_link_t *link) /* The 3c589 has an extra EEPROM for configuration info, including the hardware address. The 3c562 puts the address in the CIS. */ tuple.DesiredTuple = 0x88; - if (pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) { - pcmcia_get_tuple_data(handle, &tuple); + if (pcmcia_get_first_tuple(link, &tuple) == CS_SUCCESS) { + pcmcia_get_tuple_data(link, &tuple); for (i = 0; i < 3; i++) phys_addr[i] = htons(buf[i]); } else { @@ -346,13 +332,12 @@ static void tc589_config(dev_link_t *link) else printk(KERN_ERR "3c589_cs: invalid if_port requested\n"); - link->dev = &lp->node; - link->state &= ~DEV_CONFIG_PENDING; - SET_NETDEV_DEV(dev, &handle_to_dev(handle)); + link->dev_node = &lp->node; + SET_NETDEV_DEV(dev, &handle_to_dev(link)); if (register_netdev(dev) != 0) { printk(KERN_ERR "3c589_cs: register_netdev() failed\n"); - link->dev = NULL; + link->dev_node = NULL; goto failed; } @@ -366,14 +351,13 @@ static void tc589_config(dev_link_t *link) printk(KERN_INFO " %dK FIFO split %s Rx:Tx, %s xcvr\n", (fifo & 7) ? 32 : 8, ram_split[(fifo >> 16) & 3], if_names[dev->if_port]); - return; + return 0; cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); failed: tc589_release(link); - return; - + return -ENODEV; } /* tc589_config */ /*====================================================================== @@ -384,44 +368,28 @@ failed: ======================================================================*/ -static void tc589_release(dev_link_t *link) +static void tc589_release(struct pcmcia_device *link) { - DEBUG(0, "3c589_release(0x%p)\n", link); - - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - - link->state &= ~DEV_CONFIG; + pcmcia_disable_device(link); } -static int tc589_suspend(struct pcmcia_device *p_dev) +static int tc589_suspend(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - if (link->open) - netif_device_detach(dev); - pcmcia_release_configuration(link->handle); - } + if (link->open) + netif_device_detach(dev); return 0; } -static int tc589_resume(struct pcmcia_device *p_dev) +static int tc589_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - pcmcia_request_configuration(link->handle, &link->conf); - if (link->open) { - tc589_reset(dev); - netif_device_attach(dev); - } + if (link->open) { + tc589_reset(dev); + netif_device_attach(dev); } return 0; @@ -587,9 +555,9 @@ static int el3_config(struct net_device *dev, struct ifmap *map) static int el3_open(struct net_device *dev) { struct el3_private *lp = netdev_priv(dev); - dev_link_t *link = &lp->link; + struct pcmcia_device *link = lp->p_dev; - if (!DEV_OK(link)) + if (!pcmcia_dev_present(link)) return -ENODEV; link->open++; @@ -848,9 +816,9 @@ static struct net_device_stats *el3_get_stats(struct net_device *dev) { struct el3_private *lp = netdev_priv(dev); unsigned long flags; - dev_link_t *link = &lp->link; + struct pcmcia_device *link = lp->p_dev; - if (DEV_OK(link)) { + if (pcmcia_dev_present(link)) { spin_lock_irqsave(&lp->lock, flags); update_stats(dev); spin_unlock_irqrestore(&lp->lock, flags); @@ -950,11 +918,11 @@ static int el3_rx(struct net_device *dev) static void set_multicast_list(struct net_device *dev) { struct el3_private *lp = netdev_priv(dev); - dev_link_t *link = &lp->link; + struct pcmcia_device *link = lp->p_dev; kio_addr_t ioaddr = dev->base_addr; u16 opts = SetRxFilter | RxStation | RxBroadcast; - if (!(DEV_OK(link))) return; + if (!pcmcia_dev_present(link)) return; if (dev->flags & IFF_PROMISC) opts |= RxMulticast | RxProm; else if (dev->mc_count || (dev->flags & IFF_ALLMULTI)) @@ -965,12 +933,12 @@ static void set_multicast_list(struct net_device *dev) static int el3_close(struct net_device *dev) { struct el3_private *lp = netdev_priv(dev); - dev_link_t *link = &lp->link; + struct pcmcia_device *link = lp->p_dev; kio_addr_t ioaddr = dev->base_addr; DEBUG(1, "%s: shutting down ethercard.\n", dev->name); - if (DEV_OK(link)) { + if (pcmcia_dev_present(link)) { /* Turn off statistics ASAP. We update lp->stats below. */ outw(StatsDisable, ioaddr + EL3_CMD); @@ -1020,7 +988,7 @@ static struct pcmcia_driver tc589_driver = { .drv = { .name = "3c589_cs", }, - .probe = tc589_attach, + .probe = tc589_probe, .remove = tc589_detach, .id_table = tc589_ids, .suspend = tc589_suspend, diff --git a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c index aa5581369399..56233afcb2b3 100644 --- a/drivers/net/pcmcia/axnet_cs.c +++ b/drivers/net/pcmcia/axnet_cs.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "../8390.h" #include @@ -85,8 +86,8 @@ static char *version = /*====================================================================*/ -static void axnet_config(dev_link_t *link); -static void axnet_release(dev_link_t *link); +static int axnet_config(struct pcmcia_device *link); +static void axnet_release(struct pcmcia_device *link); static int axnet_open(struct net_device *dev); static int axnet_close(struct net_device *dev); static int axnet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); @@ -116,7 +117,7 @@ static irqreturn_t ax_interrupt(int irq, void *dev_id, struct pt_regs *regs); /*====================================================================*/ typedef struct axnet_dev_t { - dev_link_t link; + struct pcmcia_device *p_dev; dev_node_t node; caddr_t base; struct timer_list watchdog; @@ -141,10 +142,9 @@ static inline axnet_dev_t *PRIV(struct net_device *dev) ======================================================================*/ -static int axnet_attach(struct pcmcia_device *p_dev) +static int axnet_probe(struct pcmcia_device *link) { axnet_dev_t *info; - dev_link_t *link; struct net_device *dev; DEBUG(0, "axnet_attach()\n"); @@ -156,7 +156,7 @@ static int axnet_attach(struct pcmcia_device *p_dev) return -ENOMEM; info = PRIV(dev); - link = &info->link; + info->p_dev = link; link->priv = dev; link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; link->irq.IRQInfo1 = IRQ_LEVEL_ID; @@ -168,13 +168,7 @@ static int axnet_attach(struct pcmcia_device *p_dev) dev->do_ioctl = &axnet_ioctl; SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops); - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - axnet_config(link); - - return 0; + return axnet_config(link); } /* axnet_attach */ /*====================================================================== @@ -186,18 +180,16 @@ static int axnet_attach(struct pcmcia_device *p_dev) ======================================================================*/ -static void axnet_detach(struct pcmcia_device *p_dev) +static void axnet_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; DEBUG(0, "axnet_detach(0x%p)\n", link); - if (link->dev) + if (link->dev_node) unregister_netdev(dev); - if (link->state & DEV_CONFIG) - axnet_release(link); + axnet_release(link); free_netdev(dev); } /* axnet_detach */ @@ -208,7 +200,7 @@ static void axnet_detach(struct pcmcia_device *p_dev) ======================================================================*/ -static int get_prom(dev_link_t *link) +static int get_prom(struct pcmcia_device *link) { struct net_device *dev = link->priv; kio_addr_t ioaddr = dev->base_addr; @@ -262,7 +254,7 @@ static int get_prom(dev_link_t *link) #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) -static int try_io_port(dev_link_t *link) +static int try_io_port(struct pcmcia_device *link) { int j, ret; if (link->io.NumPorts1 == 32) { @@ -283,25 +275,23 @@ static int try_io_port(dev_link_t *link) for (j = 0; j < 0x400; j += 0x20) { link->io.BasePort1 = j ^ 0x300; link->io.BasePort2 = (j ^ 0x300) + 0x10; - ret = pcmcia_request_io(link->handle, &link->io); + ret = pcmcia_request_io(link, &link->io); if (ret == CS_SUCCESS) return ret; } return ret; } else { - return pcmcia_request_io(link->handle, &link->io); + return pcmcia_request_io(link, &link->io); } } -static void axnet_config(dev_link_t *link) +static int axnet_config(struct pcmcia_device *link) { - client_handle_t handle = link->handle; struct net_device *dev = link->priv; axnet_dev_t *info = PRIV(dev); tuple_t tuple; cisparse_t parse; int i, j, last_ret, last_fn; u_short buf[64]; - config_info_t conf; DEBUG(0, "axnet_config(0x%p)\n", link); @@ -310,29 +300,22 @@ static void axnet_config(dev_link_t *link) tuple.TupleDataMax = sizeof(buf); tuple.TupleOffset = 0; tuple.DesiredTuple = CISTPL_CONFIG; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; /* don't trust the CIS on this; Linksys got it wrong */ link->conf.Present = 0x63; - /* Configure card */ - link->state |= DEV_CONFIG; - - /* Look up current Vcc */ - CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf)); - link->conf.Vcc = conf.Vcc; - tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; tuple.Attributes = 0; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); while (last_ret == CS_SUCCESS) { cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); cistpl_io_t *io = &(parse.cftable_entry.io); - if (pcmcia_get_tuple_data(handle, &tuple) != 0 || - pcmcia_parse_tuple(handle, &tuple, &parse) != 0 || + if (pcmcia_get_tuple_data(link, &tuple) != 0 || + pcmcia_parse_tuple(link, &tuple, &parse) != 0 || cfg->index == 0 || cfg->io.nwin == 0) goto next_entry; @@ -354,21 +337,21 @@ static void axnet_config(dev_link_t *link) if (last_ret == CS_SUCCESS) break; } next_entry: - last_ret = pcmcia_get_next_tuple(handle, &tuple); + last_ret = pcmcia_get_next_tuple(link, &tuple); } if (last_ret != CS_SUCCESS) { - cs_error(handle, RequestIO, last_ret); + cs_error(link, RequestIO, last_ret); goto failed; } - CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); if (link->io.NumPorts2 == 8) { link->conf.Attributes |= CONF_ENABLE_SPKR; link->conf.Status = CCSR_AUDIO_ENA; } - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); dev->irq = link->irq.AssignedIRQ; dev->base_addr = link->io.BasePort1; @@ -405,7 +388,7 @@ static void axnet_config(dev_link_t *link) Bit 2 of CCSR is active low. */ if (i == 32) { conf_reg_t reg = { 0, CS_WRITE, CISREG_CCSR, 0x04 }; - pcmcia_access_configuration_register(link->handle, ®); + pcmcia_access_configuration_register(link, ®); for (i = 0; i < 32; i++) { j = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 1); if ((j != 0) && (j != 0xffff)) break; @@ -413,13 +396,12 @@ static void axnet_config(dev_link_t *link) } info->phy_id = (i < 32) ? i : -1; - link->dev = &info->node; - link->state &= ~DEV_CONFIG_PENDING; - SET_NETDEV_DEV(dev, &handle_to_dev(handle)); + link->dev_node = &info->node; + SET_NETDEV_DEV(dev, &handle_to_dev(link)); if (register_netdev(dev) != 0) { printk(KERN_NOTICE "axnet_cs: register_netdev() failed\n"); - link->dev = NULL; + link->dev_node = NULL; goto failed; } @@ -435,14 +417,13 @@ static void axnet_config(dev_link_t *link) } else { printk(KERN_NOTICE " No MII transceivers found!\n"); } - return; + return 0; cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); failed: axnet_release(link); - link->state &= ~DEV_CONFIG_PENDING; - return; + return -ENODEV; } /* axnet_config */ /*====================================================================== @@ -453,45 +434,29 @@ failed: ======================================================================*/ -static void axnet_release(dev_link_t *link) +static void axnet_release(struct pcmcia_device *link) { - DEBUG(0, "axnet_release(0x%p)\n", link); - - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - - link->state &= ~DEV_CONFIG; + pcmcia_disable_device(link); } -static int axnet_suspend(struct pcmcia_device *p_dev) +static int axnet_suspend(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - if (link->open) - netif_device_detach(dev); - pcmcia_release_configuration(link->handle); - } + if (link->open) + netif_device_detach(dev); return 0; } -static int axnet_resume(struct pcmcia_device *p_dev) +static int axnet_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - pcmcia_request_configuration(link->handle, &link->conf); - if (link->open) { - axnet_reset_8390(dev); - AX88190_init(dev, 1); - netif_device_attach(dev); - } + if (link->open) { + axnet_reset_8390(dev); + AX88190_init(dev, 1); + netif_device_attach(dev); } return 0; @@ -561,11 +526,11 @@ static void mdio_write(kio_addr_t addr, int phy_id, int loc, int value) static int axnet_open(struct net_device *dev) { axnet_dev_t *info = PRIV(dev); - dev_link_t *link = &info->link; + struct pcmcia_device *link = info->p_dev; DEBUG(2, "axnet_open('%s')\n", dev->name); - if (!DEV_OK(link)) + if (!pcmcia_dev_present(link)) return -ENODEV; link->open++; @@ -587,7 +552,7 @@ static int axnet_open(struct net_device *dev) static int axnet_close(struct net_device *dev) { axnet_dev_t *info = PRIV(dev); - dev_link_t *link = &info->link; + struct pcmcia_device *link = info->p_dev; DEBUG(2, "axnet_close('%s')\n", dev->name); @@ -832,7 +797,7 @@ static struct pcmcia_driver axnet_cs_driver = { .drv = { .name = "axnet_cs", }, - .probe = axnet_attach, + .probe = axnet_probe, .remove = axnet_detach, .id_table = axnet_ids, .suspend = axnet_suspend, @@ -1682,17 +1647,67 @@ static struct net_device_stats *get_stats(struct net_device *dev) return &ei_local->stat; } +/* + * Form the 64 bit 8390 multicast table from the linked list of addresses + * associated with this dev structure. + */ + +static inline void make_mc_bits(u8 *bits, struct net_device *dev) +{ + struct dev_mc_list *dmi; + u32 crc; + + for (dmi=dev->mc_list; dmi; dmi=dmi->next) { + + crc = ether_crc(ETH_ALEN, dmi->dmi_addr); + /* + * The 8390 uses the 6 most significant bits of the + * CRC to index the multicast table. + */ + bits[crc>>29] |= (1<<((crc>>26)&7)); + } +} + /** * do_set_multicast_list - set/clear multicast filter * @dev: net device for which multicast filter is adjusted * - * Set or clear the multicast filter for this adaptor. May be called - * from a BH in 2.1.x. Must be called with lock held. + * Set or clear the multicast filter for this adaptor. + * Must be called with lock held. */ static void do_set_multicast_list(struct net_device *dev) { long e8390_base = dev->base_addr; + int i; + struct ei_device *ei_local = (struct ei_device*)netdev_priv(dev); + + if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI))) { + memset(ei_local->mcfilter, 0, 8); + if (dev->mc_list) + make_mc_bits(ei_local->mcfilter, dev); + } else { + /* set to accept-all */ + memset(ei_local->mcfilter, 0xFF, 8); + } + + /* + * DP8390 manuals don't specify any magic sequence for altering + * the multicast regs on an already running card. To be safe, we + * ensure multicast mode is off prior to loading up the new hash + * table. If this proves to be not enough, we can always resort + * to stopping the NIC, loading the table and then restarting. + */ + + if (netif_running(dev)) + outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); + + outb_p(E8390_NODMA + E8390_PAGE1, e8390_base + E8390_CMD); + for(i = 0; i < 8; i++) + { + outb_p(ei_local->mcfilter[i], e8390_base + EN1_MULT_SHIFT(i)); + } + outb_p(E8390_NODMA + E8390_PAGE0, e8390_base + E8390_CMD); if(dev->flags&IFF_PROMISC) outb_p(E8390_RXCONFIG | 0x58, e8390_base + EN0_RXCR); @@ -1794,12 +1809,6 @@ static void AX88190_init(struct net_device *dev, int startp) if(inb_p(e8390_base + EN1_PHYS_SHIFT(i))!=dev->dev_addr[i]) printk(KERN_ERR "Hw. address read/write mismap %d\n",i); } - /* - * Initialize the multicast list to accept-all. If we enable multicast - * the higher levels can do the filtering. - */ - for (i = 0; i < 8; i++) - outb_p(0xff, e8390_base + EN1_MULT + i); outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG); outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); diff --git a/drivers/net/pcmcia/com20020_cs.c b/drivers/net/pcmcia/com20020_cs.c index 2827a48ea37c..441de824ab6b 100644 --- a/drivers/net/pcmcia/com20020_cs.c +++ b/drivers/net/pcmcia/com20020_cs.c @@ -118,8 +118,8 @@ MODULE_LICENSE("GPL"); /*====================================================================*/ -static void com20020_config(dev_link_t *link); -static void com20020_release(dev_link_t *link); +static int com20020_config(struct pcmcia_device *link); +static void com20020_release(struct pcmcia_device *link); static void com20020_detach(struct pcmcia_device *p_dev); @@ -138,9 +138,8 @@ typedef struct com20020_dev_t { ======================================================================*/ -static int com20020_attach(struct pcmcia_device *p_dev) +static int com20020_probe(struct pcmcia_device *p_dev) { - dev_link_t *link; com20020_dev_t *info; struct net_device *dev; struct arcnet_local *lp; @@ -148,10 +147,6 @@ static int com20020_attach(struct pcmcia_device *p_dev) DEBUG(0, "com20020_attach()\n"); /* Create new network device */ - link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL); - if (!link) - return -ENOMEM; - info = kmalloc(sizeof(struct com20020_dev_t), GFP_KERNEL); if (!info) goto fail_alloc_info; @@ -161,7 +156,6 @@ static int com20020_attach(struct pcmcia_device *p_dev) goto fail_alloc_dev; memset(info, 0, sizeof(struct com20020_dev_t)); - memset(link, 0, sizeof(struct dev_link_t)); lp = dev->priv; lp->timeout = timeout; lp->backplane = backplane; @@ -172,28 +166,23 @@ static int com20020_attach(struct pcmcia_device *p_dev) /* fill in our module parameters as defaults */ dev->dev_addr[0] = node; - link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; - link->io.NumPorts1 = 16; - link->io.IOAddrLines = 16; - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; - link->irq.IRQInfo1 = IRQ_LEVEL_ID; - link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; - link->conf.IntType = INT_MEMORY_AND_IO; - link->conf.Present = PRESENT_OPTION; + p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + p_dev->io.NumPorts1 = 16; + p_dev->io.IOAddrLines = 16; + p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID; + p_dev->conf.Attributes = CONF_ENABLE_IRQ; + p_dev->conf.IntType = INT_MEMORY_AND_IO; + p_dev->conf.Present = PRESENT_OPTION; - link->irq.Instance = info->dev = dev; - link->priv = info; + p_dev->irq.Instance = info->dev = dev; + p_dev->priv = info; - link->state |= DEV_PRESENT; - com20020_config(link); - - return 0; + return com20020_config(p_dev); fail_alloc_dev: kfree(info); fail_alloc_info: - kfree(link); return -ENOMEM; } /* com20020_attach */ @@ -206,9 +195,8 @@ fail_alloc_info: ======================================================================*/ -static void com20020_detach(struct pcmcia_device *p_dev) +static void com20020_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct com20020_dev_t *info = link->priv; struct net_device *dev = info->dev; @@ -216,7 +204,7 @@ static void com20020_detach(struct pcmcia_device *p_dev) DEBUG(0, "com20020_detach(0x%p)\n", link); - if (link->dev) { + if (link->dev_node) { DEBUG(1,"unregister...\n"); unregister_netdev(dev); @@ -229,8 +217,7 @@ static void com20020_detach(struct pcmcia_device *p_dev) free_irq(dev->irq, dev); } - if (link->state & DEV_CONFIG) - com20020_release(link); + com20020_release(link); /* Unlink device structure, free bits */ DEBUG(1,"unlinking...\n"); @@ -245,8 +232,6 @@ static void com20020_detach(struct pcmcia_device *p_dev) DEBUG(1,"kfree2...\n"); kfree(info); } - DEBUG(1,"kfree3...\n"); - kfree(link); } /* com20020_detach */ @@ -261,10 +246,9 @@ static void com20020_detach(struct pcmcia_device *p_dev) #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) -static void com20020_config(dev_link_t *link) +static int com20020_config(struct pcmcia_device *link) { struct arcnet_local *lp; - client_handle_t handle; tuple_t tuple; cisparse_t parse; com20020_dev_t *info; @@ -273,7 +257,6 @@ static void com20020_config(dev_link_t *link) u_char buf[64]; int ioaddr; - handle = link->handle; info = link->priv; dev = info->dev; @@ -286,14 +269,11 @@ static void com20020_config(dev_link_t *link) tuple.TupleDataMax = 64; tuple.TupleOffset = 0; tuple.DesiredTuple = CISTPL_CONFIG; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; - /* Configure card */ - link->state |= DEV_CONFIG; - DEBUG(1,"arcnet: baseport1 is %Xh\n", link->io.BasePort1); i = !CS_SUCCESS; if (!link->io.BasePort1) @@ -301,13 +281,13 @@ static void com20020_config(dev_link_t *link) for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10) { link->io.BasePort1 = ioaddr; - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) break; } } else - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i != CS_SUCCESS) { @@ -321,7 +301,7 @@ static void com20020_config(dev_link_t *link) DEBUG(1,"arcnet: request IRQ %d (%Xh/%Xh)\n", link->irq.AssignedIRQ, link->irq.IRQInfo1, link->irq.IRQInfo2); - i = pcmcia_request_irq(link->handle, &link->irq); + i = pcmcia_request_irq(link, &link->irq); if (i != CS_SUCCESS) { DEBUG(1,"arcnet: requestIRQ failed totally!\n"); @@ -330,7 +310,7 @@ static void com20020_config(dev_link_t *link) dev->irq = link->irq.AssignedIRQ; - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); if (com20020_check(dev)) { @@ -342,15 +322,14 @@ static void com20020_config(dev_link_t *link) lp->card_name = "PCMCIA COM20020"; lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */ - link->dev = &info->node; - link->state &= ~DEV_CONFIG_PENDING; - SET_NETDEV_DEV(dev, &handle_to_dev(handle)); + link->dev_node = &info->node; + SET_NETDEV_DEV(dev, &handle_to_dev(link)); i = com20020_found(dev, 0); /* calls register_netdev */ if (i != 0) { DEBUG(1,KERN_NOTICE "com20020_cs: com20020_found() failed\n"); - link->dev = NULL; + link->dev_node = NULL; goto failed; } @@ -358,13 +337,14 @@ static void com20020_config(dev_link_t *link) DEBUG(1,KERN_INFO "%s: port %#3lx, irq %d\n", dev->name, dev->base_addr, dev->irq); - return; + return 0; cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); failed: DEBUG(1,"com20020_config failed...\n"); com20020_release(link); + return -ENODEV; } /* com20020_config */ /*====================================================================== @@ -375,52 +355,33 @@ failed: ======================================================================*/ -static void com20020_release(dev_link_t *link) +static void com20020_release(struct pcmcia_device *link) { - - DEBUG(1,"release...\n"); - - DEBUG(0, "com20020_release(0x%p)\n", link); - - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - - link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING); + DEBUG(0, "com20020_release(0x%p)\n", link); + pcmcia_disable_device(link); } -static int com20020_suspend(struct pcmcia_device *p_dev) +static int com20020_suspend(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); com20020_dev_t *info = link->priv; struct net_device *dev = info->dev; - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - if (link->open) { - netif_device_detach(dev); - } - pcmcia_release_configuration(link->handle); - } + if (link->open) + netif_device_detach(dev); return 0; } -static int com20020_resume(struct pcmcia_device *p_dev) +static int com20020_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); com20020_dev_t *info = link->priv; struct net_device *dev = info->dev; - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - pcmcia_request_configuration(link->handle, &link->conf); - if (link->open) { - int ioaddr = dev->base_addr; - struct arcnet_local *lp = dev->priv; - ARCRESET; - } - } + if (link->open) { + int ioaddr = dev->base_addr; + struct arcnet_local *lp = dev->priv; + ARCRESET; + } return 0; } @@ -436,7 +397,7 @@ static struct pcmcia_driver com20020_cs_driver = { .drv = { .name = "com20020_cs", }, - .probe = com20020_attach, + .probe = com20020_probe, .remove = com20020_detach, .id_table = com20020_ids, .suspend = com20020_suspend, diff --git a/drivers/net/pcmcia/fmvj18x_cs.c b/drivers/net/pcmcia/fmvj18x_cs.c index b7ac14ba8877..09b11761cdfa 100644 --- a/drivers/net/pcmcia/fmvj18x_cs.c +++ b/drivers/net/pcmcia/fmvj18x_cs.c @@ -84,10 +84,10 @@ static char *version = DRV_NAME ".c " DRV_VERSION " 2002/03/23"; /* PCMCIA event handlers */ -static void fmvj18x_config(dev_link_t *link); -static int fmvj18x_get_hwinfo(dev_link_t *link, u_char *node_id); -static int fmvj18x_setup_mfc(dev_link_t *link); -static void fmvj18x_release(dev_link_t *link); +static int fmvj18x_config(struct pcmcia_device *link); +static int fmvj18x_get_hwinfo(struct pcmcia_device *link, u_char *node_id); +static int fmvj18x_setup_mfc(struct pcmcia_device *link); +static void fmvj18x_release(struct pcmcia_device *link); static void fmvj18x_detach(struct pcmcia_device *p_dev); /* @@ -116,7 +116,7 @@ typedef enum { MBH10302, MBH10304, TDK, CONTEC, LA501, UNGERMANN, driver specific data structure */ typedef struct local_info_t { - dev_link_t link; + struct pcmcia_device *p_dev; dev_node_t node; struct net_device_stats stats; long open_time; @@ -228,10 +228,9 @@ typedef struct local_info_t { #define BANK_1U 0x24 /* bank 1 (CONFIG_1) */ #define BANK_2U 0x28 /* bank 2 (CONFIG_1) */ -static int fmvj18x_attach(struct pcmcia_device *p_dev) +static int fmvj18x_probe(struct pcmcia_device *link) { local_info_t *lp; - dev_link_t *link; struct net_device *dev; DEBUG(0, "fmvj18x_attach()\n"); @@ -241,8 +240,8 @@ static int fmvj18x_attach(struct pcmcia_device *p_dev) if (!dev) return -ENOMEM; lp = netdev_priv(dev); - link = &lp->link; link->priv = dev; + lp->p_dev = link; /* The io structure describes IO port mapping */ link->io.NumPorts1 = 32; @@ -257,7 +256,6 @@ static int fmvj18x_attach(struct pcmcia_device *p_dev) /* General socket configuration */ link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; /* The FMVJ18x specific entries in the device structure. */ @@ -274,29 +272,21 @@ static int fmvj18x_attach(struct pcmcia_device *p_dev) #endif SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops); - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - fmvj18x_config(link); - - return 0; + return fmvj18x_config(link); } /* fmvj18x_attach */ /*====================================================================*/ -static void fmvj18x_detach(struct pcmcia_device *p_dev) +static void fmvj18x_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; DEBUG(0, "fmvj18x_detach(0x%p)\n", link); - if (link->dev) + if (link->dev_node) unregister_netdev(dev); - if (link->state & DEV_CONFIG) - fmvj18x_release(link); + fmvj18x_release(link); free_netdev(dev); } /* fmvj18x_detach */ @@ -306,7 +296,7 @@ static void fmvj18x_detach(struct pcmcia_device *p_dev) #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) -static int mfc_try_io_port(dev_link_t *link) +static int mfc_try_io_port(struct pcmcia_device *link) { int i, ret; static const kio_addr_t serial_base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; @@ -318,13 +308,13 @@ static int mfc_try_io_port(dev_link_t *link) link->io.NumPorts2 = 0; printk(KERN_NOTICE "fmvj18x_cs: out of resource for serial\n"); } - ret = pcmcia_request_io(link->handle, &link->io); + ret = pcmcia_request_io(link, &link->io); if (ret == CS_SUCCESS) return ret; } return ret; } -static int ungermann_try_io_port(dev_link_t *link) +static int ungermann_try_io_port(struct pcmcia_device *link) { int ret; kio_addr_t ioaddr; @@ -334,7 +324,7 @@ static int ungermann_try_io_port(dev_link_t *link) */ for (ioaddr = 0x300; ioaddr < 0x3e0; ioaddr += 0x20) { link->io.BasePort1 = ioaddr; - ret = pcmcia_request_io(link->handle, &link->io); + ret = pcmcia_request_io(link, &link->io); if (ret == CS_SUCCESS) { /* calculate ConfigIndex value */ link->conf.ConfigIndex = @@ -345,9 +335,8 @@ static int ungermann_try_io_port(dev_link_t *link) return ret; /* RequestIO failed */ } -static void fmvj18x_config(dev_link_t *link) +static int fmvj18x_config(struct pcmcia_device *link) { - client_handle_t handle = link->handle; struct net_device *dev = link->priv; local_info_t *lp = netdev_priv(dev); tuple_t tuple; @@ -366,42 +355,34 @@ static void fmvj18x_config(dev_link_t *link) registers. */ tuple.DesiredTuple = CISTPL_CONFIG; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); tuple.TupleData = (u_char *)buf; tuple.TupleDataMax = 64; tuple.TupleOffset = 0; - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); - - /* Configure card */ - link->state |= DEV_CONFIG; + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; tuple.DesiredTuple = CISTPL_FUNCE; tuple.TupleOffset = 0; - if (pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) { + if (pcmcia_get_first_tuple(link, &tuple) == CS_SUCCESS) { /* Yes, I have CISTPL_FUNCE. Let's check CISTPL_MANFID */ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigIndex = parse.cftable_entry.index; tuple.DesiredTuple = CISTPL_MANFID; - if (pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); + if (pcmcia_get_first_tuple(link, &tuple) == CS_SUCCESS) + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); else buf[0] = 0xffff; switch (le16_to_cpu(buf[0])) { case MANFID_TDK: cardtype = TDK; - if (le16_to_cpu(buf[1]) == PRODID_TDK_CF010) { - cs_status_t status; - pcmcia_get_status(handle, &status); - if (status.CardState & CS_EVENT_3VCARD) - link->conf.Vcc = 33; /* inserted in 3.3V slot */ - } else if (le16_to_cpu(buf[1]) == PRODID_TDK_GN3410 + if (le16_to_cpu(buf[1]) == PRODID_TDK_GN3410 || le16_to_cpu(buf[1]) == PRODID_TDK_NP9610 || le16_to_cpu(buf[1]) == PRODID_TDK_MN3200) { /* MultiFunction Card */ @@ -429,8 +410,8 @@ static void fmvj18x_config(dev_link_t *link) } else { /* old type card */ tuple.DesiredTuple = CISTPL_MANFID; - if (pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); + if (pcmcia_get_first_tuple(link, &tuple) == CS_SUCCESS) + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); else buf[0] = 0xffff; switch (le16_to_cpu(buf[0])) { @@ -461,10 +442,10 @@ static void fmvj18x_config(dev_link_t *link) ret = ungermann_try_io_port(link); if (ret != CS_SUCCESS) goto cs_failed; } else { - CS_CHECK(RequestIO, pcmcia_request_io(link->handle, &link->io)); + CS_CHECK(RequestIO, pcmcia_request_io(link, &link->io)); } - CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq)); - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); dev->irq = link->irq.AssignedIRQ; dev->base_addr = link->io.BasePort1; @@ -493,17 +474,17 @@ static void fmvj18x_config(dev_link_t *link) case CONTEC: tuple.DesiredTuple = CISTPL_FUNCE; tuple.TupleOffset = 0; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); tuple.TupleOffset = 0; - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); if (cardtype == MBH10304) { /* MBH10304's CIS_FUNCE is corrupted */ node_id = &(tuple.TupleData[5]); card_name = "FMV-J182"; } else { while (tuple.TupleData[0] != CISTPL_FUNCE_LAN_NODE_ID ) { - CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); + CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); } node_id = &(tuple.TupleData[2]); if( cardtype == TDK ) { @@ -545,13 +526,12 @@ static void fmvj18x_config(dev_link_t *link) } lp->cardtype = cardtype; - link->dev = &lp->node; - link->state &= ~DEV_CONFIG_PENDING; - SET_NETDEV_DEV(dev, &handle_to_dev(handle)); + link->dev_node = &lp->node; + SET_NETDEV_DEV(dev, &handle_to_dev(link)); if (register_netdev(dev) != 0) { printk(KERN_NOTICE "fmvj18x_cs: register_netdev() failed\n"); - link->dev = NULL; + link->dev_node = NULL; goto failed; } @@ -564,19 +544,18 @@ static void fmvj18x_config(dev_link_t *link) for (i = 0; i < 6; i++) printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n")); - return; + return 0; cs_failed: /* All Card Services errors end up here */ - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); failed: fmvj18x_release(link); - link->state &= ~DEV_CONFIG_PENDING; - + return -ENODEV; } /* fmvj18x_config */ /*====================================================================*/ -static int fmvj18x_get_hwinfo(dev_link_t *link, u_char *node_id) +static int fmvj18x_get_hwinfo(struct pcmcia_device *link, u_char *node_id) { win_req_t req; memreq_t mem; @@ -587,9 +566,9 @@ static int fmvj18x_get_hwinfo(dev_link_t *link, u_char *node_id) req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; req.Base = 0; req.Size = 0; req.AccessSpeed = 0; - i = pcmcia_request_window(&link->handle, &req, &link->win); + i = pcmcia_request_window(&link, &req, &link->win); if (i != CS_SUCCESS) { - cs_error(link->handle, RequestWindow, i); + cs_error(link, RequestWindow, i); return -1; } @@ -623,13 +602,13 @@ static int fmvj18x_get_hwinfo(dev_link_t *link, u_char *node_id) iounmap(base); j = pcmcia_release_window(link->win); if (j != CS_SUCCESS) - cs_error(link->handle, ReleaseWindow, j); + cs_error(link, ReleaseWindow, j); return (i != 0x200) ? 0 : -1; } /* fmvj18x_get_hwinfo */ /*====================================================================*/ -static int fmvj18x_setup_mfc(dev_link_t *link) +static int fmvj18x_setup_mfc(struct pcmcia_device *link) { win_req_t req; memreq_t mem; @@ -642,9 +621,9 @@ static int fmvj18x_setup_mfc(dev_link_t *link) req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; req.Base = 0; req.Size = 0; req.AccessSpeed = 0; - i = pcmcia_request_window(&link->handle, &req, &link->win); + i = pcmcia_request_window(&link, &req, &link->win); if (i != CS_SUCCESS) { - cs_error(link->handle, RequestWindow, i); + cs_error(link, RequestWindow, i); return -1; } @@ -666,54 +645,35 @@ static int fmvj18x_setup_mfc(dev_link_t *link) iounmap(base); j = pcmcia_release_window(link->win); if (j != CS_SUCCESS) - cs_error(link->handle, ReleaseWindow, j); + cs_error(link, ReleaseWindow, j); return 0; } /*====================================================================*/ -static void fmvj18x_release(dev_link_t *link) +static void fmvj18x_release(struct pcmcia_device *link) { - - DEBUG(0, "fmvj18x_release(0x%p)\n", link); - - /* Don't bother checking to see if these succeed or not */ - pcmcia_release_window(link->win); - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - - link->state &= ~DEV_CONFIG; + DEBUG(0, "fmvj18x_release(0x%p)\n", link); + pcmcia_disable_device(link); } -static int fmvj18x_suspend(struct pcmcia_device *p_dev) +static int fmvj18x_suspend(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - if (link->open) - netif_device_detach(dev); - pcmcia_release_configuration(link->handle); - } - + if (link->open) + netif_device_detach(dev); return 0; } -static int fmvj18x_resume(struct pcmcia_device *p_dev) +static int fmvj18x_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - pcmcia_request_configuration(link->handle, &link->conf); - if (link->open) { - fjn_reset(dev); - netif_device_attach(dev); - } + if (link->open) { + fjn_reset(dev); + netif_device_attach(dev); } return 0; @@ -751,7 +711,7 @@ static struct pcmcia_driver fmvj18x_cs_driver = { .drv = { .name = "fmvj18x_cs", }, - .probe = fmvj18x_attach, + .probe = fmvj18x_probe, .remove = fmvj18x_detach, .id_table = fmvj18x_ids, .suspend = fmvj18x_suspend, @@ -1148,11 +1108,11 @@ static int fjn_config(struct net_device *dev, struct ifmap *map){ static int fjn_open(struct net_device *dev) { struct local_info_t *lp = netdev_priv(dev); - dev_link_t *link = &lp->link; + struct pcmcia_device *link = lp->p_dev; DEBUG(4, "fjn_open('%s').\n", dev->name); - if (!DEV_OK(link)) + if (!pcmcia_dev_present(link)) return -ENODEV; link->open++; @@ -1173,7 +1133,7 @@ static int fjn_open(struct net_device *dev) static int fjn_close(struct net_device *dev) { struct local_info_t *lp = netdev_priv(dev); - dev_link_t *link = &lp->link; + struct pcmcia_device *link = lp->p_dev; kio_addr_t ioaddr = dev->base_addr; DEBUG(4, "fjn_close('%s').\n", dev->name); diff --git a/drivers/net/pcmcia/ibmtr_cs.c b/drivers/net/pcmcia/ibmtr_cs.c index b9c7e39576f5..b8fe70b85641 100644 --- a/drivers/net/pcmcia/ibmtr_cs.c +++ b/drivers/net/pcmcia/ibmtr_cs.c @@ -105,15 +105,15 @@ MODULE_LICENSE("GPL"); /*====================================================================*/ -static void ibmtr_config(dev_link_t *link); +static int ibmtr_config(struct pcmcia_device *link); static void ibmtr_hw_setup(struct net_device *dev, u_int mmiobase); -static void ibmtr_release(dev_link_t *link); +static void ibmtr_release(struct pcmcia_device *link); static void ibmtr_detach(struct pcmcia_device *p_dev); /*====================================================================*/ typedef struct ibmtr_dev_t { - dev_link_t link; + struct pcmcia_device *p_dev; struct net_device *dev; dev_node_t node; window_handle_t sram_win_handle; @@ -138,12 +138,11 @@ static struct ethtool_ops netdev_ethtool_ops = { ======================================================================*/ -static int ibmtr_attach(struct pcmcia_device *p_dev) +static int ibmtr_attach(struct pcmcia_device *link) { ibmtr_dev_t *info; - dev_link_t *link; struct net_device *dev; - + DEBUG(0, "ibmtr_attach()\n"); /* Create new token-ring device */ @@ -156,7 +155,7 @@ static int ibmtr_attach(struct pcmcia_device *p_dev) return -ENOMEM; } - link = &info->link; + info->p_dev = link; link->priv = info; info->ti = netdev_priv(dev); @@ -167,21 +166,14 @@ static int ibmtr_attach(struct pcmcia_device *p_dev) link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->irq.Handler = &tok_interrupt; link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; link->conf.Present = PRESENT_OPTION; link->irq.Instance = info->dev = dev; - + SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops); - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT; - ibmtr_config(link); - - return 0; + return ibmtr_config(link); } /* ibmtr_attach */ /*====================================================================== @@ -193,23 +185,22 @@ static int ibmtr_attach(struct pcmcia_device *p_dev) ======================================================================*/ -static void ibmtr_detach(struct pcmcia_device *p_dev) +static void ibmtr_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct ibmtr_dev_t *info = link->priv; struct net_device *dev = info->dev; DEBUG(0, "ibmtr_detach(0x%p)\n", link); - if (link->dev) + if (link->dev_node) unregister_netdev(dev); { struct tok_info *ti = netdev_priv(dev); del_timer_sync(&(ti->tr_timer)); } - if (link->state & DEV_CONFIG) - ibmtr_release(link); + + ibmtr_release(link); free_netdev(dev); kfree(info); @@ -226,9 +217,8 @@ static void ibmtr_detach(struct pcmcia_device *p_dev) #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) -static void ibmtr_config(dev_link_t *link) +static int ibmtr_config(struct pcmcia_device *link) { - client_handle_t handle = link->handle; ibmtr_dev_t *info = link->priv; struct net_device *dev = info->dev; struct tok_info *ti = netdev_priv(dev); @@ -246,29 +236,25 @@ static void ibmtr_config(dev_link_t *link) tuple.TupleDataMax = 64; tuple.TupleOffset = 0; tuple.DesiredTuple = CISTPL_CONFIG; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; - - /* Configure card */ - link->state |= DEV_CONFIG; - link->conf.ConfigIndex = 0x61; /* Determine if this is PRIMARY or ALTERNATE. */ /* Try PRIMARY card at 0xA20-0xA23 */ link->io.BasePort1 = 0xA20; - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i != CS_SUCCESS) { /* Couldn't get 0xA20-0xA23. Try ALTERNATE at 0xA24-0xA27. */ link->io.BasePort1 = 0xA24; - CS_CHECK(RequestIO, pcmcia_request_io(link->handle, &link->io)); + CS_CHECK(RequestIO, pcmcia_request_io(link, &link->io)); } dev->base_addr = link->io.BasePort1; - CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); dev->irq = link->irq.AssignedIRQ; ti->irq = link->irq.AssignedIRQ; ti->global_int_enable=GLOBAL_INT_ENABLE+((dev->irq==9) ? 2 : dev->irq); @@ -279,7 +265,7 @@ static void ibmtr_config(dev_link_t *link) req.Base = 0; req.Size = 0x2000; req.AccessSpeed = 250; - CS_CHECK(RequestWindow, pcmcia_request_window(&link->handle, &req, &link->win)); + CS_CHECK(RequestWindow, pcmcia_request_window(&link, &req, &link->win)); mem.CardOffset = mmiobase; mem.Page = 0; @@ -292,7 +278,7 @@ static void ibmtr_config(dev_link_t *link) req.Base = 0; req.Size = sramsize * 1024; req.AccessSpeed = 250; - CS_CHECK(RequestWindow, pcmcia_request_window(&link->handle, &req, &info->sram_win_handle)); + CS_CHECK(RequestWindow, pcmcia_request_window(&link, &req, &info->sram_win_handle)); mem.CardOffset = srambase; mem.Page = 0; @@ -302,21 +288,20 @@ static void ibmtr_config(dev_link_t *link) ti->sram_virt = ioremap(req.Base, req.Size); ti->sram_phys = req.Base; - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); /* Set up the Token-Ring Controller Configuration Register and turn on the card. Check the "Local Area Network Credit Card Adapters Technical Reference" SC30-3585 for this info. */ ibmtr_hw_setup(dev, mmiobase); - link->dev = &info->node; - link->state &= ~DEV_CONFIG_PENDING; - SET_NETDEV_DEV(dev, &handle_to_dev(handle)); + link->dev_node = &info->node; + SET_NETDEV_DEV(dev, &handle_to_dev(link)); i = ibmtr_probe_card(dev); if (i != 0) { printk(KERN_NOTICE "ibmtr_cs: register_netdev() failed\n"); - link->dev = NULL; + link->dev_node = NULL; goto failed; } @@ -330,12 +315,13 @@ static void ibmtr_config(dev_link_t *link) for (i = 0; i < TR_ALEN; i++) printk("%02X", dev->dev_addr[i]); printk("\n"); - return; + return 0; cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); failed: ibmtr_release(link); + return -ENODEV; } /* ibmtr_config */ /*====================================================================== @@ -346,56 +332,41 @@ failed: ======================================================================*/ -static void ibmtr_release(dev_link_t *link) +static void ibmtr_release(struct pcmcia_device *link) { - ibmtr_dev_t *info = link->priv; - struct net_device *dev = info->dev; - - DEBUG(0, "ibmtr_release(0x%p)\n", link); - - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - if (link->win) { - struct tok_info *ti = netdev_priv(dev); - iounmap(ti->mmio); - pcmcia_release_window(link->win); - pcmcia_release_window(info->sram_win_handle); - } - - link->state &= ~DEV_CONFIG; -} - -static int ibmtr_suspend(struct pcmcia_device *p_dev) -{ - dev_link_t *link = dev_to_instance(p_dev); ibmtr_dev_t *info = link->priv; struct net_device *dev = info->dev; - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - if (link->open) - netif_device_detach(dev); - pcmcia_release_configuration(link->handle); - } + DEBUG(0, "ibmtr_release(0x%p)\n", link); + + if (link->win) { + struct tok_info *ti = netdev_priv(dev); + iounmap(ti->mmio); + pcmcia_release_window(info->sram_win_handle); + } + pcmcia_disable_device(link); +} + +static int ibmtr_suspend(struct pcmcia_device *link) +{ + ibmtr_dev_t *info = link->priv; + struct net_device *dev = info->dev; + + if (link->open) + netif_device_detach(dev); return 0; } -static int ibmtr_resume(struct pcmcia_device *p_dev) +static int ibmtr_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); ibmtr_dev_t *info = link->priv; struct net_device *dev = info->dev; - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - pcmcia_request_configuration(link->handle, &link->conf); - if (link->open) { - ibmtr_probe(dev); /* really? */ - netif_device_attach(dev); - } - } + if (link->open) { + ibmtr_probe(dev); /* really? */ + netif_device_attach(dev); + } return 0; } diff --git a/drivers/net/pcmcia/nmclan_cs.c b/drivers/net/pcmcia/nmclan_cs.c index 787176c57fd9..4260c2128f47 100644 --- a/drivers/net/pcmcia/nmclan_cs.c +++ b/drivers/net/pcmcia/nmclan_cs.c @@ -362,7 +362,7 @@ typedef struct _mace_statistics { } mace_statistics; typedef struct _mace_private { - dev_link_t link; + struct pcmcia_device *p_dev; dev_node_t node; struct net_device_stats linux_stats; /* Linux statistics counters */ mace_statistics mace_stats; /* MACE chip statistics counters */ @@ -417,8 +417,8 @@ INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG); Function Prototypes ---------------------------------------------------------------------------- */ -static void nmclan_config(dev_link_t *link); -static void nmclan_release(dev_link_t *link); +static int nmclan_config(struct pcmcia_device *link); +static void nmclan_release(struct pcmcia_device *link); static void nmclan_reset(struct net_device *dev); static int mace_config(struct net_device *dev, struct ifmap *map); @@ -443,10 +443,9 @@ nmclan_attach Services. ---------------------------------------------------------------------------- */ -static int nmclan_attach(struct pcmcia_device *p_dev) +static int nmclan_probe(struct pcmcia_device *link) { mace_private *lp; - dev_link_t *link; struct net_device *dev; DEBUG(0, "nmclan_attach()\n"); @@ -457,7 +456,7 @@ static int nmclan_attach(struct pcmcia_device *p_dev) if (!dev) return -ENOMEM; lp = netdev_priv(dev); - link = &lp->link; + lp->p_dev = link; link->priv = dev; spin_lock_init(&lp->bank_lock); @@ -469,7 +468,6 @@ static int nmclan_attach(struct pcmcia_device *p_dev) link->irq.Handler = &mace_interrupt; link->irq.Instance = dev; link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; link->conf.ConfigIndex = 1; link->conf.Present = PRESENT_OPTION; @@ -489,13 +487,7 @@ static int nmclan_attach(struct pcmcia_device *p_dev) dev->watchdog_timeo = TX_TIMEOUT; #endif - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - nmclan_config(link); - - return 0; + return nmclan_config(link); } /* nmclan_attach */ /* ---------------------------------------------------------------------------- @@ -506,18 +498,16 @@ nmclan_detach when the device is released. ---------------------------------------------------------------------------- */ -static void nmclan_detach(struct pcmcia_device *p_dev) +static void nmclan_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; DEBUG(0, "nmclan_detach(0x%p)\n", link); - if (link->dev) + if (link->dev_node) unregister_netdev(dev); - if (link->state & DEV_CONFIG) - nmclan_release(link); + nmclan_release(link); free_netdev(dev); } /* nmclan_detach */ @@ -661,9 +651,8 @@ nmclan_config #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) -static void nmclan_config(dev_link_t *link) +static int nmclan_config(struct pcmcia_device *link) { - client_handle_t handle = link->handle; struct net_device *dev = link->priv; mace_private *lp = netdev_priv(dev); tuple_t tuple; @@ -679,17 +668,14 @@ static void nmclan_config(dev_link_t *link) tuple.TupleDataMax = 64; tuple.TupleOffset = 0; tuple.DesiredTuple = CISTPL_CONFIG; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; - /* Configure card */ - link->state |= DEV_CONFIG; - - CS_CHECK(RequestIO, pcmcia_request_io(handle, &link->io)); - CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq)); - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf)); + CS_CHECK(RequestIO, pcmcia_request_io(link, &link->io)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); dev->irq = link->irq.AssignedIRQ; dev->base_addr = link->io.BasePort1; @@ -700,8 +686,8 @@ static void nmclan_config(dev_link_t *link) tuple.TupleData = buf; tuple.TupleDataMax = 64; tuple.TupleOffset = 0; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); memcpy(dev->dev_addr, tuple.TupleData, ETHER_ADDR_LEN); /* Verify configuration by reading the MACE ID. */ @@ -716,8 +702,7 @@ static void nmclan_config(dev_link_t *link) } else { printk(KERN_NOTICE "nmclan_cs: mace id not found: %x %x should" " be 0x40 0x?9\n", sig[0], sig[1]); - link->state &= ~DEV_CONFIG_PENDING; - return; + return -ENODEV; } } @@ -730,14 +715,13 @@ static void nmclan_config(dev_link_t *link) else printk(KERN_NOTICE "nmclan_cs: invalid if_port requested\n"); - link->dev = &lp->node; - link->state &= ~DEV_CONFIG_PENDING; - SET_NETDEV_DEV(dev, &handle_to_dev(handle)); + link->dev_node = &lp->node; + SET_NETDEV_DEV(dev, &handle_to_dev(link)); i = register_netdev(dev); if (i != 0) { printk(KERN_NOTICE "nmclan_cs: register_netdev() failed\n"); - link->dev = NULL; + link->dev_node = NULL; goto failed; } @@ -747,14 +731,13 @@ static void nmclan_config(dev_link_t *link) dev->name, dev->base_addr, dev->irq, if_names[dev->if_port]); for (i = 0; i < 6; i++) printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n")); - return; + return 0; cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); failed: - nmclan_release(link); - return; - + nmclan_release(link); + return -ENODEV; } /* nmclan_config */ /* ---------------------------------------------------------------------------- @@ -763,46 +746,29 @@ nmclan_release net device, and release the PCMCIA configuration. If the device is still open, this will be postponed until it is closed. ---------------------------------------------------------------------------- */ -static void nmclan_release(dev_link_t *link) +static void nmclan_release(struct pcmcia_device *link) { - - DEBUG(0, "nmclan_release(0x%p)\n", link); - - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - - link->state &= ~DEV_CONFIG; + DEBUG(0, "nmclan_release(0x%p)\n", link); + pcmcia_disable_device(link); } -static int nmclan_suspend(struct pcmcia_device *p_dev) +static int nmclan_suspend(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - if (link->open) - netif_device_detach(dev); - pcmcia_release_configuration(link->handle); - } - + if (link->open) + netif_device_detach(dev); return 0; } -static int nmclan_resume(struct pcmcia_device *p_dev) +static int nmclan_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - pcmcia_request_configuration(link->handle, &link->conf); - if (link->open) { - nmclan_reset(dev); - netif_device_attach(dev); - } + if (link->open) { + nmclan_reset(dev); + netif_device_attach(dev); } return 0; @@ -818,7 +784,7 @@ static void nmclan_reset(struct net_device *dev) mace_private *lp = netdev_priv(dev); #if RESET_XILINX - dev_link_t *link = &lp->link; + struct pcmcia_device *link = &lp->link; conf_reg_t reg; u_long OrigCorValue; @@ -827,7 +793,7 @@ static void nmclan_reset(struct net_device *dev) reg.Action = CS_READ; reg.Offset = CISREG_COR; reg.Value = 0; - pcmcia_access_configuration_register(link->handle, ®); + pcmcia_access_configuration_register(link, ®); OrigCorValue = reg.Value; /* Reset Xilinx */ @@ -836,12 +802,12 @@ static void nmclan_reset(struct net_device *dev) DEBUG(1, "nmclan_reset: OrigCorValue=0x%lX, resetting...\n", OrigCorValue); reg.Value = COR_SOFT_RESET; - pcmcia_access_configuration_register(link->handle, ®); + pcmcia_access_configuration_register(link, ®); /* Need to wait for 20 ms for PCMCIA to finish reset. */ /* Restore original COR configuration index */ reg.Value = COR_LEVEL_REQ | (OrigCorValue & COR_CONFIG_MASK); - pcmcia_access_configuration_register(link->handle, ®); + pcmcia_access_configuration_register(link, ®); /* Xilinx is now completely reset along with the MACE chip. */ lp->tx_free_frames=AM2150_MAX_TX_FRAMES; @@ -885,9 +851,9 @@ static int mace_open(struct net_device *dev) { kio_addr_t ioaddr = dev->base_addr; mace_private *lp = netdev_priv(dev); - dev_link_t *link = &lp->link; + struct pcmcia_device *link = lp->p_dev; - if (!DEV_OK(link)) + if (!pcmcia_dev_present(link)) return -ENODEV; link->open++; @@ -908,7 +874,7 @@ static int mace_close(struct net_device *dev) { kio_addr_t ioaddr = dev->base_addr; mace_private *lp = netdev_priv(dev); - dev_link_t *link = &lp->link; + struct pcmcia_device *link = lp->p_dev; DEBUG(2, "%s: shutting down ethercard.\n", dev->name); @@ -963,12 +929,12 @@ mace_start_xmit static void mace_tx_timeout(struct net_device *dev) { mace_private *lp = netdev_priv(dev); - dev_link_t *link = &lp->link; + struct pcmcia_device *link = lp->p_dev; printk(KERN_NOTICE "%s: transmit timed out -- ", dev->name); #if RESET_ON_TIMEOUT printk("resetting card\n"); - pcmcia_reset_card(link->handle, NULL); + pcmcia_reset_card(link, NULL); #else /* #if RESET_ON_TIMEOUT */ printk("NOT resetting card\n"); #endif /* #if RESET_ON_TIMEOUT */ @@ -1635,7 +1601,7 @@ static struct pcmcia_driver nmclan_cs_driver = { .drv = { .name = "nmclan_cs", }, - .probe = nmclan_attach, + .probe = nmclan_probe, .remove = nmclan_detach, .id_table = nmclan_ids, .suspend = nmclan_suspend, diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c index b46e5f703efa..506e777c5f06 100644 --- a/drivers/net/pcmcia/pcnet_cs.c +++ b/drivers/net/pcmcia/pcnet_cs.c @@ -103,8 +103,8 @@ module_param_array(hw_addr, int, NULL, 0); /*====================================================================*/ static void mii_phy_probe(struct net_device *dev); -static void pcnet_config(dev_link_t *link); -static void pcnet_release(dev_link_t *link); +static int pcnet_config(struct pcmcia_device *link); +static void pcnet_release(struct pcmcia_device *link); static int pcnet_open(struct net_device *dev); static int pcnet_close(struct net_device *dev); static int ei_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); @@ -113,9 +113,9 @@ static irqreturn_t ei_irq_wrapper(int irq, void *dev_id, struct pt_regs *regs); static void ei_watchdog(u_long arg); static void pcnet_reset_8390(struct net_device *dev); static int set_config(struct net_device *dev, struct ifmap *map); -static int setup_shmem_window(dev_link_t *link, int start_pg, +static int setup_shmem_window(struct pcmcia_device *link, int start_pg, int stop_pg, int cm_offset); -static int setup_dma_config(dev_link_t *link, int start_pg, +static int setup_dma_config(struct pcmcia_device *link, int start_pg, int stop_pg); static void pcnet_detach(struct pcmcia_device *p_dev); @@ -214,7 +214,7 @@ static hw_info_t dl10019_info = { 0, 0, 0, 0, IS_DL10019|HAS_MII }; static hw_info_t dl10022_info = { 0, 0, 0, 0, IS_DL10022|HAS_MII }; typedef struct pcnet_dev_t { - dev_link_t link; + struct pcmcia_device *p_dev; dev_node_t node; u_int flags; void __iomem *base; @@ -240,10 +240,9 @@ static inline pcnet_dev_t *PRIV(struct net_device *dev) ======================================================================*/ -static int pcnet_probe(struct pcmcia_device *p_dev) +static int pcnet_probe(struct pcmcia_device *link) { pcnet_dev_t *info; - dev_link_t *link; struct net_device *dev; DEBUG(0, "pcnet_attach()\n"); @@ -252,7 +251,7 @@ static int pcnet_probe(struct pcmcia_device *p_dev) dev = __alloc_ei_netdev(sizeof(pcnet_dev_t)); if (!dev) return -ENOMEM; info = PRIV(dev); - link = &info->link; + info->p_dev = link; link->priv = dev; link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; @@ -265,13 +264,7 @@ static int pcnet_probe(struct pcmcia_device *p_dev) dev->stop = &pcnet_close; dev->set_config = &set_config; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - pcnet_config(link); - - return 0; + return pcnet_config(link); } /* pcnet_attach */ /*====================================================================== @@ -283,18 +276,16 @@ static int pcnet_probe(struct pcmcia_device *p_dev) ======================================================================*/ -static void pcnet_detach(struct pcmcia_device *p_dev) +static void pcnet_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; DEBUG(0, "pcnet_detach(0x%p)\n", link); - if (link->dev) + if (link->dev_node) unregister_netdev(dev); - if (link->state & DEV_CONFIG) - pcnet_release(link); + pcnet_release(link); free_netdev(dev); } /* pcnet_detach */ @@ -306,7 +297,7 @@ static void pcnet_detach(struct pcmcia_device *p_dev) ======================================================================*/ -static hw_info_t *get_hwinfo(dev_link_t *link) +static hw_info_t *get_hwinfo(struct pcmcia_device *link) { struct net_device *dev = link->priv; win_req_t req; @@ -318,9 +309,9 @@ static hw_info_t *get_hwinfo(dev_link_t *link) req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; req.Base = 0; req.Size = 0; req.AccessSpeed = 0; - i = pcmcia_request_window(&link->handle, &req, &link->win); + i = pcmcia_request_window(&link, &req, &link->win); if (i != CS_SUCCESS) { - cs_error(link->handle, RequestWindow, i); + cs_error(link, RequestWindow, i); return NULL; } @@ -343,7 +334,7 @@ static hw_info_t *get_hwinfo(dev_link_t *link) iounmap(virt); j = pcmcia_release_window(link->win); if (j != CS_SUCCESS) - cs_error(link->handle, ReleaseWindow, j); + cs_error(link, ReleaseWindow, j); return (i < NR_INFO) ? hw_info+i : NULL; } /* get_hwinfo */ @@ -355,7 +346,7 @@ static hw_info_t *get_hwinfo(dev_link_t *link) ======================================================================*/ -static hw_info_t *get_prom(dev_link_t *link) +static hw_info_t *get_prom(struct pcmcia_device *link) { struct net_device *dev = link->priv; kio_addr_t ioaddr = dev->base_addr; @@ -409,7 +400,7 @@ static hw_info_t *get_prom(dev_link_t *link) ======================================================================*/ -static hw_info_t *get_dl10019(dev_link_t *link) +static hw_info_t *get_dl10019(struct pcmcia_device *link) { struct net_device *dev = link->priv; int i; @@ -431,7 +422,7 @@ static hw_info_t *get_dl10019(dev_link_t *link) ======================================================================*/ -static hw_info_t *get_ax88190(dev_link_t *link) +static hw_info_t *get_ax88190(struct pcmcia_device *link) { struct net_device *dev = link->priv; kio_addr_t ioaddr = dev->base_addr; @@ -464,7 +455,7 @@ static hw_info_t *get_ax88190(dev_link_t *link) ======================================================================*/ -static hw_info_t *get_hwired(dev_link_t *link) +static hw_info_t *get_hwired(struct pcmcia_device *link) { struct net_device *dev = link->priv; int i; @@ -491,7 +482,7 @@ static hw_info_t *get_hwired(dev_link_t *link) #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) -static int try_io_port(dev_link_t *link) +static int try_io_port(struct pcmcia_device *link) { int j, ret; if (link->io.NumPorts1 == 32) { @@ -512,18 +503,17 @@ static int try_io_port(dev_link_t *link) for (j = 0; j < 0x400; j += 0x20) { link->io.BasePort1 = j ^ 0x300; link->io.BasePort2 = (j ^ 0x300) + 0x10; - ret = pcmcia_request_io(link->handle, &link->io); + ret = pcmcia_request_io(link, &link->io); if (ret == CS_SUCCESS) return ret; } return ret; } else { - return pcmcia_request_io(link->handle, &link->io); + return pcmcia_request_io(link, &link->io); } } -static void pcnet_config(dev_link_t *link) +static int pcnet_config(struct pcmcia_device *link) { - client_handle_t handle = link->handle; struct net_device *dev = link->priv; pcnet_dev_t *info = PRIV(dev); tuple_t tuple; @@ -531,7 +521,6 @@ static void pcnet_config(dev_link_t *link) int i, last_ret, last_fn, start_pg, stop_pg, cm_offset; int manfid = 0, prodid = 0, has_shmem = 0; u_short buf[64]; - config_info_t conf; hw_info_t *hw_info; DEBUG(0, "pcnet_config(0x%p)\n", link); @@ -541,36 +530,29 @@ static void pcnet_config(dev_link_t *link) tuple.TupleDataMax = sizeof(buf); tuple.TupleOffset = 0; tuple.DesiredTuple = CISTPL_CONFIG; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; - /* Configure card */ - link->state |= DEV_CONFIG; - - /* Look up current Vcc */ - CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf)); - link->conf.Vcc = conf.Vcc; - tuple.DesiredTuple = CISTPL_MANFID; tuple.Attributes = TUPLE_RETURN_COMMON; - if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) && - (pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS)) { + if ((pcmcia_get_first_tuple(link, &tuple) == CS_SUCCESS) && + (pcmcia_get_tuple_data(link, &tuple) == CS_SUCCESS)) { manfid = le16_to_cpu(buf[0]); prodid = le16_to_cpu(buf[1]); } tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; tuple.Attributes = 0; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); while (last_ret == CS_SUCCESS) { cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); cistpl_io_t *io = &(parse.cftable_entry.io); - if (pcmcia_get_tuple_data(handle, &tuple) != 0 || - pcmcia_parse_tuple(handle, &tuple, &parse) != 0 || + if (pcmcia_get_tuple_data(link, &tuple) != 0 || + pcmcia_parse_tuple(link, &tuple, &parse) != 0 || cfg->index == 0 || cfg->io.nwin == 0) goto next_entry; @@ -594,14 +576,14 @@ static void pcnet_config(dev_link_t *link) if (last_ret == CS_SUCCESS) break; } next_entry: - last_ret = pcmcia_get_next_tuple(handle, &tuple); + last_ret = pcmcia_get_next_tuple(link, &tuple); } if (last_ret != CS_SUCCESS) { - cs_error(handle, RequestIO, last_ret); + cs_error(link, RequestIO, last_ret); goto failed; } - CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); if (link->io.NumPorts2 == 8) { link->conf.Attributes |= CONF_ENABLE_SPKR; @@ -611,7 +593,7 @@ static void pcnet_config(dev_link_t *link) (prodid == PRODID_IBM_HOME_AND_AWAY)) link->conf.ConfigIndex |= 0x10; - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); dev->irq = link->irq.AssignedIRQ; dev->base_addr = link->io.BasePort1; if (info->flags & HAS_MISC_REG) { @@ -679,9 +661,8 @@ static void pcnet_config(dev_link_t *link) info->eth_phy = 0; } - link->dev = &info->node; - link->state &= ~DEV_CONFIG_PENDING; - SET_NETDEV_DEV(dev, &handle_to_dev(handle)); + link->dev_node = &info->node; + SET_NETDEV_DEV(dev, &handle_to_dev(link)); #ifdef CONFIG_NET_POLL_CONTROLLER dev->poll_controller = ei_poll; @@ -689,7 +670,7 @@ static void pcnet_config(dev_link_t *link) if (register_netdev(dev) != 0) { printk(KERN_NOTICE "pcnet_cs: register_netdev() failed\n"); - link->dev = NULL; + link->dev_node = NULL; goto failed; } @@ -712,14 +693,13 @@ static void pcnet_config(dev_link_t *link) printk(" hw_addr "); for (i = 0; i < 6; i++) printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n")); - return; + return 0; cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); failed: pcnet_release(link); - link->state &= ~DEV_CONFIG_PENDING; - return; + return -ENODEV; } /* pcnet_config */ /*====================================================================== @@ -730,21 +710,16 @@ failed: ======================================================================*/ -static void pcnet_release(dev_link_t *link) +static void pcnet_release(struct pcmcia_device *link) { - pcnet_dev_t *info = PRIV(link->priv); + pcnet_dev_t *info = PRIV(link->priv); - DEBUG(0, "pcnet_release(0x%p)\n", link); + DEBUG(0, "pcnet_release(0x%p)\n", link); - if (info->flags & USE_SHMEM) { - iounmap(info->base); - pcmcia_release_window(link->win); - } - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); + if (info->flags & USE_SHMEM) + iounmap(info->base); - link->state &= ~DEV_CONFIG; + pcmcia_disable_device(link); } /*====================================================================== @@ -756,34 +731,24 @@ static void pcnet_release(dev_link_t *link) ======================================================================*/ -static int pcnet_suspend(struct pcmcia_device *p_dev) +static int pcnet_suspend(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - if (link->open) - netif_device_detach(dev); - pcmcia_release_configuration(link->handle); - } + if (link->open) + netif_device_detach(dev); return 0; } -static int pcnet_resume(struct pcmcia_device *p_dev) +static int pcnet_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - pcmcia_request_configuration(link->handle, &link->conf); - if (link->open) { - pcnet_reset_8390(dev); - NS8390_init(dev, 1); - netif_device_attach(dev); - } + if (link->open) { + pcnet_reset_8390(dev); + NS8390_init(dev, 1); + netif_device_attach(dev); } return 0; @@ -1023,11 +988,11 @@ static void mii_phy_probe(struct net_device *dev) static int pcnet_open(struct net_device *dev) { pcnet_dev_t *info = PRIV(dev); - dev_link_t *link = &info->link; - + struct pcmcia_device *link = info->p_dev; + DEBUG(2, "pcnet_open('%s')\n", dev->name); - if (!DEV_OK(link)) + if (!pcmcia_dev_present(link)) return -ENODEV; link->open++; @@ -1051,7 +1016,7 @@ static int pcnet_open(struct net_device *dev) static int pcnet_close(struct net_device *dev) { pcnet_dev_t *info = PRIV(dev); - dev_link_t *link = &info->link; + struct pcmcia_device *link = info->p_dev; DEBUG(2, "pcnet_close('%s')\n", dev->name); @@ -1429,7 +1394,7 @@ static void dma_block_output(struct net_device *dev, int count, /*====================================================================*/ -static int setup_dma_config(dev_link_t *link, int start_pg, +static int setup_dma_config(struct pcmcia_device *link, int start_pg, int stop_pg) { struct net_device *dev = link->priv; @@ -1532,7 +1497,7 @@ static void shmem_block_output(struct net_device *dev, int count, /*====================================================================*/ -static int setup_shmem_window(dev_link_t *link, int start_pg, +static int setup_shmem_window(struct pcmcia_device *link, int start_pg, int stop_pg, int cm_offset) { struct net_device *dev = link->priv; @@ -1554,7 +1519,7 @@ static int setup_shmem_window(dev_link_t *link, int start_pg, req.Attributes |= WIN_USE_WAIT; req.Base = 0; req.Size = window_size; req.AccessSpeed = mem_speed; - CS_CHECK(RequestWindow, pcmcia_request_window(&link->handle, &req, &link->win)); + CS_CHECK(RequestWindow, pcmcia_request_window(&link, &req, &link->win)); mem.CardOffset = (start_pg << 8) + cm_offset; offset = mem.CardOffset % window_size; @@ -1595,7 +1560,7 @@ static int setup_shmem_window(dev_link_t *link, int start_pg, return 0; cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); failed: return 1; } diff --git a/drivers/net/pcmcia/smc91c92_cs.c b/drivers/net/pcmcia/smc91c92_cs.c index 8839c4faafd6..e74bf5014ef6 100644 --- a/drivers/net/pcmcia/smc91c92_cs.c +++ b/drivers/net/pcmcia/smc91c92_cs.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include @@ -103,7 +104,7 @@ static const char *version = #define MEMORY_WAIT_TIME 8 struct smc_private { - dev_link_t link; + struct pcmcia_device *p_dev; spinlock_t lock; u_short manfid; u_short cardid; @@ -278,8 +279,8 @@ enum RxCfg { RxAllMulti = 0x0004, RxPromisc = 0x0002, /*====================================================================*/ static void smc91c92_detach(struct pcmcia_device *p_dev); -static void smc91c92_config(dev_link_t *link); -static void smc91c92_release(dev_link_t *link); +static int smc91c92_config(struct pcmcia_device *link); +static void smc91c92_release(struct pcmcia_device *link); static int smc_open(struct net_device *dev); static int smc_close(struct net_device *dev); @@ -308,10 +309,9 @@ static struct ethtool_ops ethtool_ops; ======================================================================*/ -static int smc91c92_attach(struct pcmcia_device *p_dev) +static int smc91c92_probe(struct pcmcia_device *link) { struct smc_private *smc; - dev_link_t *link; struct net_device *dev; DEBUG(0, "smc91c92_attach()\n"); @@ -321,7 +321,7 @@ static int smc91c92_attach(struct pcmcia_device *p_dev) if (!dev) return -ENOMEM; smc = netdev_priv(dev); - link = &smc->link; + smc->p_dev = link; link->priv = dev; spin_lock_init(&smc->lock); @@ -333,7 +333,6 @@ static int smc91c92_attach(struct pcmcia_device *p_dev) link->irq.Handler = &smc_interrupt; link->irq.Instance = dev; link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; /* The SMC91c92-specific entries in the device structure. */ @@ -357,13 +356,7 @@ static int smc91c92_attach(struct pcmcia_device *p_dev) smc->mii_if.phy_id_mask = 0x1f; smc->mii_if.reg_num_mask = 0x1f; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - smc91c92_config(link); - - return 0; + return smc91c92_config(link); } /* smc91c92_attach */ /*====================================================================== @@ -375,18 +368,16 @@ static int smc91c92_attach(struct pcmcia_device *p_dev) ======================================================================*/ -static void smc91c92_detach(struct pcmcia_device *p_dev) +static void smc91c92_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; DEBUG(0, "smc91c92_detach(0x%p)\n", link); - if (link->dev) + if (link->dev_node) unregister_netdev(dev); - if (link->state & DEV_CONFIG) - smc91c92_release(link); + smc91c92_release(link); free_netdev(dev); } /* smc91c92_detach */ @@ -414,7 +405,7 @@ static int cvt_ascii_address(struct net_device *dev, char *s) /*====================================================================*/ -static int first_tuple(client_handle_t handle, tuple_t *tuple, +static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { int i; @@ -425,7 +416,7 @@ static int first_tuple(client_handle_t handle, tuple_t *tuple, return pcmcia_parse_tuple(handle, tuple, parse); } -static int next_tuple(client_handle_t handle, tuple_t *tuple, +static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { int i; @@ -447,7 +438,7 @@ static int next_tuple(client_handle_t handle, tuple_t *tuple, ======================================================================*/ -static int mhz_3288_power(dev_link_t *link) +static int mhz_3288_power(struct pcmcia_device *link) { struct net_device *dev = link->priv; struct smc_private *smc = netdev_priv(dev); @@ -469,7 +460,7 @@ static int mhz_3288_power(dev_link_t *link) return 0; } -static int mhz_mfc_config(dev_link_t *link) +static int mhz_mfc_config(struct pcmcia_device *link) { struct net_device *dev = link->priv; struct smc_private *smc = netdev_priv(dev); @@ -504,7 +495,7 @@ static int mhz_mfc_config(dev_link_t *link) tuple->TupleDataMax = 255; tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY; - i = first_tuple(link->handle, tuple, parse); + i = first_tuple(link, tuple, parse); /* The Megahertz combo cards have modem-like CIS entries, so we have to explicitly try a bunch of port combinations. */ while (i == CS_SUCCESS) { @@ -513,11 +504,11 @@ static int mhz_mfc_config(dev_link_t *link) for (k = 0; k < 0x400; k += 0x10) { if (k & 0x80) continue; link->io.BasePort1 = k ^ 0x300; - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) break; } if (i == CS_SUCCESS) break; - i = next_tuple(link->handle, tuple, parse); + i = next_tuple(link, tuple, parse); } if (i != CS_SUCCESS) goto free_cfg_mem; @@ -527,7 +518,7 @@ static int mhz_mfc_config(dev_link_t *link) req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; req.Base = req.Size = 0; req.AccessSpeed = 0; - i = pcmcia_request_window(&link->handle, &req, &link->win); + i = pcmcia_request_window(&link, &req, &link->win); if (i != CS_SUCCESS) goto free_cfg_mem; smc->base = ioremap(req.Base, req.Size); @@ -546,9 +537,8 @@ free_cfg_mem: return i; } -static int mhz_setup(dev_link_t *link) +static int mhz_setup(struct pcmcia_device *link) { - client_handle_t handle = link->handle; struct net_device *dev = link->priv; struct smc_cfg_mem *cfg_mem; tuple_t *tuple; @@ -571,13 +561,13 @@ static int mhz_setup(dev_link_t *link) /* Read the station address from the CIS. It is stored as the last (fourth) string in the Version 1 Version/ID tuple. */ tuple->DesiredTuple = CISTPL_VERS_1; - if (first_tuple(handle, tuple, parse) != CS_SUCCESS) { + if (first_tuple(link, tuple, parse) != CS_SUCCESS) { rc = -1; goto free_cfg_mem; } /* Ugh -- the EM1144 card has two VERS_1 tuples!?! */ - if (next_tuple(handle, tuple, parse) != CS_SUCCESS) - first_tuple(handle, tuple, parse); + if (next_tuple(link, tuple, parse) != CS_SUCCESS) + first_tuple(link, tuple, parse); if (parse->version_1.ns > 3) { station_addr = parse->version_1.str + parse->version_1.ofs[3]; if (cvt_ascii_address(dev, station_addr) == 0) { @@ -588,11 +578,11 @@ static int mhz_setup(dev_link_t *link) /* Another possibility: for the EM3288, in a special tuple */ tuple->DesiredTuple = 0x81; - if (pcmcia_get_first_tuple(handle, tuple) != CS_SUCCESS) { + if (pcmcia_get_first_tuple(link, tuple) != CS_SUCCESS) { rc = -1; goto free_cfg_mem; } - if (pcmcia_get_tuple_data(handle, tuple) != CS_SUCCESS) { + if (pcmcia_get_tuple_data(link, tuple) != CS_SUCCESS) { rc = -1; goto free_cfg_mem; } @@ -616,7 +606,7 @@ free_cfg_mem: ======================================================================*/ -static void mot_config(dev_link_t *link) +static void mot_config(struct pcmcia_device *link) { struct net_device *dev = link->priv; struct smc_private *smc = netdev_priv(dev); @@ -637,7 +627,7 @@ static void mot_config(dev_link_t *link) mdelay(100); } -static int mot_setup(dev_link_t *link) +static int mot_setup(struct pcmcia_device *link) { struct net_device *dev = link->priv; kio_addr_t ioaddr = dev->base_addr; @@ -671,7 +661,7 @@ static int mot_setup(dev_link_t *link) /*====================================================================*/ -static int smc_config(dev_link_t *link) +static int smc_config(struct pcmcia_device *link) { struct net_device *dev = link->priv; struct smc_cfg_mem *cfg_mem; @@ -696,16 +686,16 @@ static int smc_config(dev_link_t *link) tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY; link->io.NumPorts1 = 16; - i = first_tuple(link->handle, tuple, parse); + i = first_tuple(link, tuple, parse); while (i != CS_NO_MORE_ITEMS) { if (i == CS_SUCCESS) { link->conf.ConfigIndex = cf->index; link->io.BasePort1 = cf->io.win[0].base; link->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK; - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) break; } - i = next_tuple(link->handle, tuple, parse); + i = next_tuple(link, tuple, parse); } if (i == CS_SUCCESS) dev->base_addr = link->io.BasePort1; @@ -714,9 +704,8 @@ static int smc_config(dev_link_t *link) return i; } -static int smc_setup(dev_link_t *link) +static int smc_setup(struct pcmcia_device *link) { - client_handle_t handle = link->handle; struct net_device *dev = link->priv; struct smc_cfg_mem *cfg_mem; tuple_t *tuple; @@ -739,11 +728,11 @@ static int smc_setup(dev_link_t *link) /* Check for a LAN function extension tuple */ tuple->DesiredTuple = CISTPL_FUNCE; - i = first_tuple(handle, tuple, parse); + i = first_tuple(link, tuple, parse); while (i == CS_SUCCESS) { if (parse->funce.type == CISTPL_FUNCE_LAN_NODE_ID) break; - i = next_tuple(handle, tuple, parse); + i = next_tuple(link, tuple, parse); } if (i == CS_SUCCESS) { node_id = (cistpl_lan_node_id_t *)parse->funce.data; @@ -756,7 +745,7 @@ static int smc_setup(dev_link_t *link) } /* Try the third string in the Version 1 Version/ID tuple. */ tuple->DesiredTuple = CISTPL_VERS_1; - if (first_tuple(handle, tuple, parse) != CS_SUCCESS) { + if (first_tuple(link, tuple, parse) != CS_SUCCESS) { rc = -1; goto free_cfg_mem; } @@ -774,7 +763,7 @@ free_cfg_mem: /*====================================================================*/ -static int osi_config(dev_link_t *link) +static int osi_config(struct pcmcia_device *link) { struct net_device *dev = link->priv; static const kio_addr_t com[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; @@ -794,22 +783,21 @@ static int osi_config(dev_link_t *link) for (i = j = 0; j < 4; j++) { link->io.BasePort2 = com[j]; - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) break; } if (i != CS_SUCCESS) { /* Fallback: turn off hard decode */ link->conf.ConfigIndex = 0x03; link->io.NumPorts2 = 0; - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); } dev->base_addr = link->io.BasePort1 + 0x10; return i; } -static int osi_setup(dev_link_t *link, u_short manfid, u_short cardid) +static int osi_setup(struct pcmcia_device *link, u_short manfid, u_short cardid) { - client_handle_t handle = link->handle; struct net_device *dev = link->priv; struct smc_cfg_mem *cfg_mem; tuple_t *tuple; @@ -830,12 +818,12 @@ static int osi_setup(dev_link_t *link, u_short manfid, u_short cardid) /* Read the station address from tuple 0x90, subtuple 0x04 */ tuple->DesiredTuple = 0x90; - i = pcmcia_get_first_tuple(handle, tuple); + i = pcmcia_get_first_tuple(link, tuple); while (i == CS_SUCCESS) { - i = pcmcia_get_tuple_data(handle, tuple); + i = pcmcia_get_tuple_data(link, tuple); if ((i != CS_SUCCESS) || (buf[0] == 0x04)) break; - i = pcmcia_get_next_tuple(handle, tuple); + i = pcmcia_get_next_tuple(link, tuple); } if (i != CS_SUCCESS) { rc = -1; @@ -868,57 +856,47 @@ free_cfg_mem: return rc; } -static int smc91c92_suspend(struct pcmcia_device *p_dev) +static int smc91c92_suspend(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - if (link->open) - netif_device_detach(dev); - pcmcia_release_configuration(link->handle); - } + if (link->open) + netif_device_detach(dev); return 0; } -static int smc91c92_resume(struct pcmcia_device *p_dev) +static int smc91c92_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; struct smc_private *smc = netdev_priv(dev); int i; - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - if ((smc->manfid == MANFID_MEGAHERTZ) && - (smc->cardid == PRODID_MEGAHERTZ_EM3288)) - mhz_3288_power(link); - pcmcia_request_configuration(link->handle, &link->conf); - if (smc->manfid == MANFID_MOTOROLA) - mot_config(link); - if ((smc->manfid == MANFID_OSITECH) && - (smc->cardid != PRODID_OSITECH_SEVEN)) { - /* Power up the card and enable interrupts */ - set_bits(0x0300, dev->base_addr-0x10+OSITECH_AUI_PWR); - set_bits(0x0300, dev->base_addr-0x10+OSITECH_RESET_ISR); - } - if (((smc->manfid == MANFID_OSITECH) && - (smc->cardid == PRODID_OSITECH_SEVEN)) || - ((smc->manfid == MANFID_PSION) && - (smc->cardid == PRODID_PSION_NET100))) { - /* Download the Seven of Diamonds firmware */ - for (i = 0; i < sizeof(__Xilinx7OD); i++) { - outb(__Xilinx7OD[i], link->io.BasePort1+2); - udelay(50); - } - } - if (link->open) { - smc_reset(dev); - netif_device_attach(dev); + if ((smc->manfid == MANFID_MEGAHERTZ) && + (smc->cardid == PRODID_MEGAHERTZ_EM3288)) + mhz_3288_power(link); + if (smc->manfid == MANFID_MOTOROLA) + mot_config(link); + if ((smc->manfid == MANFID_OSITECH) && + (smc->cardid != PRODID_OSITECH_SEVEN)) { + /* Power up the card and enable interrupts */ + set_bits(0x0300, dev->base_addr-0x10+OSITECH_AUI_PWR); + set_bits(0x0300, dev->base_addr-0x10+OSITECH_RESET_ISR); + } + if (((smc->manfid == MANFID_OSITECH) && + (smc->cardid == PRODID_OSITECH_SEVEN)) || + ((smc->manfid == MANFID_PSION) && + (smc->cardid == PRODID_PSION_NET100))) { + /* Download the Seven of Diamonds firmware */ + for (i = 0; i < sizeof(__Xilinx7OD); i++) { + outb(__Xilinx7OD[i], link->io.BasePort1+2); + udelay(50); } } + if (link->open) { + smc_reset(dev); + netif_device_attach(dev); + } return 0; } @@ -931,7 +909,7 @@ static int smc91c92_resume(struct pcmcia_device *p_dev) ======================================================================*/ -static int check_sig(dev_link_t *link) +static int check_sig(struct pcmcia_device *link) { struct net_device *dev = link->priv; kio_addr_t ioaddr = dev->base_addr; @@ -964,13 +942,15 @@ static int check_sig(dev_link_t *link) } if (width) { - printk(KERN_INFO "smc91c92_cs: using 8-bit IO window.\n"); - smc91c92_suspend(link->handle); - pcmcia_release_io(link->handle, &link->io); - link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; - pcmcia_request_io(link->handle, &link->io); - smc91c92_resume(link->handle); - return check_sig(link); + modconf_t mod = { + .Attributes = CONF_IO_CHANGE_WIDTH, + }; + printk(KERN_INFO "smc91c92_cs: using 8-bit IO window.\n"); + + smc91c92_suspend(link); + pcmcia_modify_configuration(link, &mod); + smc91c92_resume(link); + return check_sig(link); } return -ENODEV; } @@ -984,11 +964,10 @@ static int check_sig(dev_link_t *link) ======================================================================*/ #define CS_EXIT_TEST(ret, svc, label) \ -if (ret != CS_SUCCESS) { cs_error(link->handle, svc, ret); goto label; } +if (ret != CS_SUCCESS) { cs_error(link, svc, ret); goto label; } -static void smc91c92_config(dev_link_t *link) +static int smc91c92_config(struct pcmcia_device *link) { - client_handle_t handle = link->handle; struct net_device *dev = link->priv; struct smc_private *smc = netdev_priv(dev); struct smc_cfg_mem *cfg_mem; @@ -1015,21 +994,18 @@ static void smc91c92_config(dev_link_t *link) tuple->TupleDataMax = 64; tuple->DesiredTuple = CISTPL_CONFIG; - i = first_tuple(handle, tuple, parse); + i = first_tuple(link, tuple, parse); CS_EXIT_TEST(i, ParseTuple, config_failed); link->conf.ConfigBase = parse->config.base; link->conf.Present = parse->config.rmask[0]; tuple->DesiredTuple = CISTPL_MANFID; tuple->Attributes = TUPLE_RETURN_COMMON; - if (first_tuple(handle, tuple, parse) == CS_SUCCESS) { + if (first_tuple(link, tuple, parse) == CS_SUCCESS) { smc->manfid = parse->manfid.manf; smc->cardid = parse->manfid.card; } - /* Configure card */ - link->state |= DEV_CONFIG; - if ((smc->manfid == MANFID_OSITECH) && (smc->cardid != PRODID_OSITECH_SEVEN)) { i = osi_config(link); @@ -1043,9 +1019,9 @@ static void smc91c92_config(dev_link_t *link) } CS_EXIT_TEST(i, RequestIO, config_failed); - i = pcmcia_request_irq(link->handle, &link->irq); + i = pcmcia_request_irq(link, &link->irq); CS_EXIT_TEST(i, RequestIRQ, config_failed); - i = pcmcia_request_configuration(link->handle, &link->conf); + i = pcmcia_request_configuration(link, &link->conf); CS_EXIT_TEST(i, RequestConfiguration, config_failed); if (smc->manfid == MANFID_MOTOROLA) @@ -1124,13 +1100,12 @@ static void smc91c92_config(dev_link_t *link) SMC_SELECT_BANK(0); } - link->dev = &smc->node; - link->state &= ~DEV_CONFIG_PENDING; - SET_NETDEV_DEV(dev, &handle_to_dev(handle)); + link->dev_node = &smc->node; + SET_NETDEV_DEV(dev, &handle_to_dev(link)); if (register_netdev(dev) != 0) { printk(KERN_ERR "smc91c92_cs: register_netdev() failed\n"); - link->dev = NULL; + link->dev_node = NULL; goto config_undo; } @@ -1160,15 +1135,14 @@ static void smc91c92_config(dev_link_t *link) } } kfree(cfg_mem); - return; + return 0; config_undo: unregister_netdev(dev); config_failed: /* CS_EXIT_TEST() calls jump to here... */ smc91c92_release(link); - link->state &= ~DEV_CONFIG_PENDING; kfree(cfg_mem); - + return -ENODEV; } /* smc91c92_config */ /*====================================================================== @@ -1179,22 +1153,15 @@ config_failed: /* CS_EXIT_TEST() calls jump to here... */ ======================================================================*/ -static void smc91c92_release(dev_link_t *link) +static void smc91c92_release(struct pcmcia_device *link) { - - DEBUG(0, "smc91c92_release(0x%p)\n", link); - - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - if (link->win) { - struct net_device *dev = link->priv; - struct smc_private *smc = netdev_priv(dev); - iounmap(smc->base); - pcmcia_release_window(link->win); - } - - link->state &= ~DEV_CONFIG; + DEBUG(0, "smc91c92_release(0x%p)\n", link); + if (link->win) { + struct net_device *dev = link->priv; + struct smc_private *smc = netdev_priv(dev); + iounmap(smc->base); + } + pcmcia_disable_device(link); } /*====================================================================== @@ -1283,7 +1250,7 @@ static void smc_dump(struct net_device *dev) static int smc_open(struct net_device *dev) { struct smc_private *smc = netdev_priv(dev); - dev_link_t *link = &smc->link; + struct pcmcia_device *link = smc->p_dev; #ifdef PCMCIA_DEBUG DEBUG(0, "%s: smc_open(%p), ID/Window %4.4x.\n", @@ -1292,7 +1259,7 @@ static int smc_open(struct net_device *dev) #endif /* Check that the PCMCIA card is still here. */ - if (!DEV_OK(link)) + if (!pcmcia_dev_present(link)) return -ENODEV; /* Physical device present signature. */ if (check_sig(link) < 0) { @@ -1320,7 +1287,7 @@ static int smc_open(struct net_device *dev) static int smc_close(struct net_device *dev) { struct smc_private *smc = netdev_priv(dev); - dev_link_t *link = &smc->link; + struct pcmcia_device *link = smc->p_dev; kio_addr_t ioaddr = dev->base_addr; DEBUG(0, "%s: smc_close(), status %4.4x.\n", @@ -2311,7 +2278,7 @@ static struct pcmcia_driver smc91c92_cs_driver = { .drv = { .name = "smc91c92_cs", }, - .probe = smc91c92_attach, + .probe = smc91c92_probe, .remove = smc91c92_detach, .id_table = smc91c92_ids, .suspend = smc91c92_suspend, diff --git a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c index eed496803fe4..71f45056a70c 100644 --- a/drivers/net/pcmcia/xirc2ps_cs.c +++ b/drivers/net/pcmcia/xirc2ps_cs.c @@ -289,9 +289,9 @@ static void mii_wr(kio_addr_t ioaddr, u_char phyaddr, u_char phyreg, * and ejection events. They are invoked from the event handler. */ -static int has_ce2_string(dev_link_t * link); -static void xirc2ps_config(dev_link_t * link); -static void xirc2ps_release(dev_link_t * link); +static int has_ce2_string(struct pcmcia_device * link); +static int xirc2ps_config(struct pcmcia_device * link); +static void xirc2ps_release(struct pcmcia_device * link); /**************** * The attach() and detach() entry points are used to create and destroy @@ -313,10 +313,10 @@ static irqreturn_t xirc2ps_interrupt(int irq, void *dev_id, struct pt_regs *regs /**************** * A linked list of "instances" of the device. Each actual * PCMCIA card corresponds to one device instance, and is described - * by one dev_link_t structure (defined in ds.h). + * by one struct pcmcia_device structure (defined in ds.h). * * You may not want to use a linked list for this -- for example, the - * memory card driver uses an array of dev_link_t pointers, where minor + * memory card driver uses an array of struct pcmcia_device pointers, where minor * device numbers are used to derive the corresponding array index. */ @@ -326,13 +326,13 @@ static irqreturn_t xirc2ps_interrupt(int irq, void *dev_id, struct pt_regs *regs * example, ethernet cards, modems). In other cases, there may be * many actual or logical devices (SCSI adapters, memory cards with * multiple partitions). The dev_node_t structures need to be kept - * in a linked list starting at the 'dev' field of a dev_link_t + * in a linked list starting at the 'dev' field of a struct pcmcia_device * structure. We allocate them in the card's private data structure, * because they generally can't be allocated dynamically. */ typedef struct local_info_t { - dev_link_t link; + struct pcmcia_device *p_dev; dev_node_t node; struct net_device_stats stats; int card_type; @@ -355,7 +355,7 @@ static void do_tx_timeout(struct net_device *dev); static struct net_device_stats *do_get_stats(struct net_device *dev); static void set_addresses(struct net_device *dev); static void set_multicast_list(struct net_device *dev); -static int set_card_type(dev_link_t *link, const void *s); +static int set_card_type(struct pcmcia_device *link, const void *s); static int do_config(struct net_device *dev, struct ifmap *map); static int do_open(struct net_device *dev); static int do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); @@ -368,7 +368,7 @@ static int do_stop(struct net_device *dev); /*=============== Helper functions =========================*/ static int -first_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse) +first_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { int err; @@ -379,7 +379,7 @@ first_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse) } static int -next_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse) +next_tuple(struct pcmcia_device *handle, tuple_t *tuple, cisparse_t *parse) { int err; @@ -553,9 +553,8 @@ mii_wr(kio_addr_t ioaddr, u_char phyaddr, u_char phyreg, unsigned data, int len) */ static int -xirc2ps_attach(struct pcmcia_device *p_dev) +xirc2ps_probe(struct pcmcia_device *link) { - dev_link_t *link; struct net_device *dev; local_info_t *local; @@ -566,12 +565,11 @@ xirc2ps_attach(struct pcmcia_device *p_dev) if (!dev) return -ENOMEM; local = netdev_priv(dev); - link = &local->link; + local->p_dev = link; link->priv = dev; /* General socket configuration */ link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; link->conf.ConfigIndex = 1; link->conf.Present = PRESENT_OPTION; @@ -593,13 +591,7 @@ xirc2ps_attach(struct pcmcia_device *p_dev) dev->watchdog_timeo = TX_TIMEOUT; #endif - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - xirc2ps_config(link); - - return 0; + return xirc2ps_config(link); } /* xirc2ps_attach */ /**************** @@ -610,18 +602,16 @@ xirc2ps_attach(struct pcmcia_device *p_dev) */ static void -xirc2ps_detach(struct pcmcia_device *p_dev) +xirc2ps_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; DEBUG(0, "detach(0x%p)\n", link); - if (link->dev) + if (link->dev_node) unregister_netdev(dev); - if (link->state & DEV_CONFIG) - xirc2ps_release(link); + xirc2ps_release(link); free_netdev(dev); } /* xirc2ps_detach */ @@ -645,7 +635,7 @@ xirc2ps_detach(struct pcmcia_device *p_dev) * */ static int -set_card_type(dev_link_t *link, const void *s) +set_card_type(struct pcmcia_device *link, const void *s) { struct net_device *dev = link->priv; local_info_t *local = netdev_priv(dev); @@ -714,9 +704,8 @@ set_card_type(dev_link_t *link, const void *s) * Returns: true if this is a CE2 */ static int -has_ce2_string(dev_link_t * link) +has_ce2_string(struct pcmcia_device * link) { - client_handle_t handle = link->handle; tuple_t tuple; cisparse_t parse; u_char buf[256]; @@ -726,7 +715,7 @@ has_ce2_string(dev_link_t * link) tuple.TupleDataMax = 254; tuple.TupleOffset = 0; tuple.DesiredTuple = CISTPL_VERS_1; - if (!first_tuple(handle, &tuple, &parse) && parse.version_1.ns > 2) { + if (!first_tuple(link, &tuple, &parse) && parse.version_1.ns > 2) { if (strstr(parse.version_1.str + parse.version_1.ofs[2], "CE2")) return 1; } @@ -738,10 +727,9 @@ has_ce2_string(dev_link_t * link) * is received, to configure the PCMCIA socket, and to make the * ethernet device available to the system. */ -static void -xirc2ps_config(dev_link_t * link) +static int +xirc2ps_config(struct pcmcia_device * link) { - client_handle_t handle = link->handle; struct net_device *dev = link->priv; local_info_t *local = netdev_priv(dev); tuple_t tuple; @@ -767,7 +755,7 @@ xirc2ps_config(dev_link_t * link) /* Is this a valid card */ tuple.DesiredTuple = CISTPL_MANFID; - if ((err=first_tuple(handle, &tuple, &parse))) { + if ((err=first_tuple(link, &tuple, &parse))) { printk(KNOT_XIRC "manfid not found in CIS\n"); goto failure; } @@ -803,15 +791,15 @@ xirc2ps_config(dev_link_t * link) /* get configuration stuff */ tuple.DesiredTuple = CISTPL_CONFIG; - if ((err=first_tuple(handle, &tuple, &parse))) + if ((err=first_tuple(link, &tuple, &parse))) goto cis_error; link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; /* get the ethernet address from the CIS */ tuple.DesiredTuple = CISTPL_FUNCE; - for (err = first_tuple(handle, &tuple, &parse); !err; - err = next_tuple(handle, &tuple, &parse)) { + for (err = first_tuple(link, &tuple, &parse); !err; + err = next_tuple(link, &tuple, &parse)) { /* Once I saw two CISTPL_FUNCE_LAN_NODE_ID entries: * the first one with a length of zero the second correct - * so I skip all entries with length 0 */ @@ -821,8 +809,8 @@ xirc2ps_config(dev_link_t * link) } if (err) { /* not found: try to get the node-id from tuple 0x89 */ tuple.DesiredTuple = 0x89; /* data layout looks like tuple 0x22 */ - if ((err = pcmcia_get_first_tuple(handle, &tuple)) == 0 && - (err = pcmcia_get_tuple_data(handle, &tuple)) == 0) { + if ((err = pcmcia_get_first_tuple(link, &tuple)) == 0 && + (err = pcmcia_get_tuple_data(link, &tuple)) == 0) { if (tuple.TupleDataLen == 8 && *buf == CISTPL_FUNCE_LAN_NODE_ID) memcpy(&parse, buf, 8); else @@ -831,8 +819,8 @@ xirc2ps_config(dev_link_t * link) } if (err) { /* another try (James Lehmer's CE2 version 4.1)*/ tuple.DesiredTuple = CISTPL_FUNCE; - for (err = first_tuple(handle, &tuple, &parse); !err; - err = next_tuple(handle, &tuple, &parse)) { + for (err = first_tuple(link, &tuple, &parse); !err; + err = next_tuple(link, &tuple, &parse)) { if (parse.funce.type == 0x02 && parse.funce.data[0] == 1 && parse.funce.data[1] == 6 && tuple.TupleDataLen == 13) { buf[1] = 4; @@ -853,9 +841,6 @@ xirc2ps_config(dev_link_t * link) for (i=0; i < 6; i++) dev->dev_addr[i] = node_id->id[i]; - /* Configure card */ - link->state |= DEV_CONFIG; - link->io.IOAddrLines =10; link->io.Attributes1 = IO_DATA_PATH_WIDTH_16; link->irq.Attributes = IRQ_HANDLE_PRESENT; @@ -875,14 +860,14 @@ xirc2ps_config(dev_link_t * link) * Ethernet port */ link->io.NumPorts1 = 16; /* no Mako stuff anymore */ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - for (err = first_tuple(handle, &tuple, &parse); !err; - err = next_tuple(handle, &tuple, &parse)) { + for (err = first_tuple(link, &tuple, &parse); !err; + err = next_tuple(link, &tuple, &parse)) { if (cf->io.nwin > 0 && (cf->io.win[0].base & 0xf) == 8) { for (ioaddr = 0x300; ioaddr < 0x400; ioaddr += 0x10) { link->conf.ConfigIndex = cf->index ; link->io.BasePort2 = cf->io.win[0].base; link->io.BasePort1 = ioaddr; - if (!(err=pcmcia_request_io(link->handle, &link->io))) + if (!(err=pcmcia_request_io(link, &link->io))) goto port_found; } } @@ -896,15 +881,15 @@ xirc2ps_config(dev_link_t * link) */ for (pass=0; pass < 2; pass++) { tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - for (err = first_tuple(handle, &tuple, &parse); !err; - err = next_tuple(handle, &tuple, &parse)){ + for (err = first_tuple(link, &tuple, &parse); !err; + err = next_tuple(link, &tuple, &parse)){ if (cf->io.nwin > 0 && (cf->io.win[0].base & 0xf) == 8){ link->conf.ConfigIndex = cf->index ; link->io.BasePort2 = cf->io.win[0].base; link->io.BasePort1 = link->io.BasePort2 + (pass ? (cf->index & 0x20 ? -24:8) : (cf->index & 0x20 ? 8:-24)); - if (!(err=pcmcia_request_io(link->handle, &link->io))) + if (!(err=pcmcia_request_io(link, &link->io))) goto port_found; } } @@ -919,12 +904,12 @@ xirc2ps_config(dev_link_t * link) link->io.NumPorts1 = 16; for (ioaddr = 0x300; ioaddr < 0x400; ioaddr += 0x10) { link->io.BasePort1 = ioaddr; - if (!(err=pcmcia_request_io(link->handle, &link->io))) + if (!(err=pcmcia_request_io(link, &link->io))) goto port_found; } link->io.BasePort1 = 0; /* let CS decide */ - if ((err=pcmcia_request_io(link->handle, &link->io))) { - cs_error(link->handle, RequestIO, err); + if ((err=pcmcia_request_io(link, &link->io))) { + cs_error(link, RequestIO, err); goto config_error; } } @@ -936,8 +921,8 @@ xirc2ps_config(dev_link_t * link) * Now allocate an interrupt line. Note that this does not * actually assign a handler to the interrupt. */ - if ((err=pcmcia_request_irq(link->handle, &link->irq))) { - cs_error(link->handle, RequestIRQ, err); + if ((err=pcmcia_request_irq(link, &link->irq))) { + cs_error(link, RequestIRQ, err); goto config_error; } @@ -945,8 +930,8 @@ xirc2ps_config(dev_link_t * link) * This actually configures the PCMCIA socket -- setting up * the I/O windows and the interrupt mapping. */ - if ((err=pcmcia_request_configuration(link->handle, &link->conf))) { - cs_error(link->handle, RequestConfiguration, err); + if ((err=pcmcia_request_configuration(link, &link->conf))) { + cs_error(link, RequestConfiguration, err); goto config_error; } @@ -963,15 +948,15 @@ xirc2ps_config(dev_link_t * link) reg.Action = CS_WRITE; reg.Offset = CISREG_IOBASE_0; reg.Value = link->io.BasePort2 & 0xff; - if ((err = pcmcia_access_configuration_register(link->handle, ®))) { - cs_error(link->handle, AccessConfigurationRegister, err); + if ((err = pcmcia_access_configuration_register(link, ®))) { + cs_error(link, AccessConfigurationRegister, err); goto config_error; } reg.Action = CS_WRITE; reg.Offset = CISREG_IOBASE_1; reg.Value = (link->io.BasePort2 >> 8) & 0xff; - if ((err = pcmcia_access_configuration_register(link->handle, ®))) { - cs_error(link->handle, AccessConfigurationRegister, err); + if ((err = pcmcia_access_configuration_register(link, ®))) { + cs_error(link, AccessConfigurationRegister, err); goto config_error; } @@ -982,15 +967,15 @@ xirc2ps_config(dev_link_t * link) req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; req.Base = req.Size = 0; req.AccessSpeed = 0; - if ((err = pcmcia_request_window(&link->handle, &req, &link->win))) { - cs_error(link->handle, RequestWindow, err); + if ((err = pcmcia_request_window(&link, &req, &link->win))) { + cs_error(link, RequestWindow, err); goto config_error; } local->dingo_ccr = ioremap(req.Base,0x1000) + 0x0800; mem.CardOffset = 0x0; mem.Page = 0; if ((err = pcmcia_map_mem_page(link->win, &mem))) { - cs_error(link->handle, MapMemPage, err); + cs_error(link, MapMemPage, err); goto config_error; } @@ -1050,13 +1035,12 @@ xirc2ps_config(dev_link_t * link) if (local->dingo) do_reset(dev, 1); /* a kludge to make the cem56 work */ - link->dev = &local->node; - link->state &= ~DEV_CONFIG_PENDING; - SET_NETDEV_DEV(dev, &handle_to_dev(handle)); + link->dev_node = &local->node; + SET_NETDEV_DEV(dev, &handle_to_dev(link)); if ((err=register_netdev(dev))) { printk(KNOT_XIRC "register_netdev() failed\n"); - link->dev = NULL; + link->dev_node = NULL; goto config_error; } @@ -1069,17 +1053,16 @@ xirc2ps_config(dev_link_t * link) printk("%c%02X", i?':':' ', dev->dev_addr[i]); printk("\n"); - return; + return 0; config_error: - link->state &= ~DEV_CONFIG_PENDING; xirc2ps_release(link); - return; + return -ENODEV; cis_error: printk(KNOT_XIRC "unable to parse CIS\n"); failure: - link->state &= ~DEV_CONFIG_PENDING; + return -ENODEV; } /* xirc2ps_config */ /**************** @@ -1088,57 +1071,41 @@ xirc2ps_config(dev_link_t * link) * still open, this will be postponed until it is closed. */ static void -xirc2ps_release(dev_link_t *link) +xirc2ps_release(struct pcmcia_device *link) { + DEBUG(0, "release(0x%p)\n", link); - DEBUG(0, "release(0x%p)\n", link); - - if (link->win) { - struct net_device *dev = link->priv; - local_info_t *local = netdev_priv(dev); - if (local->dingo) - iounmap(local->dingo_ccr - 0x0800); - pcmcia_release_window(link->win); - } - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - link->state &= ~DEV_CONFIG; - + if (link->win) { + struct net_device *dev = link->priv; + local_info_t *local = netdev_priv(dev); + if (local->dingo) + iounmap(local->dingo_ccr - 0x0800); + } + pcmcia_disable_device(link); } /* xirc2ps_release */ /*====================================================================*/ -static int xirc2ps_suspend(struct pcmcia_device *p_dev) +static int xirc2ps_suspend(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - if (link->open) { - netif_device_detach(dev); - do_powerdown(dev); - } - pcmcia_release_configuration(link->handle); + if (link->open) { + netif_device_detach(dev); + do_powerdown(dev); } return 0; } -static int xirc2ps_resume(struct pcmcia_device *p_dev) +static int xirc2ps_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - pcmcia_request_configuration(link->handle, &link->conf); - if (link->open) { - do_reset(dev,1); - netif_device_attach(dev); - } + if (link->open) { + do_reset(dev,1); + netif_device_attach(dev); } return 0; @@ -1552,13 +1519,13 @@ static int do_open(struct net_device *dev) { local_info_t *lp = netdev_priv(dev); - dev_link_t *link = &lp->link; + struct pcmcia_device *link = lp->p_dev; DEBUG(0, "do_open(%p)\n", dev); /* Check that the PCMCIA card is still here. */ /* Physical device present signature. */ - if (!DEV_OK(link)) + if (!pcmcia_dev_present(link)) return -ENODEV; /* okay */ @@ -1882,7 +1849,7 @@ do_stop(struct net_device *dev) { kio_addr_t ioaddr = dev->base_addr; local_info_t *lp = netdev_priv(dev); - dev_link_t *link = &lp->link; + struct pcmcia_device *link = lp->p_dev; DEBUG(0, "do_stop(%p)\n", dev); @@ -1935,7 +1902,7 @@ static struct pcmcia_driver xirc2ps_cs_driver = { .drv = { .name = "xirc2ps_cs", }, - .probe = xirc2ps_attach, + .probe = xirc2ps_probe, .remove = xirc2ps_detach, .id_table = xirc2ps_ids, .suspend = xirc2ps_suspend, @@ -1973,7 +1940,7 @@ static int __init setup_xirc2ps_cs(char *str) MAYBE_SET(lockup_hack, 6); #undef MAYBE_SET - return 0; + return 1; } __setup("xirc2ps_cs=", setup_xirc2ps_cs); diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index 9595f74da93f..07c31f19c6ba 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -1167,8 +1167,8 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) * station address PROM at the base address and programmed into the * "Physical Address Registers" CSR12-14. * As a precautionary measure, we read the PROM values and complain if - * they disagree with the CSRs. Either way, we use the CSR values, and - * double check that they are valid. + * they disagree with the CSRs. If they miscompare, and the PROM addr + * is valid, then the PROM addr is used. */ for (i = 0; i < 3; i++) { unsigned int val; diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c index 1f5975a61e1f..43f5e86fc559 100644 --- a/drivers/net/spider_net.c +++ b/drivers/net/spider_net.c @@ -1442,7 +1442,7 @@ spider_net_handle_error_irq(struct spider_net_card *card, u32 status_reg) case SPIDER_NET_GRFAFLLINT: /* fallthrough */ case SPIDER_NET_GRMFLLINT: if (netif_msg_intr(card) && net_ratelimit()) - pr_err("Spider RX RAM full, incoming packets " + pr_debug("Spider RX RAM full, incoming packets " "might be discarded!\n"); spider_net_rx_irq_off(card); tasklet_schedule(&card->rxram_full_tl); @@ -2086,7 +2086,7 @@ spider_net_setup_netdev(struct spider_net_card *card) spider_net_setup_netdev_ops(netdev); - netdev->features = 0; + netdev->features = NETIF_F_HW_CSUM; /* some time: NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX | * NETIF_F_HW_VLAN_FILTER */ diff --git a/drivers/net/starfire.c b/drivers/net/starfire.c index 35b18057fbdd..45ad036733e2 100644 --- a/drivers/net/starfire.c +++ b/drivers/net/starfire.c @@ -2122,8 +2122,7 @@ static void __devexit starfire_remove_one (struct pci_dev *pdev) struct net_device *dev = pci_get_drvdata(pdev); struct netdev_private *np = netdev_priv(dev); - if (!dev) - BUG(); + BUG_ON(!dev); unregister_netdev(dev); diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 964c09644832..73e271e59c6a 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -69,8 +69,8 @@ #define DRV_MODULE_NAME "tg3" #define PFX DRV_MODULE_NAME ": " -#define DRV_MODULE_VERSION "3.55" -#define DRV_MODULE_RELDATE "Mar 27, 2006" +#define DRV_MODULE_VERSION "3.56" +#define DRV_MODULE_RELDATE "Apr 1, 2006" #define TG3_DEF_MAC_MODE 0 #define TG3_DEF_RX_MODE 0 @@ -497,18 +497,18 @@ static void tg3_write_mem(struct tg3 *tp, u32 off, u32 val) unsigned long flags; spin_lock_irqsave(&tp->indirect_lock, flags); - if (tp->write32 != tg3_write_indirect_reg32) { - tw32_f(TG3PCI_MEM_WIN_BASE_ADDR, off); - tw32_f(TG3PCI_MEM_WIN_DATA, val); - - /* Always leave this as zero. */ - tw32_f(TG3PCI_MEM_WIN_BASE_ADDR, 0); - } else { + if (tp->tg3_flags & TG3_FLAG_SRAM_USE_CONFIG) { pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off); pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_DATA, val); /* Always leave this as zero. */ pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0); + } else { + tw32_f(TG3PCI_MEM_WIN_BASE_ADDR, off); + tw32_f(TG3PCI_MEM_WIN_DATA, val); + + /* Always leave this as zero. */ + tw32_f(TG3PCI_MEM_WIN_BASE_ADDR, 0); } spin_unlock_irqrestore(&tp->indirect_lock, flags); } @@ -518,18 +518,18 @@ static void tg3_read_mem(struct tg3 *tp, u32 off, u32 *val) unsigned long flags; spin_lock_irqsave(&tp->indirect_lock, flags); - if (tp->write32 != tg3_write_indirect_reg32) { - tw32_f(TG3PCI_MEM_WIN_BASE_ADDR, off); - *val = tr32(TG3PCI_MEM_WIN_DATA); - - /* Always leave this as zero. */ - tw32_f(TG3PCI_MEM_WIN_BASE_ADDR, 0); - } else { + if (tp->tg3_flags & TG3_FLAG_SRAM_USE_CONFIG) { pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off); pci_read_config_dword(tp->pdev, TG3PCI_MEM_WIN_DATA, val); /* Always leave this as zero. */ pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0); + } else { + tw32_f(TG3PCI_MEM_WIN_BASE_ADDR, off); + *val = tr32(TG3PCI_MEM_WIN_DATA); + + /* Always leave this as zero. */ + tw32_f(TG3PCI_MEM_WIN_BASE_ADDR, 0); } spin_unlock_irqrestore(&tp->indirect_lock, flags); } @@ -2966,9 +2966,7 @@ static void tg3_tx(struct tg3 *tp) struct sk_buff *skb = ri->skb; int i; - if (unlikely(skb == NULL)) - BUG(); - + BUG_ON(skb == NULL); pci_unmap_single(tp->pdev, pci_unmap_addr(ri, mapping), skb_headlen(skb), @@ -2979,12 +2977,10 @@ static void tg3_tx(struct tg3 *tp) sw_idx = NEXT_TX(sw_idx); for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { - if (unlikely(sw_idx == hw_idx)) - BUG(); + BUG_ON(sw_idx == hw_idx); ri = &tp->tx_buffers[sw_idx]; - if (unlikely(ri->skb != NULL)) - BUG(); + BUG_ON(ri->skb != NULL); pci_unmap_page(tp->pdev, pci_unmap_addr(ri, mapping), @@ -4935,9 +4931,8 @@ static int tg3_halt_cpu(struct tg3 *tp, u32 offset) { int i; - if (offset == TX_CPU_BASE && - (tp->tg3_flags2 & TG3_FLG2_5705_PLUS)) - BUG(); + BUG_ON(offset == TX_CPU_BASE && + (tp->tg3_flags2 & TG3_FLG2_5705_PLUS)); if (offset == RX_CPU_BASE) { for (i = 0; i < 10000; i++) { @@ -5840,10 +5835,14 @@ static int tg3_reset_hw(struct tg3 *tp) GRC_MODE_NO_TX_PHDR_CSUM | GRC_MODE_NO_RX_PHDR_CSUM); tp->grc_mode |= GRC_MODE_HOST_SENDBDS; - if (tp->tg3_flags & TG3_FLAG_NO_TX_PSEUDO_CSUM) - tp->grc_mode |= GRC_MODE_NO_TX_PHDR_CSUM; - if (tp->tg3_flags & TG3_FLAG_NO_RX_PSEUDO_CSUM) - tp->grc_mode |= GRC_MODE_NO_RX_PHDR_CSUM; + + /* Pseudo-header checksum is done by hardware logic and not + * the offload processers, so make the chip do the pseudo- + * header checksums on receive. For transmit it is more + * convenient to do the pseudo-header checksum in software + * as Linux does that on transmit for us in all cases. + */ + tp->grc_mode |= GRC_MODE_NO_TX_PHDR_CSUM; tw32(GRC_MODE, tp->grc_mode | @@ -8046,9 +8045,13 @@ static int tg3_test_nvram(struct tg3 *tp) for (i = 0; i < size; i++) csum8 += buf8[i]; - if (csum8 == 0) - return 0; - return -EIO; + if (csum8 == 0) { + err = 0; + goto out; + } + + err = -EIO; + goto out; } /* Bootstrap checksum at offset 0x10 */ @@ -9543,8 +9546,11 @@ static void __devinit tg3_get_eeprom_hw_cfg(struct tg3 *tp) tp->led_ctrl = LED_CTRL_MODE_PHY_1; /* Do not even try poking around in here on Sun parts. */ - if (tp->tg3_flags2 & TG3_FLG2_SUN_570X) + if (tp->tg3_flags2 & TG3_FLG2_SUN_570X) { + /* All SUN chips are built-in LOMs. */ + tp->tg3_flags |= TG3_FLAG_EEPROM_WRITE_PROT; return; + } tg3_read_mem(tp, NIC_SRAM_DATA_SIG, &val); if (val == NIC_SRAM_DATA_SIG_MAGIC) { @@ -9642,9 +9648,7 @@ static void __devinit tg3_get_eeprom_hw_cfg(struct tg3 *tp) 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_5700) && - (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5701) && - (nic_cfg & NIC_SRAM_DATA_CFG_EEPROM_WP)) + if (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) { @@ -10269,6 +10273,13 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) pci_write_config_word(tp->pdev, PCI_COMMAND, pci_cmd); } + if (tp->write32 == tg3_write_indirect_reg32 || + ((tp->tg3_flags & TG3_FLAG_PCIX_MODE) && + (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 || + GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5701)) || + (tp->tg3_flags2 & TG3_FLG2_SUN_570X)) + tp->tg3_flags |= TG3_FLAG_SRAM_USE_CONFIG; + /* Get eeprom hw config before calling tg3_set_power_state(). * In particular, the TG3_FLAG_EEPROM_WRITE_PROT flag must be * determined before calling tg3_set_power_state() so that @@ -10311,15 +10322,6 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) if (tp->pci_chip_rev_id == CHIPREV_ID_5700_B0) tp->tg3_flags |= TG3_FLAG_BROKEN_CHECKSUMS; - /* Pseudo-header checksum is done by hardware logic and not - * the offload processers, so make the chip do the pseudo- - * header checksums on receive. For transmit it is more - * convenient to do the pseudo-header checksum in software - * as Linux does that on transmit for us in all cases. - */ - tp->tg3_flags |= TG3_FLAG_NO_TX_PSEUDO_CSUM; - tp->tg3_flags &= ~TG3_FLAG_NO_RX_PSEUDO_CSUM; - /* Derive initial jumbo mode from MTU assigned in * ether_setup() via the alloc_etherdev() call */ diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h index c43cc3264202..8c8b987d1250 100644 --- a/drivers/net/tg3.h +++ b/drivers/net/tg3.h @@ -2171,8 +2171,7 @@ struct tg3 { #define TG3_FLAG_PCIX_MODE 0x00020000 #define TG3_FLAG_PCI_HIGH_SPEED 0x00040000 #define TG3_FLAG_PCI_32BIT 0x00080000 -#define TG3_FLAG_NO_TX_PSEUDO_CSUM 0x00100000 -#define TG3_FLAG_NO_RX_PSEUDO_CSUM 0x00200000 +#define TG3_FLAG_SRAM_USE_CONFIG 0x00100000 #define TG3_FLAG_SERDES_WOL_CAP 0x00400000 #define TG3_FLAG_JUMBO_RING_ENABLE 0x00800000 #define TG3_FLAG_10_100_ONLY 0x01000000 diff --git a/drivers/net/tokenring/Kconfig b/drivers/net/tokenring/Kconfig index e4cfc80b283b..99c4c1922f19 100644 --- a/drivers/net/tokenring/Kconfig +++ b/drivers/net/tokenring/Kconfig @@ -3,7 +3,7 @@ # menu "Token Ring devices" - depends on NETDEVICES + depends on NETDEVICES && !UML # So far, we only have PCI, ISA, and MCA token ring devices config TR diff --git a/drivers/net/tokenring/abyss.c b/drivers/net/tokenring/abyss.c index 9345e68c451e..649d8ea354f5 100644 --- a/drivers/net/tokenring/abyss.c +++ b/drivers/net/tokenring/abyss.c @@ -438,8 +438,7 @@ static void __devexit abyss_detach (struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); - if (!dev) - BUG(); + BUG_ON(!dev); unregister_netdev(dev); release_region(dev->base_addr-0x10, ABYSS_IO_EXTENT); free_irq(dev->irq, dev); diff --git a/drivers/net/tokenring/madgemc.c b/drivers/net/tokenring/madgemc.c index 3a25d191ea4a..19e6f4dfd69c 100644 --- a/drivers/net/tokenring/madgemc.c +++ b/drivers/net/tokenring/madgemc.c @@ -735,8 +735,7 @@ static int __devexit madgemc_remove(struct device *device) struct net_local *tp; struct card_info *card; - if (!dev) - BUG(); + BUG_ON(!dev); tp = dev->priv; card = tp->tmspriv; diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c index 241871589283..a9b2150909d6 100644 --- a/drivers/net/via-rhine.c +++ b/drivers/net/via-rhine.c @@ -1085,6 +1085,25 @@ static void rhine_check_media(struct net_device *dev, unsigned int init_media) else iowrite8(ioread8(ioaddr + ChipCmd1) & ~Cmd1FDuplex, ioaddr + ChipCmd1); + if (debug > 1) + printk(KERN_INFO "%s: force_media %d, carrier %d\n", dev->name, + rp->mii_if.force_media, netif_carrier_ok(dev)); +} + +/* Called after status of force_media possibly changed */ +void rhine_set_carrier(struct mii_if_info *mii) +{ + if (mii->force_media) { + /* autoneg is off: Link is always assumed to be up */ + if (!netif_carrier_ok(mii->dev)) + netif_carrier_on(mii->dev); + } + else /* Let MMI library update carrier status */ + rhine_check_media(mii->dev, 0); + if (debug > 1) + printk(KERN_INFO "%s: force_media %d, carrier %d\n", + mii->dev->name, mii->force_media, + netif_carrier_ok(mii->dev)); } static void rhine_check_media_task(struct net_device *dev) @@ -1782,6 +1801,7 @@ static int netdev_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) spin_lock_irq(&rp->lock); rc = mii_ethtool_sset(&rp->mii_if, cmd); spin_unlock_irq(&rp->lock); + rhine_set_carrier(&rp->mii_if); return rc; } @@ -1869,6 +1889,7 @@ static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) spin_lock_irq(&rp->lock); rc = generic_mii_ioctl(&rp->mii_if, if_mii(rq), cmd, NULL); spin_unlock_irq(&rp->lock); + rhine_set_carrier(&rp->mii_if); return rc; } diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index fd17aa8491b6..bad09ebdb50b 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -309,7 +309,10 @@ config APPLE_AIRPORT Say Y here to support the Airport 802.11b wireless Ethernet hardware built into the Macintosh iBook and other recent PowerPC-based Macintosh machines. This is essentially a Lucent Orinoco card with - a non-standard interface + a non-standard interface. + + This driver does not support the Airport Extreme (802.11b/g). Use + the BCM43xx driver for Airport Extreme cards. config PLX_HERMES tristate "Hermes in PLX9052 based PCI adaptor support (Netgear MA301 etc.)" @@ -353,7 +356,7 @@ config PCI_HERMES config ATMEL tristate "Atmel at76c50x chipset 802.11b support" - depends on NET_RADIO + depends on NET_RADIO && (PCI || PCMCIA) select FW_LOADER select CRC32 ---help--- @@ -401,6 +404,7 @@ config PCMCIA_HERMES config PCMCIA_SPECTRUM tristate "Symbol Spectrum24 Trilogy PCMCIA card support" depends on NET_RADIO && PCMCIA && HERMES + select FW_LOADER ---help--- This is a driver for 802.11b cards using RAM-loadable Symbol @@ -500,6 +504,7 @@ config PRISM54 will be called prism54.ko. source "drivers/net/wireless/hostap/Kconfig" +source "drivers/net/wireless/bcm43xx/Kconfig" # yes, this works even when no drivers are selected config NET_WIRELESS diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 3a6f7ba326ca..c86779879361 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_PCMCIA_ATMEL) += atmel_cs.o obj-$(CONFIG_PRISM54) += prism54/ obj-$(CONFIG_HOSTAP) += hostap/ +obj-$(CONFIG_BCM43XX) += bcm43xx/ # 16-bit wireless PCMCIA client drivers obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o diff --git a/drivers/net/wireless/airo_cs.c b/drivers/net/wireless/airo_cs.c index a496460ce224..af0cbb6c5c0c 100644 --- a/drivers/net/wireless/airo_cs.c +++ b/drivers/net/wireless/airo_cs.c @@ -80,8 +80,8 @@ MODULE_SUPPORTED_DEVICE("Aironet 4500, 4800 and Cisco 340 PCMCIA cards"); event handler. */ -static void airo_config(dev_link_t *link); -static void airo_release(dev_link_t *link); +static int airo_config(struct pcmcia_device *link); +static void airo_release(struct pcmcia_device *link); /* The attach() and detach() entry points are used to create and destroy @@ -101,10 +101,10 @@ static void airo_detach(struct pcmcia_device *p_dev); /* A linked list of "instances" of the aironet device. Each actual PCMCIA card corresponds to one device instance, and is described - by one dev_link_t structure (defined in ds.h). + by one struct pcmcia_device structure (defined in ds.h). You may not want to use a linked list for this -- for example, the - memory card driver uses an array of dev_link_t pointers, where minor + memory card driver uses an array of struct pcmcia_device pointers, where minor device numbers are used to derive the corresponding array index. */ @@ -114,7 +114,7 @@ static void airo_detach(struct pcmcia_device *p_dev); example, ethernet cards, modems). In other cases, there may be many actual or logical devices (SCSI adapters, memory cards with multiple partitions). The dev_node_t structures need to be kept - in a linked list starting at the 'dev' field of a dev_link_t + in a linked list starting at the 'dev' field of a struct pcmcia_device structure. We allocate them in the card's private data structure, because they generally shouldn't be allocated dynamically. @@ -141,24 +141,16 @@ typedef struct local_info_t { ======================================================================*/ -static int airo_attach(struct pcmcia_device *p_dev) +static int airo_probe(struct pcmcia_device *p_dev) { - dev_link_t *link; local_info_t *local; DEBUG(0, "airo_attach()\n"); - /* Initialize the dev_link_t structure */ - link = kzalloc(sizeof(struct dev_link_t), GFP_KERNEL); - if (!link) { - printk(KERN_ERR "airo_cs: no memory for new device\n"); - return -ENOMEM; - } - /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; - link->irq.IRQInfo1 = IRQ_LEVEL_ID; - link->irq.Handler = NULL; + p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID; + p_dev->irq.Handler = NULL; /* General socket configuration defaults can go here. In this @@ -167,26 +159,18 @@ static int airo_attach(struct pcmcia_device *p_dev) and attributes of IO windows) are fixed by the nature of the device, and can be hard-wired here. */ - link->conf.Attributes = 0; - link->conf.Vcc = 50; - link->conf.IntType = INT_MEMORY_AND_IO; + p_dev->conf.Attributes = 0; + p_dev->conf.IntType = INT_MEMORY_AND_IO; /* Allocate space for private device-specific data */ local = kzalloc(sizeof(local_info_t), GFP_KERNEL); if (!local) { printk(KERN_ERR "airo_cs: no memory for new device\n"); - kfree (link); return -ENOMEM; } - link->priv = local; + p_dev->priv = local; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - airo_config(link); - - return 0; + return airo_config(p_dev); } /* airo_attach */ /*====================================================================== @@ -198,14 +182,11 @@ static int airo_attach(struct pcmcia_device *p_dev) ======================================================================*/ -static void airo_detach(struct pcmcia_device *p_dev) +static void airo_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); - DEBUG(0, "airo_detach(0x%p)\n", link); - if (link->state & DEV_CONFIG) - airo_release(link); + airo_release(link); if ( ((local_info_t*)link->priv)->eth_dev ) { stop_airo_card( ((local_info_t*)link->priv)->eth_dev, 0 ); @@ -213,7 +194,6 @@ static void airo_detach(struct pcmcia_device *p_dev) ((local_info_t*)link->priv)->eth_dev = NULL; kfree(link->priv); - kfree(link); } /* airo_detach */ /*====================================================================== @@ -227,9 +207,8 @@ static void airo_detach(struct pcmcia_device *p_dev) #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) -static void airo_config(dev_link_t *link) +static int airo_config(struct pcmcia_device *link) { - client_handle_t handle; tuple_t tuple; cisparse_t parse; local_info_t *dev; @@ -237,8 +216,7 @@ static void airo_config(dev_link_t *link) u_char buf[64]; win_req_t req; memreq_t map; - - handle = link->handle; + dev = link->priv; DEBUG(0, "airo_config(0x%p)\n", link); @@ -252,15 +230,12 @@ static void airo_config(dev_link_t *link) tuple.TupleData = buf; tuple.TupleDataMax = sizeof(buf); tuple.TupleOffset = 0; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; - - /* Configure card */ - link->state |= DEV_CONFIG; - + /* In this loop, we scan the CIS for configuration table entries, each of which describes a valid card configuration, including @@ -274,12 +249,12 @@ static void airo_config(dev_link_t *link) will only use the CIS to fill in implementation-defined details. */ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); while (1) { cistpl_cftable_entry_t dflt = { 0 }; cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); - if (pcmcia_get_tuple_data(handle, &tuple) != 0 || - pcmcia_parse_tuple(handle, &tuple, &parse) != 0) + if (pcmcia_get_tuple_data(link, &tuple) != 0 || + pcmcia_parse_tuple(link, &tuple, &parse) != 0) goto next_entry; if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg; @@ -294,16 +269,11 @@ static void airo_config(dev_link_t *link) /* Use power settings for Vcc and Vpp if present */ /* Note that the CIS values need to be rescaled */ - if (cfg->vcc.present & (1<conf.Vcc = cfg->vcc.param[CISTPL_POWER_VNOM]/10000; - else if (dflt.vcc.present & (1<conf.Vcc = dflt.vcc.param[CISTPL_POWER_VNOM]/10000; - if (cfg->vpp1.present & (1<conf.Vpp1 = link->conf.Vpp2 = + link->conf.Vpp = cfg->vpp1.param[CISTPL_POWER_VNOM]/10000; else if (dflt.vpp1.present & (1<conf.Vpp1 = link->conf.Vpp2 = + link->conf.Vpp = dflt.vpp1.param[CISTPL_POWER_VNOM]/10000; /* Do we need to allocate an interrupt? */ @@ -329,12 +299,12 @@ static void airo_config(dev_link_t *link) } /* This reserves IO space but doesn't actually enable it */ - if (pcmcia_request_io(link->handle, &link->io) != 0) + if (pcmcia_request_io(link, &link->io) != 0) goto next_entry; /* Now set up a common memory window, if needed. There is room - in the dev_link_t structure for one memory window handle, + in the struct pcmcia_device structure for one memory window handle, but if the base addresses need to be saved, or if multiple windows are needed, the info should go in the private data structure for this device. @@ -350,7 +320,7 @@ static void airo_config(dev_link_t *link) req.Base = mem->win[0].host_addr; req.Size = mem->win[0].len; req.AccessSpeed = 0; - if (pcmcia_request_window(&link->handle, &req, &link->win) != 0) + if (pcmcia_request_window(&link, &req, &link->win) != 0) goto next_entry; map.Page = 0; map.CardOffset = mem->win[0].card_addr; if (pcmcia_map_mem_page(link->win, &map) != 0) @@ -360,7 +330,7 @@ static void airo_config(dev_link_t *link) break; next_entry: - CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple)); + CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple)); } /* @@ -369,33 +339,32 @@ static void airo_config(dev_link_t *link) irq structure is initialized. */ if (link->conf.Attributes & CONF_ENABLE_IRQ) - CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); /* This actually configures the PCMCIA socket -- setting up the I/O windows and the interrupt mapping, and putting the card and host interface into "Memory and IO" mode. */ - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); ((local_info_t*)link->priv)->eth_dev = init_airo_card( link->irq.AssignedIRQ, - link->io.BasePort1, 1, &handle_to_dev(handle) ); + link->io.BasePort1, 1, &handle_to_dev(link) ); if (!((local_info_t*)link->priv)->eth_dev) goto cs_failed; /* At this point, the dev_node_t structure(s) need to be - initialized and arranged in a linked list at link->dev. + initialized and arranged in a linked list at link->dev_node. */ strcpy(dev->node.dev_name, ((local_info_t*)link->priv)->eth_dev->name ); dev->node.major = dev->node.minor = 0; - link->dev = &dev->node; + link->dev_node = &dev->node; /* Finally, report what we've done */ - printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d", - dev->node.dev_name, link->conf.ConfigIndex, - link->conf.Vcc/10, link->conf.Vcc%10); - if (link->conf.Vpp1) - printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10); + printk(KERN_INFO "%s: index 0x%02x: ", + dev->node.dev_name, link->conf.ConfigIndex); + if (link->conf.Vpp) + printk(", Vpp %d.%d", link->conf.Vpp/10, link->conf.Vpp%10); if (link->conf.Attributes & CONF_ENABLE_IRQ) printk(", irq %d", link->irq.AssignedIRQ); if (link->io.NumPorts1) @@ -408,14 +377,12 @@ static void airo_config(dev_link_t *link) printk(", mem 0x%06lx-0x%06lx", req.Base, req.Base+req.Size-1); printk("\n"); - - link->state &= ~DEV_CONFIG_PENDING; - return; - + return 0; + cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); airo_release(link); - + return -ENODEV; } /* airo_config */ /*====================================================================== @@ -426,51 +393,26 @@ static void airo_config(dev_link_t *link) ======================================================================*/ -static void airo_release(dev_link_t *link) +static void airo_release(struct pcmcia_device *link) { DEBUG(0, "airo_release(0x%p)\n", link); - - /* Unlink the device chain */ - link->dev = NULL; - - /* - In a normal driver, additional code may be needed to release - other kernel data structures associated with this device. - */ - - /* Don't bother checking to see if these succeed or not */ - if (link->win) - pcmcia_release_window(link->win); - pcmcia_release_configuration(link->handle); - if (link->io.NumPorts1) - pcmcia_release_io(link->handle, &link->io); - if (link->irq.AssignedIRQ) - pcmcia_release_irq(link->handle, &link->irq); - link->state &= ~DEV_CONFIG; + pcmcia_disable_device(link); } -static int airo_suspend(struct pcmcia_device *p_dev) +static int airo_suspend(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); local_info_t *local = link->priv; - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - netif_device_detach(local->eth_dev); - pcmcia_release_configuration(link->handle); - } + netif_device_detach(local->eth_dev); return 0; } -static int airo_resume(struct pcmcia_device *p_dev) +static int airo_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); local_info_t *local = link->priv; - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - pcmcia_request_configuration(link->handle, &link->conf); + if (link->open) { reset_airo_card(local->eth_dev); netif_device_attach(local->eth_dev); } @@ -492,7 +434,7 @@ static struct pcmcia_driver airo_driver = { .drv = { .name = "airo_cs", }, - .probe = airo_attach, + .probe = airo_probe, .remove = airo_detach, .id_table = airo_ids, .suspend = airo_suspend, diff --git a/drivers/net/wireless/atmel_cs.c b/drivers/net/wireless/atmel_cs.c index d6f4a5a3e55a..26bf1127524d 100644 --- a/drivers/net/wireless/atmel_cs.c +++ b/drivers/net/wireless/atmel_cs.c @@ -91,8 +91,8 @@ MODULE_SUPPORTED_DEVICE("Atmel at76c50x PCMCIA cards"); event handler. */ -static void atmel_config(dev_link_t *link); -static void atmel_release(dev_link_t *link); +static int atmel_config(struct pcmcia_device *link); +static void atmel_release(struct pcmcia_device *link); /* The attach() and detach() entry points are used to create and destroy @@ -112,10 +112,10 @@ static void atmel_detach(struct pcmcia_device *p_dev); /* A linked list of "instances" of the atmelnet device. Each actual PCMCIA card corresponds to one device instance, and is described - by one dev_link_t structure (defined in ds.h). + by one struct pcmcia_device structure (defined in ds.h). You may not want to use a linked list for this -- for example, the - memory card driver uses an array of dev_link_t pointers, where minor + memory card driver uses an array of struct pcmcia_device pointers, where minor device numbers are used to derive the corresponding array index. */ @@ -125,7 +125,7 @@ static void atmel_detach(struct pcmcia_device *p_dev); example, ethernet cards, modems). In other cases, there may be many actual or logical devices (SCSI adapters, memory cards with multiple partitions). The dev_node_t structures need to be kept - in a linked list starting at the 'dev' field of a dev_link_t + in a linked list starting at the 'dev' field of a struct pcmcia_device structure. We allocate them in the card's private data structure, because they generally shouldn't be allocated dynamically. @@ -152,24 +152,16 @@ typedef struct local_info_t { ======================================================================*/ -static int atmel_attach(struct pcmcia_device *p_dev) +static int atmel_probe(struct pcmcia_device *p_dev) { - dev_link_t *link; local_info_t *local; DEBUG(0, "atmel_attach()\n"); - /* Initialize the dev_link_t structure */ - link = kzalloc(sizeof(struct dev_link_t), GFP_KERNEL); - if (!link) { - printk(KERN_ERR "atmel_cs: no memory for new device\n"); - return -ENOMEM; - } - /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; - link->irq.IRQInfo1 = IRQ_LEVEL_ID; - link->irq.Handler = NULL; + p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID; + p_dev->irq.Handler = NULL; /* General socket configuration defaults can go here. In this @@ -178,26 +170,18 @@ static int atmel_attach(struct pcmcia_device *p_dev) and attributes of IO windows) are fixed by the nature of the device, and can be hard-wired here. */ - link->conf.Attributes = 0; - link->conf.Vcc = 50; - link->conf.IntType = INT_MEMORY_AND_IO; + p_dev->conf.Attributes = 0; + p_dev->conf.IntType = INT_MEMORY_AND_IO; /* Allocate space for private device-specific data */ local = kzalloc(sizeof(local_info_t), GFP_KERNEL); if (!local) { printk(KERN_ERR "atmel_cs: no memory for new device\n"); - kfree (link); return -ENOMEM; } - link->priv = local; + p_dev->priv = local; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - atmel_config(link); - - return 0; + return atmel_config(p_dev); } /* atmel_attach */ /*====================================================================== @@ -209,17 +193,13 @@ static int atmel_attach(struct pcmcia_device *p_dev) ======================================================================*/ -static void atmel_detach(struct pcmcia_device *p_dev) +static void atmel_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); - DEBUG(0, "atmel_detach(0x%p)\n", link); - if (link->state & DEV_CONFIG) - atmel_release(link); + atmel_release(link); kfree(link->priv); - kfree(link); } /*====================================================================== @@ -236,19 +216,17 @@ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) /* Call-back function to interrogate PCMCIA-specific information about the current existance of the card */ static int card_present(void *arg) -{ - dev_link_t *link = (dev_link_t *)arg; - if (link->state & DEV_SUSPEND) - return 0; - else if (link->state & DEV_PRESENT) +{ + struct pcmcia_device *link = (struct pcmcia_device *)arg; + + if (pcmcia_dev_present(link)) return 1; - + return 0; } -static void atmel_config(dev_link_t *link) +static int atmel_config(struct pcmcia_device *link) { - client_handle_t handle; tuple_t tuple; cisparse_t parse; local_info_t *dev; @@ -256,9 +234,8 @@ static void atmel_config(dev_link_t *link) u_char buf[64]; struct pcmcia_device_id *did; - handle = link->handle; dev = link->priv; - did = handle_to_dev(handle).driver_data; + did = handle_to_dev(link).driver_data; DEBUG(0, "atmel_config(0x%p)\n", link); @@ -272,15 +249,12 @@ static void atmel_config(dev_link_t *link) registers. */ tuple.DesiredTuple = CISTPL_CONFIG; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; - - /* Configure card */ - link->state |= DEV_CONFIG; - + /* In this loop, we scan the CIS for configuration table entries, each of which describes a valid card configuration, including @@ -294,12 +268,12 @@ static void atmel_config(dev_link_t *link) will only use the CIS to fill in implementation-defined details. */ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); while (1) { cistpl_cftable_entry_t dflt = { 0 }; cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); - if (pcmcia_get_tuple_data(handle, &tuple) != 0 || - pcmcia_parse_tuple(handle, &tuple, &parse) != 0) + if (pcmcia_get_tuple_data(link, &tuple) != 0 || + pcmcia_parse_tuple(link, &tuple, &parse) != 0) goto next_entry; if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg; @@ -314,16 +288,11 @@ static void atmel_config(dev_link_t *link) /* Use power settings for Vcc and Vpp if present */ /* Note that the CIS values need to be rescaled */ - if (cfg->vcc.present & (1<conf.Vcc = cfg->vcc.param[CISTPL_POWER_VNOM]/10000; - else if (dflt.vcc.present & (1<conf.Vcc = dflt.vcc.param[CISTPL_POWER_VNOM]/10000; - if (cfg->vpp1.present & (1<conf.Vpp1 = link->conf.Vpp2 = + link->conf.Vpp = cfg->vpp1.param[CISTPL_POWER_VNOM]/10000; else if (dflt.vpp1.present & (1<conf.Vpp1 = link->conf.Vpp2 = + link->conf.Vpp = dflt.vpp1.param[CISTPL_POWER_VNOM]/10000; /* Do we need to allocate an interrupt? */ @@ -349,14 +318,14 @@ static void atmel_config(dev_link_t *link) } /* This reserves IO space but doesn't actually enable it */ - if (pcmcia_request_io(link->handle, &link->io) != 0) + if (pcmcia_request_io(link, &link->io) != 0) goto next_entry; /* If we got this far, we're cool! */ break; next_entry: - CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple)); + CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple)); } /* @@ -365,14 +334,14 @@ static void atmel_config(dev_link_t *link) irq structure is initialized. */ if (link->conf.Attributes & CONF_ENABLE_IRQ) - CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); /* This actually configures the PCMCIA socket -- setting up the I/O windows and the interrupt mapping, and putting the card and host interface into "Memory and IO" mode. */ - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); if (link->irq.AssignedIRQ == 0) { printk(KERN_ALERT @@ -384,7 +353,7 @@ static void atmel_config(dev_link_t *link) init_atmel_card(link->irq.AssignedIRQ, link->io.BasePort1, did ? did->driver_info : ATMEL_FW_TYPE_NONE, - &handle_to_dev(handle), + &handle_to_dev(link), card_present, link); if (!((local_info_t*)link->priv)->eth_dev) @@ -393,18 +362,18 @@ static void atmel_config(dev_link_t *link) /* At this point, the dev_node_t structure(s) need to be - initialized and arranged in a linked list at link->dev. + initialized and arranged in a linked list at link->dev_node. */ strcpy(dev->node.dev_name, ((local_info_t*)link->priv)->eth_dev->name ); dev->node.major = dev->node.minor = 0; - link->dev = &dev->node; - - link->state &= ~DEV_CONFIG_PENDING; - return; - + link->dev_node = &dev->node; + + return 0; + cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); atmel_release(link); + return -ENODEV; } /*====================================================================== @@ -415,53 +384,34 @@ static void atmel_config(dev_link_t *link) ======================================================================*/ -static void atmel_release(dev_link_t *link) +static void atmel_release(struct pcmcia_device *link) { struct net_device *dev = ((local_info_t*)link->priv)->eth_dev; - + DEBUG(0, "atmel_release(0x%p)\n", link); - - /* Unlink the device chain */ - link->dev = NULL; - - if (dev) + + if (dev) stop_atmel_card(dev); - ((local_info_t*)link->priv)->eth_dev = NULL; - - /* Don't bother checking to see if these succeed or not */ - pcmcia_release_configuration(link->handle); - if (link->io.NumPorts1) - pcmcia_release_io(link->handle, &link->io); - if (link->irq.AssignedIRQ) - pcmcia_release_irq(link->handle, &link->irq); - link->state &= ~DEV_CONFIG; + ((local_info_t*)link->priv)->eth_dev = NULL; + + pcmcia_disable_device(link); } -static int atmel_suspend(struct pcmcia_device *dev) +static int atmel_suspend(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(dev); local_info_t *local = link->priv; - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - netif_device_detach(local->eth_dev); - pcmcia_release_configuration(link->handle); - } + netif_device_detach(local->eth_dev); return 0; } -static int atmel_resume(struct pcmcia_device *dev) +static int atmel_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(dev); local_info_t *local = link->priv; - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - pcmcia_request_configuration(link->handle, &link->conf); - atmel_open(local->eth_dev); - netif_device_attach(local->eth_dev); - } + atmel_open(local->eth_dev); + netif_device_attach(local->eth_dev); return 0; } @@ -515,7 +465,7 @@ static struct pcmcia_driver atmel_driver = { .drv = { .name = "atmel_cs", }, - .probe = atmel_attach, + .probe = atmel_probe, .remove = atmel_detach, .id_table = atmel_ids, .suspend = atmel_suspend, diff --git a/drivers/net/wireless/bcm43xx/Kconfig b/drivers/net/wireless/bcm43xx/Kconfig new file mode 100644 index 000000000000..418465600a77 --- /dev/null +++ b/drivers/net/wireless/bcm43xx/Kconfig @@ -0,0 +1,62 @@ +config BCM43XX + tristate "Broadcom BCM43xx wireless support" + depends on PCI && IEEE80211 && IEEE80211_SOFTMAC && NET_RADIO && EXPERIMENTAL + select FW_LOADER + ---help--- + This is an experimental driver for the Broadcom 43xx wireless chip, + found in the Apple Airport Extreme and various other devices. + +config BCM43XX_DEBUG + bool "Broadcom BCM43xx debugging (RECOMMENDED)" + depends on BCM43XX + default y + ---help--- + Broadcom 43xx debugging messages. + Say Y, because the driver is still very experimental and + this will help you get it running. + +config BCM43XX_DMA + bool +config BCM43XX_PIO + bool + +choice + prompt "BCM43xx data transfer mode" + depends on BCM43XX + default BCM43XX_DMA_AND_PIO_MODE + +config BCM43XX_DMA_AND_PIO_MODE + bool "DMA + PIO" + select BCM43XX_DMA + select BCM43XX_PIO + ---help--- + Include both, Direct Memory Access (DMA) and Programmed I/O (PIO) + data transfer modes. + The actually used mode is selectable through the module + parameter "pio". If the module parameter is pio=0, DMA is used. + Otherwise PIO is used. DMA is default. + + If unsure, choose this option. + +config BCM43XX_DMA_MODE + bool "DMA (Direct Memory Access) only" + select BCM43XX_DMA + ---help--- + Only include Direct Memory Access (DMA). + This reduces the size of the driver module, by omitting the PIO code. + +config BCM43XX_PIO_MODE + bool "PIO (Programmed I/O) only" + select BCM43XX_PIO + ---help--- + Only include Programmed I/O (PIO). + This reduces the size of the driver module, by omitting the DMA code. + Please note that PIO transfers are slow (compared to DMA). + + Also note that not all devices of the 43xx series support PIO. + The 4306 (Apple Airport Extreme and others) supports PIO, while + the 4318 is known to _not_ support PIO. + + Only use PIO, if DMA does not work for you. + +endchoice diff --git a/drivers/net/wireless/bcm43xx/Makefile b/drivers/net/wireless/bcm43xx/Makefile new file mode 100644 index 000000000000..bb5220c629d2 --- /dev/null +++ b/drivers/net/wireless/bcm43xx/Makefile @@ -0,0 +1,12 @@ +obj-$(CONFIG_BCM43XX) += bcm43xx.o +bcm43xx-obj-$(CONFIG_BCM43XX_DEBUG) += bcm43xx_debugfs.o + +bcm43xx-obj-$(CONFIG_BCM43XX_DMA) += bcm43xx_dma.o +bcm43xx-obj-$(CONFIG_BCM43XX_PIO) += bcm43xx_pio.o + +bcm43xx-objs := bcm43xx_main.o bcm43xx_ilt.o \ + bcm43xx_radio.o bcm43xx_phy.o \ + bcm43xx_power.o bcm43xx_wx.o \ + bcm43xx_leds.o bcm43xx_ethtool.o \ + bcm43xx_xmit.o bcm43xx_sysfs.o \ + $(bcm43xx-obj-y) diff --git a/drivers/net/wireless/bcm43xx/bcm43xx.h b/drivers/net/wireless/bcm43xx/bcm43xx.h new file mode 100644 index 000000000000..dcadd295de4f --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx.h @@ -0,0 +1,926 @@ +#ifndef BCM43xx_H_ +#define BCM43xx_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "bcm43xx_debugfs.h" +#include "bcm43xx_leds.h" +#include "bcm43xx_sysfs.h" + + +#define PFX KBUILD_MODNAME ": " + +#define BCM43xx_SWITCH_CORE_MAX_RETRIES 50 +#define BCM43xx_IRQWAIT_MAX_RETRIES 50 + +#define BCM43xx_IO_SIZE 8192 + +/* Active Core PCI Configuration Register. */ +#define BCM43xx_PCICFG_ACTIVE_CORE 0x80 +/* SPROM control register. */ +#define BCM43xx_PCICFG_SPROMCTL 0x88 +/* Interrupt Control PCI Configuration Register. (Only on PCI cores with rev >= 6) */ +#define BCM43xx_PCICFG_ICR 0x94 + +/* MMIO offsets */ +#define BCM43xx_MMIO_DMA1_REASON 0x20 +#define BCM43xx_MMIO_DMA1_IRQ_MASK 0x24 +#define BCM43xx_MMIO_DMA2_REASON 0x28 +#define BCM43xx_MMIO_DMA2_IRQ_MASK 0x2C +#define BCM43xx_MMIO_DMA3_REASON 0x30 +#define BCM43xx_MMIO_DMA3_IRQ_MASK 0x34 +#define BCM43xx_MMIO_DMA4_REASON 0x38 +#define BCM43xx_MMIO_DMA4_IRQ_MASK 0x3C +#define BCM43xx_MMIO_STATUS_BITFIELD 0x120 +#define BCM43xx_MMIO_STATUS2_BITFIELD 0x124 +#define BCM43xx_MMIO_GEN_IRQ_REASON 0x128 +#define BCM43xx_MMIO_GEN_IRQ_MASK 0x12C +#define BCM43xx_MMIO_RAM_CONTROL 0x130 +#define BCM43xx_MMIO_RAM_DATA 0x134 +#define BCM43xx_MMIO_PS_STATUS 0x140 +#define BCM43xx_MMIO_RADIO_HWENABLED_HI 0x158 +#define BCM43xx_MMIO_SHM_CONTROL 0x160 +#define BCM43xx_MMIO_SHM_DATA 0x164 +#define BCM43xx_MMIO_SHM_DATA_UNALIGNED 0x166 +#define BCM43xx_MMIO_XMITSTAT_0 0x170 +#define BCM43xx_MMIO_XMITSTAT_1 0x174 +#define BCM43xx_MMIO_REV3PLUS_TSF_LOW 0x180 /* core rev >= 3 only */ +#define BCM43xx_MMIO_REV3PLUS_TSF_HIGH 0x184 /* core rev >= 3 only */ +#define BCM43xx_MMIO_DMA1_BASE 0x200 +#define BCM43xx_MMIO_DMA2_BASE 0x220 +#define BCM43xx_MMIO_DMA3_BASE 0x240 +#define BCM43xx_MMIO_DMA4_BASE 0x260 +#define BCM43xx_MMIO_PIO1_BASE 0x300 +#define BCM43xx_MMIO_PIO2_BASE 0x310 +#define BCM43xx_MMIO_PIO3_BASE 0x320 +#define BCM43xx_MMIO_PIO4_BASE 0x330 +#define BCM43xx_MMIO_PHY_VER 0x3E0 +#define BCM43xx_MMIO_PHY_RADIO 0x3E2 +#define BCM43xx_MMIO_ANTENNA 0x3E8 +#define BCM43xx_MMIO_CHANNEL 0x3F0 +#define BCM43xx_MMIO_CHANNEL_EXT 0x3F4 +#define BCM43xx_MMIO_RADIO_CONTROL 0x3F6 +#define BCM43xx_MMIO_RADIO_DATA_HIGH 0x3F8 +#define BCM43xx_MMIO_RADIO_DATA_LOW 0x3FA +#define BCM43xx_MMIO_PHY_CONTROL 0x3FC +#define BCM43xx_MMIO_PHY_DATA 0x3FE +#define BCM43xx_MMIO_MACFILTER_CONTROL 0x420 +#define BCM43xx_MMIO_MACFILTER_DATA 0x422 +#define BCM43xx_MMIO_RADIO_HWENABLED_LO 0x49A +#define BCM43xx_MMIO_GPIO_CONTROL 0x49C +#define BCM43xx_MMIO_GPIO_MASK 0x49E +#define BCM43xx_MMIO_TSF_0 0x632 /* core rev < 3 only */ +#define BCM43xx_MMIO_TSF_1 0x634 /* core rev < 3 only */ +#define BCM43xx_MMIO_TSF_2 0x636 /* core rev < 3 only */ +#define BCM43xx_MMIO_TSF_3 0x638 /* core rev < 3 only */ +#define BCM43xx_MMIO_POWERUP_DELAY 0x6A8 + +/* SPROM offsets. */ +#define BCM43xx_SPROM_BASE 0x1000 +#define BCM43xx_SPROM_BOARDFLAGS2 0x1c +#define BCM43xx_SPROM_IL0MACADDR 0x24 +#define BCM43xx_SPROM_ET0MACADDR 0x27 +#define BCM43xx_SPROM_ET1MACADDR 0x2a +#define BCM43xx_SPROM_ETHPHY 0x2d +#define BCM43xx_SPROM_BOARDREV 0x2e +#define BCM43xx_SPROM_PA0B0 0x2f +#define BCM43xx_SPROM_PA0B1 0x30 +#define BCM43xx_SPROM_PA0B2 0x31 +#define BCM43xx_SPROM_WL0GPIO0 0x32 +#define BCM43xx_SPROM_WL0GPIO2 0x33 +#define BCM43xx_SPROM_MAXPWR 0x34 +#define BCM43xx_SPROM_PA1B0 0x35 +#define BCM43xx_SPROM_PA1B1 0x36 +#define BCM43xx_SPROM_PA1B2 0x37 +#define BCM43xx_SPROM_IDL_TSSI_TGT 0x38 +#define BCM43xx_SPROM_BOARDFLAGS 0x39 +#define BCM43xx_SPROM_ANTENNA_GAIN 0x3a +#define BCM43xx_SPROM_VERSION 0x3f + +/* BCM43xx_SPROM_BOARDFLAGS values */ +#define BCM43xx_BFL_BTCOEXIST 0x0001 /* implements Bluetooth coexistance */ +#define BCM43xx_BFL_PACTRL 0x0002 /* GPIO 9 controlling the PA */ +#define BCM43xx_BFL_AIRLINEMODE 0x0004 /* implements GPIO 13 radio disable indication */ +#define BCM43xx_BFL_RSSI 0x0008 /* software calculates nrssi slope. */ +#define BCM43xx_BFL_ENETSPI 0x0010 /* has ephy roboswitch spi */ +#define BCM43xx_BFL_XTAL_NOSLOW 0x0020 /* no slow clock available */ +#define BCM43xx_BFL_CCKHIPWR 0x0040 /* can do high power CCK transmission */ +#define BCM43xx_BFL_ENETADM 0x0080 /* has ADMtek switch */ +#define BCM43xx_BFL_ENETVLAN 0x0100 /* can do vlan */ +#define BCM43xx_BFL_AFTERBURNER 0x0200 /* supports Afterburner mode */ +#define BCM43xx_BFL_NOPCI 0x0400 /* leaves PCI floating */ +#define BCM43xx_BFL_FEM 0x0800 /* supports the Front End Module */ +#define BCM43xx_BFL_EXTLNA 0x1000 /* has an external LNA */ +#define BCM43xx_BFL_HGPA 0x2000 /* had high gain PA */ +#define BCM43xx_BFL_BTCMOD 0x4000 /* BFL_BTCOEXIST is given in alternate GPIOs */ +#define BCM43xx_BFL_ALTIQ 0x8000 /* alternate I/Q settings */ + +/* GPIO register offset, in both ChipCommon and PCI core. */ +#define BCM43xx_GPIO_CONTROL 0x6c + +/* SHM Routing */ +#define BCM43xx_SHM_SHARED 0x0001 +#define BCM43xx_SHM_WIRELESS 0x0002 +#define BCM43xx_SHM_PCM 0x0003 +#define BCM43xx_SHM_HWMAC 0x0004 +#define BCM43xx_SHM_UCODE 0x0300 + +/* MacFilter offsets. */ +#define BCM43xx_MACFILTER_SELF 0x0000 +#define BCM43xx_MACFILTER_ASSOC 0x0003 + +/* Chipcommon registers. */ +#define BCM43xx_CHIPCOMMON_CAPABILITIES 0x04 +#define BCM43xx_CHIPCOMMON_PLLONDELAY 0xB0 +#define BCM43xx_CHIPCOMMON_FREFSELDELAY 0xB4 +#define BCM43xx_CHIPCOMMON_SLOWCLKCTL 0xB8 +#define BCM43xx_CHIPCOMMON_SYSCLKCTL 0xC0 + +/* PCI core specific registers. */ +#define BCM43xx_PCICORE_BCAST_ADDR 0x50 +#define BCM43xx_PCICORE_BCAST_DATA 0x54 +#define BCM43xx_PCICORE_SBTOPCI2 0x108 + +/* SBTOPCI2 values. */ +#define BCM43xx_SBTOPCI2_PREFETCH 0x4 +#define BCM43xx_SBTOPCI2_BURST 0x8 + +/* Chipcommon capabilities. */ +#define BCM43xx_CAPABILITIES_PCTL 0x00040000 +#define BCM43xx_CAPABILITIES_PLLMASK 0x00030000 +#define BCM43xx_CAPABILITIES_PLLSHIFT 16 +#define BCM43xx_CAPABILITIES_FLASHMASK 0x00000700 +#define BCM43xx_CAPABILITIES_FLASHSHIFT 8 +#define BCM43xx_CAPABILITIES_EXTBUSPRESENT 0x00000040 +#define BCM43xx_CAPABILITIES_UARTGPIO 0x00000020 +#define BCM43xx_CAPABILITIES_UARTCLOCKMASK 0x00000018 +#define BCM43xx_CAPABILITIES_UARTCLOCKSHIFT 3 +#define BCM43xx_CAPABILITIES_MIPSBIGENDIAN 0x00000004 +#define BCM43xx_CAPABILITIES_NRUARTSMASK 0x00000003 + +/* PowerControl */ +#define BCM43xx_PCTL_IN 0xB0 +#define BCM43xx_PCTL_OUT 0xB4 +#define BCM43xx_PCTL_OUTENABLE 0xB8 +#define BCM43xx_PCTL_XTAL_POWERUP 0x40 +#define BCM43xx_PCTL_PLL_POWERDOWN 0x80 + +/* PowerControl Clock Modes */ +#define BCM43xx_PCTL_CLK_FAST 0x00 +#define BCM43xx_PCTL_CLK_SLOW 0x01 +#define BCM43xx_PCTL_CLK_DYNAMIC 0x02 + +#define BCM43xx_PCTL_FORCE_SLOW 0x0800 +#define BCM43xx_PCTL_FORCE_PLL 0x1000 +#define BCM43xx_PCTL_DYN_XTAL 0x2000 + +/* COREIDs */ +#define BCM43xx_COREID_CHIPCOMMON 0x800 +#define BCM43xx_COREID_ILINE20 0x801 +#define BCM43xx_COREID_SDRAM 0x803 +#define BCM43xx_COREID_PCI 0x804 +#define BCM43xx_COREID_MIPS 0x805 +#define BCM43xx_COREID_ETHERNET 0x806 +#define BCM43xx_COREID_V90 0x807 +#define BCM43xx_COREID_USB11_HOSTDEV 0x80a +#define BCM43xx_COREID_IPSEC 0x80b +#define BCM43xx_COREID_PCMCIA 0x80d +#define BCM43xx_COREID_EXT_IF 0x80f +#define BCM43xx_COREID_80211 0x812 +#define BCM43xx_COREID_MIPS_3302 0x816 +#define BCM43xx_COREID_USB11_HOST 0x817 +#define BCM43xx_COREID_USB11_DEV 0x818 +#define BCM43xx_COREID_USB20_HOST 0x819 +#define BCM43xx_COREID_USB20_DEV 0x81a +#define BCM43xx_COREID_SDIO_HOST 0x81b + +/* Core Information Registers */ +#define BCM43xx_CIR_BASE 0xf00 +#define BCM43xx_CIR_SBTPSFLAG (BCM43xx_CIR_BASE + 0x18) +#define BCM43xx_CIR_SBIMSTATE (BCM43xx_CIR_BASE + 0x90) +#define BCM43xx_CIR_SBINTVEC (BCM43xx_CIR_BASE + 0x94) +#define BCM43xx_CIR_SBTMSTATELOW (BCM43xx_CIR_BASE + 0x98) +#define BCM43xx_CIR_SBTMSTATEHIGH (BCM43xx_CIR_BASE + 0x9c) +#define BCM43xx_CIR_SBIMCONFIGLOW (BCM43xx_CIR_BASE + 0xa8) +#define BCM43xx_CIR_SB_ID_HI (BCM43xx_CIR_BASE + 0xfc) + +/* Mask to get the Backplane Flag Number from SBTPSFLAG. */ +#define BCM43xx_BACKPLANE_FLAG_NR_MASK 0x3f + +/* SBIMCONFIGLOW values/masks. */ +#define BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_MASK 0x00000007 +#define BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_SHIFT 0 +#define BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_MASK 0x00000070 +#define BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_SHIFT 4 +#define BCM43xx_SBIMCONFIGLOW_CONNID_MASK 0x00ff0000 +#define BCM43xx_SBIMCONFIGLOW_CONNID_SHIFT 16 + +/* sbtmstatelow state flags */ +#define BCM43xx_SBTMSTATELOW_RESET 0x01 +#define BCM43xx_SBTMSTATELOW_REJECT 0x02 +#define BCM43xx_SBTMSTATELOW_CLOCK 0x10000 +#define BCM43xx_SBTMSTATELOW_FORCE_GATE_CLOCK 0x20000 + +/* sbtmstatehigh state flags */ +#define BCM43xx_SBTMSTATEHIGH_SERROR 0x1 +#define BCM43xx_SBTMSTATEHIGH_BUSY 0x4 + +/* sbimstate flags */ +#define BCM43xx_SBIMSTATE_IB_ERROR 0x20000 +#define BCM43xx_SBIMSTATE_TIMEOUT 0x40000 + +/* PHYVersioning */ +#define BCM43xx_PHYTYPE_A 0x00 +#define BCM43xx_PHYTYPE_B 0x01 +#define BCM43xx_PHYTYPE_G 0x02 + +/* PHYRegisters */ +#define BCM43xx_PHY_ILT_A_CTRL 0x0072 +#define BCM43xx_PHY_ILT_A_DATA1 0x0073 +#define BCM43xx_PHY_ILT_A_DATA2 0x0074 +#define BCM43xx_PHY_G_LO_CONTROL 0x0810 +#define BCM43xx_PHY_ILT_G_CTRL 0x0472 +#define BCM43xx_PHY_ILT_G_DATA1 0x0473 +#define BCM43xx_PHY_ILT_G_DATA2 0x0474 +#define BCM43xx_PHY_A_PCTL 0x007B +#define BCM43xx_PHY_G_PCTL 0x0029 +#define BCM43xx_PHY_A_CRS 0x0029 +#define BCM43xx_PHY_RADIO_BITFIELD 0x0401 +#define BCM43xx_PHY_G_CRS 0x0429 +#define BCM43xx_PHY_NRSSILT_CTRL 0x0803 +#define BCM43xx_PHY_NRSSILT_DATA 0x0804 + +/* RadioRegisters */ +#define BCM43xx_RADIOCTL_ID 0x01 + +/* StatusBitField */ +#define BCM43xx_SBF_MAC_ENABLED 0x00000001 +#define BCM43xx_SBF_2 0x00000002 /*FIXME: fix name*/ +#define BCM43xx_SBF_CORE_READY 0x00000004 +#define BCM43xx_SBF_400 0x00000400 /*FIXME: fix name*/ +#define BCM43xx_SBF_4000 0x00004000 /*FIXME: fix name*/ +#define BCM43xx_SBF_8000 0x00008000 /*FIXME: fix name*/ +#define BCM43xx_SBF_XFER_REG_BYTESWAP 0x00010000 +#define BCM43xx_SBF_MODE_NOTADHOC 0x00020000 +#define BCM43xx_SBF_MODE_AP 0x00040000 +#define BCM43xx_SBF_RADIOREG_LOCK 0x00080000 +#define BCM43xx_SBF_MODE_MONITOR 0x00400000 +#define BCM43xx_SBF_MODE_PROMISC 0x01000000 +#define BCM43xx_SBF_PS1 0x02000000 +#define BCM43xx_SBF_PS2 0x04000000 +#define BCM43xx_SBF_NO_SSID_BCAST 0x08000000 +#define BCM43xx_SBF_TIME_UPDATE 0x10000000 +#define BCM43xx_SBF_80000000 0x80000000 /*FIXME: fix name*/ + +/* MicrocodeFlagsBitfield (addr + lo-word values?)*/ +#define BCM43xx_UCODEFLAGS_OFFSET 0x005E + +#define BCM43xx_UCODEFLAG_AUTODIV 0x0001 +#define BCM43xx_UCODEFLAG_UNKBGPHY 0x0002 +#define BCM43xx_UCODEFLAG_UNKBPHY 0x0004 +#define BCM43xx_UCODEFLAG_UNKGPHY 0x0020 +#define BCM43xx_UCODEFLAG_UNKPACTRL 0x0040 +#define BCM43xx_UCODEFLAG_JAPAN 0x0080 + +/* Generic-Interrupt reasons. */ +#define BCM43xx_IRQ_READY (1 << 0) +#define BCM43xx_IRQ_BEACON (1 << 1) +#define BCM43xx_IRQ_PS (1 << 2) +#define BCM43xx_IRQ_REG124 (1 << 5) +#define BCM43xx_IRQ_PMQ (1 << 6) +#define BCM43xx_IRQ_PIO_WORKAROUND (1 << 8) +#define BCM43xx_IRQ_XMIT_ERROR (1 << 11) +#define BCM43xx_IRQ_RX (1 << 15) +#define BCM43xx_IRQ_SCAN (1 << 16) +#define BCM43xx_IRQ_NOISE (1 << 18) +#define BCM43xx_IRQ_XMIT_STATUS (1 << 29) + +#define BCM43xx_IRQ_ALL 0xffffffff +#define BCM43xx_IRQ_INITIAL (BCM43xx_IRQ_PS | \ + BCM43xx_IRQ_REG124 | \ + BCM43xx_IRQ_PMQ | \ + BCM43xx_IRQ_XMIT_ERROR | \ + BCM43xx_IRQ_RX | \ + BCM43xx_IRQ_SCAN | \ + BCM43xx_IRQ_NOISE | \ + BCM43xx_IRQ_XMIT_STATUS) + + +/* Initial default iw_mode */ +#define BCM43xx_INITIAL_IWMODE IW_MODE_INFRA + +/* Bus type PCI. */ +#define BCM43xx_BUSTYPE_PCI 0 +/* Bus type Silicone Backplane Bus. */ +#define BCM43xx_BUSTYPE_SB 1 +/* Bus type PCMCIA. */ +#define BCM43xx_BUSTYPE_PCMCIA 2 + +/* Threshold values. */ +#define BCM43xx_MIN_RTS_THRESHOLD 1U +#define BCM43xx_MAX_RTS_THRESHOLD 2304U +#define BCM43xx_DEFAULT_RTS_THRESHOLD BCM43xx_MAX_RTS_THRESHOLD + +#define BCM43xx_DEFAULT_SHORT_RETRY_LIMIT 7 +#define BCM43xx_DEFAULT_LONG_RETRY_LIMIT 4 + +/* Max size of a security key */ +#define BCM43xx_SEC_KEYSIZE 16 +/* Security algorithms. */ +enum { + BCM43xx_SEC_ALGO_NONE = 0, /* unencrypted, as of TX header. */ + BCM43xx_SEC_ALGO_WEP, + BCM43xx_SEC_ALGO_UNKNOWN, + BCM43xx_SEC_ALGO_AES, + BCM43xx_SEC_ALGO_WEP104, + BCM43xx_SEC_ALGO_TKIP, +}; + +#ifdef assert +# undef assert +#endif +#ifdef CONFIG_BCM43XX_DEBUG +#define assert(expr) \ + do { \ + if (unlikely(!(expr))) { \ + printk(KERN_ERR PFX "ASSERTION FAILED (%s) at: %s:%d:%s()\n", \ + #expr, __FILE__, __LINE__, __FUNCTION__); \ + } \ + } while (0) +#else +#define assert(expr) do { /* nothing */ } while (0) +#endif + +/* rate limited printk(). */ +#ifdef printkl +# undef printkl +#endif +#define printkl(f, x...) do { if (printk_ratelimit()) printk(f ,##x); } while (0) +/* rate limited printk() for debugging */ +#ifdef dprintkl +# undef dprintkl +#endif +#ifdef CONFIG_BCM43XX_DEBUG +# define dprintkl printkl +#else +# define dprintkl(f, x...) do { /* nothing */ } while (0) +#endif + +/* Helper macro for if branches. + * An if branch marked with this macro is only taken in DEBUG mode. + * Example: + * if (DEBUG_ONLY(foo == bar)) { + * do something + * } + * In DEBUG mode, the branch will be taken if (foo == bar). + * In non-DEBUG mode, the branch will never be taken. + */ +#ifdef DEBUG_ONLY +# undef DEBUG_ONLY +#endif +#ifdef CONFIG_BCM43XX_DEBUG +# define DEBUG_ONLY(x) (x) +#else +# define DEBUG_ONLY(x) 0 +#endif + +/* debugging printk() */ +#ifdef dprintk +# undef dprintk +#endif +#ifdef CONFIG_BCM43XX_DEBUG +# define dprintk(f, x...) do { printk(f ,##x); } while (0) +#else +# define dprintk(f, x...) do { /* nothing */ } while (0) +#endif + + +struct net_device; +struct pci_dev; +struct bcm43xx_dmaring; +struct bcm43xx_pioqueue; + +struct bcm43xx_initval { + u16 offset; + u16 size; + u32 value; +} __attribute__((__packed__)); + +/* Values for bcm430x_sprominfo.locale */ +enum { + BCM43xx_LOCALE_WORLD = 0, + BCM43xx_LOCALE_THAILAND, + BCM43xx_LOCALE_ISRAEL, + BCM43xx_LOCALE_JORDAN, + BCM43xx_LOCALE_CHINA, + BCM43xx_LOCALE_JAPAN, + BCM43xx_LOCALE_USA_CANADA_ANZ, + BCM43xx_LOCALE_EUROPE, + BCM43xx_LOCALE_USA_LOW, + BCM43xx_LOCALE_JAPAN_HIGH, + BCM43xx_LOCALE_ALL, + BCM43xx_LOCALE_NONE, +}; + +#define BCM43xx_SPROM_SIZE 64 /* in 16-bit words. */ +struct bcm43xx_sprominfo { + u16 boardflags2; + u8 il0macaddr[6]; + u8 et0macaddr[6]; + u8 et1macaddr[6]; + u8 et0phyaddr:5; + u8 et1phyaddr:5; + u8 et0mdcport:1; + u8 et1mdcport:1; + u8 boardrev; + u8 locale:4; + u8 antennas_aphy:2; + u8 antennas_bgphy:2; + u16 pa0b0; + u16 pa0b1; + u16 pa0b2; + u8 wl0gpio0; + u8 wl0gpio1; + u8 wl0gpio2; + u8 wl0gpio3; + u8 maxpower_aphy; + u8 maxpower_bgphy; + u16 pa1b0; + u16 pa1b1; + u16 pa1b2; + u8 idle_tssi_tgt_aphy; + u8 idle_tssi_tgt_bgphy; + u16 boardflags; + u16 antennagain_aphy; + u16 antennagain_bgphy; +}; + +/* Value pair to measure the LocalOscillator. */ +struct bcm43xx_lopair { + s8 low; + s8 high; + u8 used:1; +}; +#define BCM43xx_LO_COUNT (14*4) + +struct bcm43xx_phyinfo { + /* Hardware Data */ + u8 version; + u8 type; + u8 rev; + u16 antenna_diversity; + u16 savedpctlreg; + u16 minlowsig[2]; + u16 minlowsigpos[2]; + u8 connected:1, + calibrated:1, + is_locked:1, /* used in bcm43xx_phy_{un}lock() */ + dyn_tssi_tbl:1; /* used in bcm43xx_phy_init_tssi2dbm_table() */ + /* LO Measurement Data. + * Use bcm43xx_get_lopair() to get a value. + */ + struct bcm43xx_lopair *_lo_pairs; + + /* TSSI to dBm table in use */ + const s8 *tssi2dbm; + /* idle TSSI value */ + s8 idle_tssi; + + /* Values from bcm43xx_calc_loopback_gain() */ + u16 loopback_gain[2]; + + /* PHY lock for core.rev < 3 + * This lock is only used by bcm43xx_phy_{un}lock() + */ + spinlock_t lock; +}; + + +struct bcm43xx_radioinfo { + u16 manufact; + u16 version; + u8 revision; + + /* Desired TX power in dBm Q5.2 */ + u16 txpower_desired; + /* TX Power control values. */ + union { + /* B/G PHY */ + struct { + u16 baseband_atten; + u16 radio_atten; + u16 txctl1; + u16 txctl2; + }; + /* A PHY */ + struct { + u16 txpwr_offset; + }; + }; + + /* Current Interference Mitigation mode */ + int interfmode; + /* Stack of saved values from the Interference Mitigation code. + * Each value in the stack is layed out as follows: + * bit 0-11: offset + * bit 12-15: register ID + * bit 16-32: value + * register ID is: 0x1 PHY, 0x2 Radio, 0x3 ILT + */ +#define BCM43xx_INTERFSTACK_SIZE 26 + u32 interfstack[BCM43xx_INTERFSTACK_SIZE]; + + /* Saved values from the NRSSI Slope calculation */ + s16 nrssi[2]; + s32 nrssislope; + /* In memory nrssi lookup table. */ + s8 nrssi_lt[64]; + + /* current channel */ + u8 channel; + u8 initial_channel; + + u16 lofcal; + + u16 initval; + + u8 enabled:1; + /* ACI (adjacent channel interference) flags. */ + u8 aci_enable:1, + aci_wlan_automatic:1, + aci_hw_rssi:1; +}; + +/* Data structures for DMA transmission, per 80211 core. */ +struct bcm43xx_dma { + struct bcm43xx_dmaring *tx_ring0; + struct bcm43xx_dmaring *tx_ring1; + struct bcm43xx_dmaring *tx_ring2; + struct bcm43xx_dmaring *tx_ring3; + struct bcm43xx_dmaring *rx_ring0; + struct bcm43xx_dmaring *rx_ring1; /* only available on core.rev < 5 */ +}; + +/* Data structures for PIO transmission, per 80211 core. */ +struct bcm43xx_pio { + struct bcm43xx_pioqueue *queue0; + struct bcm43xx_pioqueue *queue1; + struct bcm43xx_pioqueue *queue2; + struct bcm43xx_pioqueue *queue3; +}; + +#define BCM43xx_MAX_80211_CORES 2 + +#ifdef CONFIG_BCM947XX +#define core_offset(bcm) (bcm)->current_core_offset +#else +#define core_offset(bcm) 0 +#endif + +/* Generic information about a core. */ +struct bcm43xx_coreinfo { + u8 available:1, + enabled:1, + initialized:1; + /** core_id ID number */ + u16 id; + /** core_rev revision number */ + u8 rev; + /** Index number for _switch_core() */ + u8 index; +}; + +/* Additional information for each 80211 core. */ +struct bcm43xx_coreinfo_80211 { + /* PHY device. */ + struct bcm43xx_phyinfo phy; + /* Radio device. */ + struct bcm43xx_radioinfo radio; + union { + /* DMA context. */ + struct bcm43xx_dma dma; + /* PIO context. */ + struct bcm43xx_pio pio; + }; +}; + +/* Context information for a noise calculation (Link Quality). */ +struct bcm43xx_noise_calculation { + struct bcm43xx_coreinfo *core_at_start; + u8 channel_at_start; + u8 calculation_running:1; + u8 nr_samples; + s8 samples[8][4]; +}; + +struct bcm43xx_stats { + u8 link_quality; + u8 noise; + struct iw_statistics wstats; + /* Store the last TX/RX times here for updating the leds. */ + unsigned long last_tx; + unsigned long last_rx; +}; + +struct bcm43xx_key { + u8 enabled:1; + u8 algorithm; +}; + +struct bcm43xx_private { + struct bcm43xx_sysfs sysfs; + + struct ieee80211_device *ieee; + struct ieee80211softmac_device *softmac; + + struct net_device *net_dev; + struct pci_dev *pci_dev; + unsigned int irq; + + void __iomem *mmio_addr; + unsigned int mmio_len; + + /* Do not use the lock directly. Use the bcm43xx_lock* helper + * functions, to be MMIO-safe. */ + spinlock_t _lock; + + /* Driver status flags. */ + u32 initialized:1, /* init_board() succeed */ + was_initialized:1, /* for PCI suspend/resume. */ + shutting_down:1, /* free_board() in progress */ + __using_pio:1, /* Internal, use bcm43xx_using_pio(). */ + bad_frames_preempt:1, /* Use "Bad Frames Preemption" (default off) */ + reg124_set_0x4:1, /* Some variable to keep track of IRQ stuff. */ + powersaving:1, /* TRUE if we are in PowerSaving mode. FALSE otherwise. */ + short_preamble:1, /* TRUE, if short preamble is enabled. */ + firmware_norelease:1; /* Do not release the firmware. Used on suspend. */ + + struct bcm43xx_stats stats; + + /* Bus type we are connected to. + * This is currently always BCM43xx_BUSTYPE_PCI + */ + u8 bustype; + + u16 board_vendor; + u16 board_type; + u16 board_revision; + + u16 chip_id; + u8 chip_rev; + u8 chip_package; + + struct bcm43xx_sprominfo sprom; +#define BCM43xx_NR_LEDS 4 + struct bcm43xx_led leds[BCM43xx_NR_LEDS]; + + /* The currently active core. */ + struct bcm43xx_coreinfo *current_core; +#ifdef CONFIG_BCM947XX + /** current core memory offset */ + u32 current_core_offset; +#endif + struct bcm43xx_coreinfo *active_80211_core; + /* coreinfo structs for all possible cores follow. + * Note that a core might not exist. + * So check the coreinfo flags before using it. + */ + struct bcm43xx_coreinfo core_chipcommon; + struct bcm43xx_coreinfo core_pci; + struct bcm43xx_coreinfo core_80211[ BCM43xx_MAX_80211_CORES ]; + /* Additional information, specific to the 80211 cores. */ + struct bcm43xx_coreinfo_80211 core_80211_ext[ BCM43xx_MAX_80211_CORES ]; + /* Index of the current 80211 core. If current_core is not + * an 80211 core, this is -1. + */ + int current_80211_core_idx; + /* Number of available 80211 cores. */ + int nr_80211_available; + + u32 chipcommon_capabilities; + + /* Reason code of the last interrupt. */ + u32 irq_reason; + u32 dma_reason[4]; + /* saved irq enable/disable state bitfield. */ + u32 irq_savedstate; + /* Link Quality calculation context. */ + struct bcm43xx_noise_calculation noisecalc; + + /* Threshold values. */ + //TODO: The RTS thr has to be _used_. Currently, it is only set via WX. + u32 rts_threshold; + + /* Interrupt Service Routine tasklet (bottom-half) */ + struct tasklet_struct isr_tasklet; + + /* Periodic tasks */ + struct timer_list periodic_tasks; + unsigned int periodic_state; + + struct work_struct restart_work; + + /* Informational stuff. */ + char nick[IW_ESSID_MAX_SIZE + 1]; + + /* encryption/decryption */ + u16 security_offset; + struct bcm43xx_key key[54]; + u8 default_key_idx; + + /* Firmware. */ + const struct firmware *ucode; + const struct firmware *pcm; + const struct firmware *initvals0; + const struct firmware *initvals1; + + /* Debugging stuff follows. */ +#ifdef CONFIG_BCM43XX_DEBUG + struct bcm43xx_dfsentry *dfsentry; +#endif +}; + +/* bcm43xx_(un)lock() protect struct bcm43xx_private. + * Note that _NO_ MMIO writes are allowed. If you want to + * write to the device through MMIO in the critical section, use + * the *_mmio lock functions. + * MMIO read-access is allowed, though. + */ +#define bcm43xx_lock(bcm, flags) spin_lock_irqsave(&(bcm)->_lock, flags) +#define bcm43xx_unlock(bcm, flags) spin_unlock_irqrestore(&(bcm)->_lock, flags) +/* bcm43xx_(un)lock_mmio() protect struct bcm43xx_private and MMIO. + * MMIO write-access to the device is allowed. + * All MMIO writes are flushed on unlock, so it is guaranteed to not + * interfere with other threads writing MMIO registers. + */ +#define bcm43xx_lock_mmio(bcm, flags) bcm43xx_lock(bcm, flags) +#define bcm43xx_unlock_mmio(bcm, flags) do { mmiowb(); bcm43xx_unlock(bcm, flags); } while (0) + +static inline +struct bcm43xx_private * bcm43xx_priv(struct net_device *dev) +{ + return ieee80211softmac_priv(dev); +} + + +/* Helper function, which returns a boolean. + * TRUE, if PIO is used; FALSE, if DMA is used. + */ +#if defined(CONFIG_BCM43XX_DMA) && defined(CONFIG_BCM43XX_PIO) +static inline +int bcm43xx_using_pio(struct bcm43xx_private *bcm) +{ + return bcm->__using_pio; +} +#elif defined(CONFIG_BCM43XX_DMA) +static inline +int bcm43xx_using_pio(struct bcm43xx_private *bcm) +{ + return 0; +} +#elif defined(CONFIG_BCM43XX_PIO) +static inline +int bcm43xx_using_pio(struct bcm43xx_private *bcm) +{ + return 1; +} +#else +# error "Using neither DMA nor PIO? Confused..." +#endif + +/* Helper functions to access data structures private to the 80211 cores. + * Note that we _must_ have an 80211 core mapped when calling + * any of these functions. + */ +static inline +struct bcm43xx_pio * bcm43xx_current_pio(struct bcm43xx_private *bcm) +{ + assert(bcm43xx_using_pio(bcm)); + assert(bcm->current_80211_core_idx >= 0); + assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES); + return &(bcm->core_80211_ext[bcm->current_80211_core_idx].pio); +} +static inline +struct bcm43xx_dma * bcm43xx_current_dma(struct bcm43xx_private *bcm) +{ + assert(!bcm43xx_using_pio(bcm)); + assert(bcm->current_80211_core_idx >= 0); + assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES); + return &(bcm->core_80211_ext[bcm->current_80211_core_idx].dma); +} +static inline +struct bcm43xx_phyinfo * bcm43xx_current_phy(struct bcm43xx_private *bcm) +{ + assert(bcm->current_80211_core_idx >= 0); + assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES); + return &(bcm->core_80211_ext[bcm->current_80211_core_idx].phy); +} +static inline +struct bcm43xx_radioinfo * bcm43xx_current_radio(struct bcm43xx_private *bcm) +{ + assert(bcm->current_80211_core_idx >= 0); + assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES); + return &(bcm->core_80211_ext[bcm->current_80211_core_idx].radio); +} + +/* Are we running in init_board() context? */ +static inline +int bcm43xx_is_initializing(struct bcm43xx_private *bcm) +{ + if (bcm->initialized) + return 0; + if (bcm->shutting_down) + return 0; + return 1; +} + +static inline +struct bcm43xx_lopair * bcm43xx_get_lopair(struct bcm43xx_phyinfo *phy, + u16 radio_attenuation, + u16 baseband_attenuation) +{ + return phy->_lo_pairs + (radio_attenuation + 14 * (baseband_attenuation / 2)); +} + + +static inline +u16 bcm43xx_read16(struct bcm43xx_private *bcm, u16 offset) +{ + return ioread16(bcm->mmio_addr + core_offset(bcm) + offset); +} + +static inline +void bcm43xx_write16(struct bcm43xx_private *bcm, u16 offset, u16 value) +{ + iowrite16(value, bcm->mmio_addr + core_offset(bcm) + offset); +} + +static inline +u32 bcm43xx_read32(struct bcm43xx_private *bcm, u16 offset) +{ + return ioread32(bcm->mmio_addr + core_offset(bcm) + offset); +} + +static inline +void bcm43xx_write32(struct bcm43xx_private *bcm, u16 offset, u32 value) +{ + iowrite32(value, bcm->mmio_addr + core_offset(bcm) + offset); +} + +static inline +int bcm43xx_pci_read_config16(struct bcm43xx_private *bcm, int offset, u16 *value) +{ + return pci_read_config_word(bcm->pci_dev, offset, value); +} + +static inline +int bcm43xx_pci_read_config32(struct bcm43xx_private *bcm, int offset, u32 *value) +{ + return pci_read_config_dword(bcm->pci_dev, offset, value); +} + +static inline +int bcm43xx_pci_write_config16(struct bcm43xx_private *bcm, int offset, u16 value) +{ + return pci_write_config_word(bcm->pci_dev, offset, value); +} + +static inline +int bcm43xx_pci_write_config32(struct bcm43xx_private *bcm, int offset, u32 value) +{ + return pci_write_config_dword(bcm->pci_dev, offset, value); +} + +/** Limit a value between two limits */ +#ifdef limit_value +# undef limit_value +#endif +#define limit_value(value, min, max) \ + ({ \ + typeof(value) __value = (value); \ + typeof(value) __min = (min); \ + typeof(value) __max = (max); \ + if (__value < __min) \ + __value = __min; \ + else if (__value > __max) \ + __value = __max; \ + __value; \ + }) + +/** Helpers to print MAC addresses. */ +#define BCM43xx_MACFMT "%02x:%02x:%02x:%02x:%02x:%02x" +#define BCM43xx_MACARG(x) ((u8*)(x))[0], ((u8*)(x))[1], \ + ((u8*)(x))[2], ((u8*)(x))[3], \ + ((u8*)(x))[4], ((u8*)(x))[5] + +#endif /* BCM43xx_H_ */ diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c b/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c new file mode 100644 index 000000000000..d2c3401e9b70 --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c @@ -0,0 +1,499 @@ +/* + + Broadcom BCM43xx wireless driver + + debugfs driver debugging code + + Copyright (c) 2005 Michael Buesch + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + + + +#include +#include +#include +#include +#include +#include + +#include "bcm43xx.h" +#include "bcm43xx_main.h" +#include "bcm43xx_debugfs.h" +#include "bcm43xx_dma.h" +#include "bcm43xx_pio.h" +#include "bcm43xx_xmit.h" + +#define REALLY_BIG_BUFFER_SIZE (1024*256) + +static struct bcm43xx_debugfs fs; +static char really_big_buffer[REALLY_BIG_BUFFER_SIZE]; +static DECLARE_MUTEX(big_buffer_sem); + + +static ssize_t write_file_dummy(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return count; +} + +static int open_file_generic(struct inode *inode, struct file *file) +{ + file->private_data = inode->u.generic_ip; + return 0; +} + +#define fappend(fmt, x...) pos += snprintf(buf + pos, len - pos, fmt , ##x) + +static ssize_t devinfo_read_file(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + const size_t len = REALLY_BIG_BUFFER_SIZE; + + struct bcm43xx_private *bcm = file->private_data; + char *buf = really_big_buffer; + size_t pos = 0; + ssize_t res; + struct net_device *net_dev; + struct pci_dev *pci_dev; + unsigned long flags; + u16 tmp16; + int i; + + down(&big_buffer_sem); + + bcm43xx_lock_mmio(bcm, flags); + if (!bcm->initialized) { + fappend("Board not initialized.\n"); + goto out; + } + net_dev = bcm->net_dev; + pci_dev = bcm->pci_dev; + + /* This is where the information is written to the "devinfo" file */ + fappend("*** %s devinfo ***\n", net_dev->name); + fappend("vendor: 0x%04x device: 0x%04x\n", + pci_dev->vendor, pci_dev->device); + fappend("subsystem_vendor: 0x%04x subsystem_device: 0x%04x\n", + pci_dev->subsystem_vendor, pci_dev->subsystem_device); + fappend("IRQ: %d\n", bcm->irq); + fappend("mmio_addr: 0x%p mmio_len: %u\n", bcm->mmio_addr, bcm->mmio_len); + fappend("chip_id: 0x%04x chip_rev: 0x%02x\n", bcm->chip_id, bcm->chip_rev); + if ((bcm->core_80211[0].rev >= 3) && (bcm43xx_read32(bcm, 0x0158) & (1 << 16))) + fappend("Radio disabled by hardware!\n"); + if ((bcm->core_80211[0].rev < 3) && !(bcm43xx_read16(bcm, 0x049A) & (1 << 4))) + fappend("Radio disabled by hardware!\n"); + fappend("board_vendor: 0x%04x board_type: 0x%04x\n", bcm->board_vendor, + bcm->board_type); + + fappend("\nCores:\n"); +#define fappend_core(name, info) fappend("core \"" name "\" %s, %s, id: 0x%04x, " \ + "rev: 0x%02x, index: 0x%02x\n", \ + (info).available \ + ? "available" : "nonavailable", \ + (info).enabled \ + ? "enabled" : "disabled", \ + (info).id, (info).rev, (info).index) + fappend_core("CHIPCOMMON", bcm->core_chipcommon); + fappend_core("PCI", bcm->core_pci); + fappend_core("first 80211", bcm->core_80211[0]); + fappend_core("second 80211", bcm->core_80211[1]); +#undef fappend_core + tmp16 = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL); + fappend("LEDs: "); + for (i = 0; i < BCM43xx_NR_LEDS; i++) + fappend("%d ", !!(tmp16 & (1 << i))); + fappend("\n"); + +out: + bcm43xx_unlock_mmio(bcm, flags); + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + up(&big_buffer_sem); + return res; +} + +static ssize_t drvinfo_read_file(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + const size_t len = REALLY_BIG_BUFFER_SIZE; + + char *buf = really_big_buffer; + size_t pos = 0; + ssize_t res; + + down(&big_buffer_sem); + + /* This is where the information is written to the "driver" file */ + fappend(KBUILD_MODNAME " driver\n"); + fappend("Compiled at: %s %s\n", __DATE__, __TIME__); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + up(&big_buffer_sem); + return res; +} + +static ssize_t spromdump_read_file(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + const size_t len = REALLY_BIG_BUFFER_SIZE; + + struct bcm43xx_private *bcm = file->private_data; + char *buf = really_big_buffer; + size_t pos = 0; + ssize_t res; + unsigned long flags; + + down(&big_buffer_sem); + bcm43xx_lock_mmio(bcm, flags); + if (!bcm->initialized) { + fappend("Board not initialized.\n"); + goto out; + } + + /* This is where the information is written to the "sprom_dump" file */ + fappend("boardflags: 0x%04x\n", bcm->sprom.boardflags); + +out: + bcm43xx_unlock_mmio(bcm, flags); + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + up(&big_buffer_sem); + return res; +} + +static ssize_t tsf_read_file(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + const size_t len = REALLY_BIG_BUFFER_SIZE; + + struct bcm43xx_private *bcm = file->private_data; + char *buf = really_big_buffer; + size_t pos = 0; + ssize_t res; + unsigned long flags; + u64 tsf; + + down(&big_buffer_sem); + bcm43xx_lock_mmio(bcm, flags); + if (!bcm->initialized) { + fappend("Board not initialized.\n"); + goto out; + } + bcm43xx_tsf_read(bcm, &tsf); + fappend("0x%08x%08x\n", + (unsigned int)((tsf & 0xFFFFFFFF00000000ULL) >> 32), + (unsigned int)(tsf & 0xFFFFFFFFULL)); + +out: + bcm43xx_unlock_mmio(bcm, flags); + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + up(&big_buffer_sem); + return res; +} + +static ssize_t tsf_write_file(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct bcm43xx_private *bcm = file->private_data; + char *buf = really_big_buffer; + ssize_t buf_size; + ssize_t res; + unsigned long flags; + u64 tsf; + + buf_size = min(count, sizeof (really_big_buffer) - 1); + down(&big_buffer_sem); + if (copy_from_user(buf, user_buf, buf_size)) { + res = -EFAULT; + goto out_up; + } + bcm43xx_lock_mmio(bcm, flags); + if (!bcm->initialized) { + printk(KERN_INFO PFX "debugfs: Board not initialized.\n"); + res = -EFAULT; + goto out_unlock; + } + if (sscanf(buf, "%lli", &tsf) != 1) { + printk(KERN_INFO PFX "debugfs: invalid values for \"tsf\"\n"); + res = -EINVAL; + goto out_unlock; + } + bcm43xx_tsf_write(bcm, tsf); + res = buf_size; + +out_unlock: + bcm43xx_unlock_mmio(bcm, flags); +out_up: + up(&big_buffer_sem); + return res; +} + +static ssize_t txstat_read_file(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + const size_t len = REALLY_BIG_BUFFER_SIZE; + + struct bcm43xx_private *bcm = file->private_data; + char *buf = really_big_buffer; + size_t pos = 0; + ssize_t res; + unsigned long flags; + struct bcm43xx_dfsentry *e; + struct bcm43xx_xmitstatus *status; + int i, cnt, j = 0; + + down(&big_buffer_sem); + bcm43xx_lock(bcm, flags); + + fappend("Last %d logged xmitstatus blobs (Latest first):\n\n", + BCM43xx_NR_LOGGED_XMITSTATUS); + e = bcm->dfsentry; + if (e->xmitstatus_printing == 0) { + /* At the beginning, make a copy of all data to avoid + * concurrency, as this function is called multiple + * times for big logs. Without copying, the data might + * change between reads. This would result in total trash. + */ + e->xmitstatus_printing = 1; + e->saved_xmitstatus_ptr = e->xmitstatus_ptr; + e->saved_xmitstatus_cnt = e->xmitstatus_cnt; + memcpy(e->xmitstatus_print_buffer, e->xmitstatus_buffer, + BCM43xx_NR_LOGGED_XMITSTATUS * sizeof(*(e->xmitstatus_buffer))); + } + i = e->saved_xmitstatus_ptr - 1; + if (i < 0) + i = BCM43xx_NR_LOGGED_XMITSTATUS - 1; + cnt = e->saved_xmitstatus_cnt; + while (cnt) { + status = e->xmitstatus_print_buffer + i; + fappend("0x%02x: cookie: 0x%04x, flags: 0x%02x, " + "cnt1: 0x%02x, cnt2: 0x%02x, seq: 0x%04x, " + "unk: 0x%04x\n", j, + status->cookie, status->flags, + status->cnt1, status->cnt2, status->seq, + status->unknown); + j++; + cnt--; + i--; + if (i < 0) + i = BCM43xx_NR_LOGGED_XMITSTATUS - 1; + } + + bcm43xx_unlock(bcm, flags); + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + bcm43xx_lock(bcm, flags); + if (*ppos == pos) { + /* Done. Drop the copied data. */ + e->xmitstatus_printing = 0; + } + bcm43xx_unlock(bcm, flags); + up(&big_buffer_sem); + return res; +} + +#undef fappend + + +static struct file_operations devinfo_fops = { + .read = devinfo_read_file, + .write = write_file_dummy, + .open = open_file_generic, +}; + +static struct file_operations spromdump_fops = { + .read = spromdump_read_file, + .write = write_file_dummy, + .open = open_file_generic, +}; + +static struct file_operations drvinfo_fops = { + .read = drvinfo_read_file, + .write = write_file_dummy, + .open = open_file_generic, +}; + +static struct file_operations tsf_fops = { + .read = tsf_read_file, + .write = tsf_write_file, + .open = open_file_generic, +}; + +static struct file_operations txstat_fops = { + .read = txstat_read_file, + .write = write_file_dummy, + .open = open_file_generic, +}; + + +void bcm43xx_debugfs_add_device(struct bcm43xx_private *bcm) +{ + struct bcm43xx_dfsentry *e; + char devdir[IFNAMSIZ]; + + assert(bcm); + e = kzalloc(sizeof(*e), GFP_KERNEL); + if (!e) { + printk(KERN_ERR PFX "out of memory\n"); + return; + } + e->bcm = bcm; + e->xmitstatus_buffer = kzalloc(BCM43xx_NR_LOGGED_XMITSTATUS + * sizeof(*(e->xmitstatus_buffer)), + GFP_KERNEL); + if (!e->xmitstatus_buffer) { + printk(KERN_ERR PFX "out of memory\n"); + kfree(e); + return; + } + e->xmitstatus_print_buffer = kzalloc(BCM43xx_NR_LOGGED_XMITSTATUS + * sizeof(*(e->xmitstatus_buffer)), + GFP_KERNEL); + if (!e->xmitstatus_print_buffer) { + printk(KERN_ERR PFX "out of memory\n"); + kfree(e); + return; + } + + + bcm->dfsentry = e; + + strncpy(devdir, bcm->net_dev->name, ARRAY_SIZE(devdir)); + e->subdir = debugfs_create_dir(devdir, fs.root); + e->dentry_devinfo = debugfs_create_file("devinfo", 0444, e->subdir, + bcm, &devinfo_fops); + if (!e->dentry_devinfo) + printk(KERN_ERR PFX "debugfs: creating \"devinfo\" for \"%s\" failed!\n", devdir); + e->dentry_spromdump = debugfs_create_file("sprom_dump", 0444, e->subdir, + bcm, &spromdump_fops); + if (!e->dentry_spromdump) + printk(KERN_ERR PFX "debugfs: creating \"sprom_dump\" for \"%s\" failed!\n", devdir); + e->dentry_tsf = debugfs_create_file("tsf", 0666, e->subdir, + bcm, &tsf_fops); + if (!e->dentry_tsf) + printk(KERN_ERR PFX "debugfs: creating \"tsf\" for \"%s\" failed!\n", devdir); + e->dentry_txstat = debugfs_create_file("tx_status", 0444, e->subdir, + bcm, &txstat_fops); + if (!e->dentry_txstat) + printk(KERN_ERR PFX "debugfs: creating \"tx_status\" for \"%s\" failed!\n", devdir); +} + +void bcm43xx_debugfs_remove_device(struct bcm43xx_private *bcm) +{ + struct bcm43xx_dfsentry *e; + + if (!bcm) + return; + + e = bcm->dfsentry; + assert(e); + debugfs_remove(e->dentry_spromdump); + debugfs_remove(e->dentry_devinfo); + debugfs_remove(e->dentry_tsf); + debugfs_remove(e->dentry_txstat); + debugfs_remove(e->subdir); + kfree(e->xmitstatus_buffer); + kfree(e->xmitstatus_print_buffer); + kfree(e); +} + +void bcm43xx_debugfs_log_txstat(struct bcm43xx_private *bcm, + struct bcm43xx_xmitstatus *status) +{ + struct bcm43xx_dfsentry *e; + struct bcm43xx_xmitstatus *savedstatus; + + /* This is protected by bcm->_lock */ + e = bcm->dfsentry; + assert(e); + savedstatus = e->xmitstatus_buffer + e->xmitstatus_ptr; + memcpy(savedstatus, status, sizeof(*status)); + e->xmitstatus_ptr++; + if (e->xmitstatus_ptr >= BCM43xx_NR_LOGGED_XMITSTATUS) + e->xmitstatus_ptr = 0; + if (e->xmitstatus_cnt < BCM43xx_NR_LOGGED_XMITSTATUS) + e->xmitstatus_cnt++; +} + +void bcm43xx_debugfs_init(void) +{ + memset(&fs, 0, sizeof(fs)); + fs.root = debugfs_create_dir(KBUILD_MODNAME, NULL); + if (!fs.root) + printk(KERN_ERR PFX "debugfs: creating \"" KBUILD_MODNAME "\" subdir failed!\n"); + fs.dentry_driverinfo = debugfs_create_file("driver", 0444, fs.root, NULL, &drvinfo_fops); + if (!fs.dentry_driverinfo) + printk(KERN_ERR PFX "debugfs: creating \"" KBUILD_MODNAME "/driver\" failed!\n"); +} + +void bcm43xx_debugfs_exit(void) +{ + debugfs_remove(fs.dentry_driverinfo); + debugfs_remove(fs.root); +} + +void bcm43xx_printk_dump(const char *data, + size_t size, + const char *description) +{ + size_t i; + char c; + + printk(KERN_INFO PFX "Data dump (%s, %u bytes):", + description, size); + for (i = 0; i < size; i++) { + c = data[i]; + if (i % 8 == 0) + printk("\n" KERN_INFO PFX "0x%08x: 0x%02x, ", i, c & 0xff); + else + printk("0x%02x, ", c & 0xff); + } + printk("\n"); +} + +void bcm43xx_printk_bitdump(const unsigned char *data, + size_t bytes, int msb_to_lsb, + const char *description) +{ + size_t i; + int j; + const unsigned char *d; + + printk(KERN_INFO PFX "*** Bitdump (%s, %u bytes, %s) ***", + description, bytes, msb_to_lsb ? "MSB to LSB" : "LSB to MSB"); + for (i = 0; i < bytes; i++) { + d = data + i; + if (i % 8 == 0) + printk("\n" KERN_INFO PFX "0x%08x: ", i); + if (msb_to_lsb) { + for (j = 7; j >= 0; j--) { + if (*d & (1 << j)) + printk("1"); + else + printk("0"); + } + } else { + for (j = 0; j < 8; j++) { + if (*d & (1 << j)) + printk("1"); + else + printk("0"); + } + } + printk(" "); + } + printk("\n"); +} diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.h b/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.h new file mode 100644 index 000000000000..50ce267f794d --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.h @@ -0,0 +1,117 @@ +#ifndef BCM43xx_DEBUGFS_H_ +#define BCM43xx_DEBUGFS_H_ + +struct bcm43xx_private; +struct bcm43xx_xmitstatus; + +#ifdef CONFIG_BCM43XX_DEBUG + +#include +#include + +struct dentry; + +/* limited by the size of the "really_big_buffer" */ +#define BCM43xx_NR_LOGGED_XMITSTATUS 100 + +struct bcm43xx_dfsentry { + struct dentry *subdir; + struct dentry *dentry_devinfo; + struct dentry *dentry_spromdump; + struct dentry *dentry_tsf; + struct dentry *dentry_txstat; + + struct bcm43xx_private *bcm; + + /* saved xmitstatus. */ + struct bcm43xx_xmitstatus *xmitstatus_buffer; + int xmitstatus_ptr; + int xmitstatus_cnt; + /* We need a seperate buffer while printing to avoid + * concurrency issues. (New xmitstatus can arrive + * while we are printing). + */ + struct bcm43xx_xmitstatus *xmitstatus_print_buffer; + int saved_xmitstatus_ptr; + int saved_xmitstatus_cnt; + int xmitstatus_printing; +}; + +struct bcm43xx_debugfs { + struct dentry *root; + struct dentry *dentry_driverinfo; +}; + +void bcm43xx_debugfs_init(void); +void bcm43xx_debugfs_exit(void); +void bcm43xx_debugfs_add_device(struct bcm43xx_private *bcm); +void bcm43xx_debugfs_remove_device(struct bcm43xx_private *bcm); +void bcm43xx_debugfs_log_txstat(struct bcm43xx_private *bcm, + struct bcm43xx_xmitstatus *status); + +/* Debug helper: Dump binary data through printk. */ +void bcm43xx_printk_dump(const char *data, + size_t size, + const char *description); +/* Debug helper: Dump bitwise binary data through printk. */ +void bcm43xx_printk_bitdump(const unsigned char *data, + size_t bytes, int msb_to_lsb, + const char *description); +#define bcm43xx_printk_bitdumpt(pointer, msb_to_lsb, description) \ + do { \ + bcm43xx_printk_bitdump((const unsigned char *)(pointer), \ + sizeof(*(pointer)), \ + (msb_to_lsb), \ + (description)); \ + } while (0) + +#else /* CONFIG_BCM43XX_DEBUG*/ + +static inline +void bcm43xx_debugfs_init(void) { } +static inline +void bcm43xx_debugfs_exit(void) { } +static inline +void bcm43xx_debugfs_add_device(struct bcm43xx_private *bcm) { } +static inline +void bcm43xx_debugfs_remove_device(struct bcm43xx_private *bcm) { } +static inline +void bcm43xx_debugfs_log_txstat(struct bcm43xx_private *bcm, + struct bcm43xx_xmitstatus *status) { } + +static inline +void bcm43xx_printk_dump(const char *data, + size_t size, + const char *description) +{ +} +static inline +void bcm43xx_printk_bitdump(const unsigned char *data, + size_t bytes, int msb_to_lsb, + const char *description) +{ +} +#define bcm43xx_printk_bitdumpt(pointer, msb_to_lsb, description) do { /* nothing */ } while (0) + +#endif /* CONFIG_BCM43XX_DEBUG*/ + +/* Ugly helper macros to make incomplete code more verbose on runtime */ +#ifdef TODO +# undef TODO +#endif +#define TODO() \ + do { \ + printk(KERN_INFO PFX "TODO: Incomplete code in %s() at %s:%d\n", \ + __FUNCTION__, __FILE__, __LINE__); \ + } while (0) + +#ifdef FIXME +# undef FIXME +#endif +#define FIXME() \ + do { \ + printk(KERN_INFO PFX "FIXME: Possibly broken code in %s() at %s:%d\n", \ + __FUNCTION__, __FILE__, __LINE__); \ + } while (0) + +#endif /* BCM43xx_DEBUGFS_H_ */ diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_dma.c b/drivers/net/wireless/bcm43xx/bcm43xx_dma.c new file mode 100644 index 000000000000..c3681b8f09b4 --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_dma.c @@ -0,0 +1,968 @@ +/* + + Broadcom BCM43xx wireless driver + + DMA ringbuffer and descriptor allocation/management + + Copyright (c) 2005 Michael Buesch + + Some code in this file is derived from the b44.c driver + Copyright (C) 2002 David S. Miller + Copyright (C) Pekka Pietikainen + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "bcm43xx.h" +#include "bcm43xx_dma.h" +#include "bcm43xx_main.h" +#include "bcm43xx_debugfs.h" +#include "bcm43xx_power.h" +#include "bcm43xx_xmit.h" + +#include +#include +#include +#include + + +static inline int free_slots(struct bcm43xx_dmaring *ring) +{ + return (ring->nr_slots - ring->used_slots); +} + +static inline int next_slot(struct bcm43xx_dmaring *ring, int slot) +{ + assert(slot >= -1 && slot <= ring->nr_slots - 1); + if (slot == ring->nr_slots - 1) + return 0; + return slot + 1; +} + +static inline int prev_slot(struct bcm43xx_dmaring *ring, int slot) +{ + assert(slot >= 0 && slot <= ring->nr_slots - 1); + if (slot == 0) + return ring->nr_slots - 1; + return slot - 1; +} + +/* Request a slot for usage. */ +static inline +int request_slot(struct bcm43xx_dmaring *ring) +{ + int slot; + + assert(ring->tx); + assert(!ring->suspended); + assert(free_slots(ring) != 0); + + slot = next_slot(ring, ring->current_slot); + ring->current_slot = slot; + ring->used_slots++; + + /* Check the number of available slots and suspend TX, + * if we are running low on free slots. + */ + if (unlikely(free_slots(ring) < ring->suspend_mark)) { + netif_stop_queue(ring->bcm->net_dev); + ring->suspended = 1; + } +#ifdef CONFIG_BCM43XX_DEBUG + if (ring->used_slots > ring->max_used_slots) + ring->max_used_slots = ring->used_slots; +#endif /* CONFIG_BCM43XX_DEBUG*/ + + return slot; +} + +/* Return a slot to the free slots. */ +static inline +void return_slot(struct bcm43xx_dmaring *ring, int slot) +{ + assert(ring->tx); + + ring->used_slots--; + + /* Check if TX is suspended and check if we have + * enough free slots to resume it again. + */ + if (unlikely(ring->suspended)) { + if (free_slots(ring) >= ring->resume_mark) { + ring->suspended = 0; + netif_wake_queue(ring->bcm->net_dev); + } + } +} + +static inline +dma_addr_t map_descbuffer(struct bcm43xx_dmaring *ring, + unsigned char *buf, + size_t len, + int tx) +{ + dma_addr_t dmaaddr; + + if (tx) { + dmaaddr = dma_map_single(&ring->bcm->pci_dev->dev, + buf, len, + DMA_TO_DEVICE); + } else { + dmaaddr = dma_map_single(&ring->bcm->pci_dev->dev, + buf, len, + DMA_FROM_DEVICE); + } + + return dmaaddr; +} + +static inline +void unmap_descbuffer(struct bcm43xx_dmaring *ring, + dma_addr_t addr, + size_t len, + int tx) +{ + if (tx) { + dma_unmap_single(&ring->bcm->pci_dev->dev, + addr, len, + DMA_TO_DEVICE); + } else { + dma_unmap_single(&ring->bcm->pci_dev->dev, + addr, len, + DMA_FROM_DEVICE); + } +} + +static inline +void sync_descbuffer_for_cpu(struct bcm43xx_dmaring *ring, + dma_addr_t addr, + size_t len) +{ + assert(!ring->tx); + + dma_sync_single_for_cpu(&ring->bcm->pci_dev->dev, + addr, len, DMA_FROM_DEVICE); +} + +static inline +void sync_descbuffer_for_device(struct bcm43xx_dmaring *ring, + dma_addr_t addr, + size_t len) +{ + assert(!ring->tx); + + dma_sync_single_for_device(&ring->bcm->pci_dev->dev, + addr, len, DMA_FROM_DEVICE); +} + +/* Unmap and free a descriptor buffer. */ +static inline +void free_descriptor_buffer(struct bcm43xx_dmaring *ring, + struct bcm43xx_dmadesc *desc, + struct bcm43xx_dmadesc_meta *meta, + int irq_context) +{ + assert(meta->skb); + if (irq_context) + dev_kfree_skb_irq(meta->skb); + else + dev_kfree_skb(meta->skb); + meta->skb = NULL; +} + +static int alloc_ringmemory(struct bcm43xx_dmaring *ring) +{ + struct device *dev = &(ring->bcm->pci_dev->dev); + + ring->vbase = dma_alloc_coherent(dev, BCM43xx_DMA_RINGMEMSIZE, + &(ring->dmabase), GFP_KERNEL); + if (!ring->vbase) { + printk(KERN_ERR PFX "DMA ringmemory allocation failed\n"); + return -ENOMEM; + } + if (ring->dmabase + BCM43xx_DMA_RINGMEMSIZE > BCM43xx_DMA_BUSADDRMAX) { + printk(KERN_ERR PFX ">>>FATAL ERROR<<< DMA RINGMEMORY >1G " + "(0x%08x, len: %lu)\n", + ring->dmabase, BCM43xx_DMA_RINGMEMSIZE); + dma_free_coherent(dev, BCM43xx_DMA_RINGMEMSIZE, + ring->vbase, ring->dmabase); + return -ENOMEM; + } + assert(!(ring->dmabase & 0x000003FF)); + memset(ring->vbase, 0, BCM43xx_DMA_RINGMEMSIZE); + + return 0; +} + +static void free_ringmemory(struct bcm43xx_dmaring *ring) +{ + struct device *dev = &(ring->bcm->pci_dev->dev); + + dma_free_coherent(dev, BCM43xx_DMA_RINGMEMSIZE, + ring->vbase, ring->dmabase); +} + +/* Reset the RX DMA channel */ +int bcm43xx_dmacontroller_rx_reset(struct bcm43xx_private *bcm, + u16 mmio_base) +{ + int i; + u32 value; + + bcm43xx_write32(bcm, + mmio_base + BCM43xx_DMA_RX_CONTROL, + 0x00000000); + for (i = 0; i < 1000; i++) { + value = bcm43xx_read32(bcm, + mmio_base + BCM43xx_DMA_RX_STATUS); + value &= BCM43xx_DMA_RXSTAT_STAT_MASK; + if (value == BCM43xx_DMA_RXSTAT_STAT_DISABLED) { + i = -1; + break; + } + udelay(10); + } + if (i != -1) { + printk(KERN_ERR PFX "Error: Wait on DMA RX status timed out.\n"); + return -ENODEV; + } + + return 0; +} + +/* Reset the RX DMA channel */ +int bcm43xx_dmacontroller_tx_reset(struct bcm43xx_private *bcm, + u16 mmio_base) +{ + int i; + u32 value; + + for (i = 0; i < 1000; i++) { + value = bcm43xx_read32(bcm, + mmio_base + BCM43xx_DMA_TX_STATUS); + value &= BCM43xx_DMA_TXSTAT_STAT_MASK; + if (value == BCM43xx_DMA_TXSTAT_STAT_DISABLED || + value == BCM43xx_DMA_TXSTAT_STAT_IDLEWAIT || + value == BCM43xx_DMA_TXSTAT_STAT_STOPPED) + break; + udelay(10); + } + bcm43xx_write32(bcm, + mmio_base + BCM43xx_DMA_TX_CONTROL, + 0x00000000); + for (i = 0; i < 1000; i++) { + value = bcm43xx_read32(bcm, + mmio_base + BCM43xx_DMA_TX_STATUS); + value &= BCM43xx_DMA_TXSTAT_STAT_MASK; + if (value == BCM43xx_DMA_TXSTAT_STAT_DISABLED) { + i = -1; + break; + } + udelay(10); + } + if (i != -1) { + printk(KERN_ERR PFX "Error: Wait on DMA TX status timed out.\n"); + return -ENODEV; + } + /* ensure the reset is completed. */ + udelay(300); + + return 0; +} + +static int setup_rx_descbuffer(struct bcm43xx_dmaring *ring, + struct bcm43xx_dmadesc *desc, + struct bcm43xx_dmadesc_meta *meta, + gfp_t gfp_flags) +{ + struct bcm43xx_rxhdr *rxhdr; + dma_addr_t dmaaddr; + u32 desc_addr; + u32 desc_ctl; + const int slot = (int)(desc - ring->vbase); + struct sk_buff *skb; + + assert(slot >= 0 && slot < ring->nr_slots); + assert(!ring->tx); + + skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags); + if (unlikely(!skb)) + return -ENOMEM; + dmaaddr = map_descbuffer(ring, skb->data, ring->rx_buffersize, 0); + if (unlikely(dmaaddr + ring->rx_buffersize > BCM43xx_DMA_BUSADDRMAX)) { + unmap_descbuffer(ring, dmaaddr, ring->rx_buffersize, 0); + dev_kfree_skb_any(skb); + printk(KERN_ERR PFX ">>>FATAL ERROR<<< DMA RX SKB >1G " + "(0x%08x, len: %u)\n", + dmaaddr, ring->rx_buffersize); + return -ENOMEM; + } + meta->skb = skb; + meta->dmaaddr = dmaaddr; + skb->dev = ring->bcm->net_dev; + desc_addr = (u32)(dmaaddr + ring->memoffset); + desc_ctl = (BCM43xx_DMADTOR_BYTECNT_MASK & + (u32)(ring->rx_buffersize - ring->frameoffset)); + if (slot == ring->nr_slots - 1) + desc_ctl |= BCM43xx_DMADTOR_DTABLEEND; + set_desc_addr(desc, desc_addr); + set_desc_ctl(desc, desc_ctl); + + rxhdr = (struct bcm43xx_rxhdr *)(skb->data); + rxhdr->frame_length = 0; + rxhdr->flags1 = 0; + + return 0; +} + +/* Allocate the initial descbuffers. + * This is used for an RX ring only. + */ +static int alloc_initial_descbuffers(struct bcm43xx_dmaring *ring) +{ + int i, err = -ENOMEM; + struct bcm43xx_dmadesc *desc; + struct bcm43xx_dmadesc_meta *meta; + + for (i = 0; i < ring->nr_slots; i++) { + desc = ring->vbase + i; + meta = ring->meta + i; + + err = setup_rx_descbuffer(ring, desc, meta, GFP_KERNEL); + if (err) + goto err_unwind; + } + ring->used_slots = ring->nr_slots; + err = 0; +out: + return err; + +err_unwind: + for (i--; i >= 0; i--) { + desc = ring->vbase + i; + meta = ring->meta + i; + + unmap_descbuffer(ring, meta->dmaaddr, ring->rx_buffersize, 0); + dev_kfree_skb(meta->skb); + } + goto out; +} + +/* Do initial setup of the DMA controller. + * Reset the controller, write the ring busaddress + * and switch the "enable" bit on. + */ +static int dmacontroller_setup(struct bcm43xx_dmaring *ring) +{ + int err = 0; + u32 value; + + if (ring->tx) { + /* Set Transmit Control register to "transmit enable" */ + bcm43xx_dma_write(ring, BCM43xx_DMA_TX_CONTROL, + BCM43xx_DMA_TXCTRL_ENABLE); + /* Set Transmit Descriptor ring address. */ + bcm43xx_dma_write(ring, BCM43xx_DMA_TX_DESC_RING, + ring->dmabase + ring->memoffset); + } else { + err = alloc_initial_descbuffers(ring); + if (err) + goto out; + /* Set Receive Control "receive enable" and frame offset */ + value = (ring->frameoffset << BCM43xx_DMA_RXCTRL_FRAMEOFF_SHIFT); + value |= BCM43xx_DMA_RXCTRL_ENABLE; + bcm43xx_dma_write(ring, BCM43xx_DMA_RX_CONTROL, value); + /* Set Receive Descriptor ring address. */ + bcm43xx_dma_write(ring, BCM43xx_DMA_RX_DESC_RING, + ring->dmabase + ring->memoffset); + /* Init the descriptor pointer. */ + bcm43xx_dma_write(ring, BCM43xx_DMA_RX_DESC_INDEX, 200); + } + +out: + return err; +} + +/* Shutdown the DMA controller. */ +static void dmacontroller_cleanup(struct bcm43xx_dmaring *ring) +{ + if (ring->tx) { + bcm43xx_dmacontroller_tx_reset(ring->bcm, ring->mmio_base); + /* Zero out Transmit Descriptor ring address. */ + bcm43xx_dma_write(ring, BCM43xx_DMA_TX_DESC_RING, 0); + } else { + bcm43xx_dmacontroller_rx_reset(ring->bcm, ring->mmio_base); + /* Zero out Receive Descriptor ring address. */ + bcm43xx_dma_write(ring, BCM43xx_DMA_RX_DESC_RING, 0); + } +} + +static void free_all_descbuffers(struct bcm43xx_dmaring *ring) +{ + struct bcm43xx_dmadesc *desc; + struct bcm43xx_dmadesc_meta *meta; + int i; + + if (!ring->used_slots) + return; + for (i = 0; i < ring->nr_slots; i++) { + desc = ring->vbase + i; + meta = ring->meta + i; + + if (!meta->skb) { + assert(ring->tx); + continue; + } + if (ring->tx) { + unmap_descbuffer(ring, meta->dmaaddr, + meta->skb->len, 1); + } else { + unmap_descbuffer(ring, meta->dmaaddr, + ring->rx_buffersize, 0); + } + free_descriptor_buffer(ring, desc, meta, 0); + } +} + +/* Main initialization function. */ +static +struct bcm43xx_dmaring * bcm43xx_setup_dmaring(struct bcm43xx_private *bcm, + u16 dma_controller_base, + int nr_descriptor_slots, + int tx) +{ + struct bcm43xx_dmaring *ring; + int err; + + ring = kzalloc(sizeof(*ring), GFP_KERNEL); + if (!ring) + goto out; + + ring->meta = kzalloc(sizeof(*ring->meta) * nr_descriptor_slots, + GFP_KERNEL); + if (!ring->meta) + goto err_kfree_ring; + + ring->memoffset = BCM43xx_DMA_DMABUSADDROFFSET; +#ifdef CONFIG_BCM947XX + if (bcm->pci_dev->bus->number == 0) + ring->memoffset = 0; +#endif + + ring->bcm = bcm; + ring->nr_slots = nr_descriptor_slots; + ring->suspend_mark = ring->nr_slots * BCM43xx_TXSUSPEND_PERCENT / 100; + ring->resume_mark = ring->nr_slots * BCM43xx_TXRESUME_PERCENT / 100; + assert(ring->suspend_mark < ring->resume_mark); + ring->mmio_base = dma_controller_base; + if (tx) { + ring->tx = 1; + ring->current_slot = -1; + } else { + switch (dma_controller_base) { + case BCM43xx_MMIO_DMA1_BASE: + ring->rx_buffersize = BCM43xx_DMA1_RXBUFFERSIZE; + ring->frameoffset = BCM43xx_DMA1_RX_FRAMEOFFSET; + break; + case BCM43xx_MMIO_DMA4_BASE: + ring->rx_buffersize = BCM43xx_DMA4_RXBUFFERSIZE; + ring->frameoffset = BCM43xx_DMA4_RX_FRAMEOFFSET; + break; + default: + assert(0); + } + } + + err = alloc_ringmemory(ring); + if (err) + goto err_kfree_meta; + err = dmacontroller_setup(ring); + if (err) + goto err_free_ringmemory; + +out: + return ring; + +err_free_ringmemory: + free_ringmemory(ring); +err_kfree_meta: + kfree(ring->meta); +err_kfree_ring: + kfree(ring); + ring = NULL; + goto out; +} + +/* Main cleanup function. */ +static void bcm43xx_destroy_dmaring(struct bcm43xx_dmaring *ring) +{ + if (!ring) + return; + + dprintk(KERN_INFO PFX "DMA 0x%04x (%s) max used slots: %d/%d\n", + ring->mmio_base, + (ring->tx) ? "TX" : "RX", + ring->max_used_slots, ring->nr_slots); + /* Device IRQs are disabled prior entering this function, + * so no need to take care of concurrency with rx handler stuff. + */ + dmacontroller_cleanup(ring); + free_all_descbuffers(ring); + free_ringmemory(ring); + + kfree(ring->meta); + kfree(ring); +} + +void bcm43xx_dma_free(struct bcm43xx_private *bcm) +{ + struct bcm43xx_dma *dma; + + if (bcm43xx_using_pio(bcm)) + return; + dma = bcm43xx_current_dma(bcm); + + bcm43xx_destroy_dmaring(dma->rx_ring1); + dma->rx_ring1 = NULL; + bcm43xx_destroy_dmaring(dma->rx_ring0); + dma->rx_ring0 = NULL; + bcm43xx_destroy_dmaring(dma->tx_ring3); + dma->tx_ring3 = NULL; + bcm43xx_destroy_dmaring(dma->tx_ring2); + dma->tx_ring2 = NULL; + bcm43xx_destroy_dmaring(dma->tx_ring1); + dma->tx_ring1 = NULL; + bcm43xx_destroy_dmaring(dma->tx_ring0); + dma->tx_ring0 = NULL; +} + +int bcm43xx_dma_init(struct bcm43xx_private *bcm) +{ + struct bcm43xx_dma *dma = bcm43xx_current_dma(bcm); + struct bcm43xx_dmaring *ring; + int err = -ENOMEM; + + /* setup TX DMA channels. */ + ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA1_BASE, + BCM43xx_TXRING_SLOTS, 1); + if (!ring) + goto out; + dma->tx_ring0 = ring; + + ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA2_BASE, + BCM43xx_TXRING_SLOTS, 1); + if (!ring) + goto err_destroy_tx0; + dma->tx_ring1 = ring; + + ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA3_BASE, + BCM43xx_TXRING_SLOTS, 1); + if (!ring) + goto err_destroy_tx1; + dma->tx_ring2 = ring; + + ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA4_BASE, + BCM43xx_TXRING_SLOTS, 1); + if (!ring) + goto err_destroy_tx2; + dma->tx_ring3 = ring; + + /* setup RX DMA channels. */ + ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA1_BASE, + BCM43xx_RXRING_SLOTS, 0); + if (!ring) + goto err_destroy_tx3; + dma->rx_ring0 = ring; + + if (bcm->current_core->rev < 5) { + ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA4_BASE, + BCM43xx_RXRING_SLOTS, 0); + if (!ring) + goto err_destroy_rx0; + dma->rx_ring1 = ring; + } + + dprintk(KERN_INFO PFX "DMA initialized\n"); + err = 0; +out: + return err; + +err_destroy_rx0: + bcm43xx_destroy_dmaring(dma->rx_ring0); + dma->rx_ring0 = NULL; +err_destroy_tx3: + bcm43xx_destroy_dmaring(dma->tx_ring3); + dma->tx_ring3 = NULL; +err_destroy_tx2: + bcm43xx_destroy_dmaring(dma->tx_ring2); + dma->tx_ring2 = NULL; +err_destroy_tx1: + bcm43xx_destroy_dmaring(dma->tx_ring1); + dma->tx_ring1 = NULL; +err_destroy_tx0: + bcm43xx_destroy_dmaring(dma->tx_ring0); + dma->tx_ring0 = NULL; + goto out; +} + +/* Generate a cookie for the TX header. */ +static u16 generate_cookie(struct bcm43xx_dmaring *ring, + int slot) +{ + u16 cookie = 0x0000; + + /* Use the upper 4 bits of the cookie as + * DMA controller ID and store the slot number + * in the lower 12 bits + */ + switch (ring->mmio_base) { + default: + assert(0); + case BCM43xx_MMIO_DMA1_BASE: + break; + case BCM43xx_MMIO_DMA2_BASE: + cookie = 0x1000; + break; + case BCM43xx_MMIO_DMA3_BASE: + cookie = 0x2000; + break; + case BCM43xx_MMIO_DMA4_BASE: + cookie = 0x3000; + break; + } + assert(((u16)slot & 0xF000) == 0x0000); + cookie |= (u16)slot; + + return cookie; +} + +/* Inspect a cookie and find out to which controller/slot it belongs. */ +static +struct bcm43xx_dmaring * parse_cookie(struct bcm43xx_private *bcm, + u16 cookie, int *slot) +{ + struct bcm43xx_dma *dma = bcm43xx_current_dma(bcm); + struct bcm43xx_dmaring *ring = NULL; + + switch (cookie & 0xF000) { + case 0x0000: + ring = dma->tx_ring0; + break; + case 0x1000: + ring = dma->tx_ring1; + break; + case 0x2000: + ring = dma->tx_ring2; + break; + case 0x3000: + ring = dma->tx_ring3; + break; + default: + assert(0); + } + *slot = (cookie & 0x0FFF); + assert(*slot >= 0 && *slot < ring->nr_slots); + + return ring; +} + +static void dmacontroller_poke_tx(struct bcm43xx_dmaring *ring, + int slot) +{ + /* Everything is ready to start. Buffers are DMA mapped and + * associated with slots. + * "slot" is the last slot of the new frame we want to transmit. + * Close your seat belts now, please. + */ + wmb(); + slot = next_slot(ring, slot); + bcm43xx_dma_write(ring, BCM43xx_DMA_TX_DESC_INDEX, + (u32)(slot * sizeof(struct bcm43xx_dmadesc))); +} + +static int dma_tx_fragment(struct bcm43xx_dmaring *ring, + struct sk_buff *skb, + u8 cur_frag) +{ + int slot; + struct bcm43xx_dmadesc *desc; + struct bcm43xx_dmadesc_meta *meta; + u32 desc_ctl; + u32 desc_addr; + + assert(skb_shinfo(skb)->nr_frags == 0); + + slot = request_slot(ring); + desc = ring->vbase + slot; + meta = ring->meta + slot; + + /* Add a device specific TX header. */ + assert(skb_headroom(skb) >= sizeof(struct bcm43xx_txhdr)); + /* Reserve enough headroom for the device tx header. */ + __skb_push(skb, sizeof(struct bcm43xx_txhdr)); + /* Now calculate and add the tx header. + * The tx header includes the PLCP header. + */ + bcm43xx_generate_txhdr(ring->bcm, + (struct bcm43xx_txhdr *)skb->data, + skb->data + sizeof(struct bcm43xx_txhdr), + skb->len - sizeof(struct bcm43xx_txhdr), + (cur_frag == 0), + generate_cookie(ring, slot)); + + meta->skb = skb; + meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); + if (unlikely(meta->dmaaddr + skb->len > BCM43xx_DMA_BUSADDRMAX)) { + return_slot(ring, slot); + printk(KERN_ERR PFX ">>>FATAL ERROR<<< DMA TX SKB >1G " + "(0x%08x, len: %u)\n", + meta->dmaaddr, skb->len); + return -ENOMEM; + } + + desc_addr = (u32)(meta->dmaaddr + ring->memoffset); + desc_ctl = BCM43xx_DMADTOR_FRAMESTART | BCM43xx_DMADTOR_FRAMEEND; + desc_ctl |= BCM43xx_DMADTOR_COMPIRQ; + desc_ctl |= (BCM43xx_DMADTOR_BYTECNT_MASK & + (u32)(meta->skb->len - ring->frameoffset)); + if (slot == ring->nr_slots - 1) + desc_ctl |= BCM43xx_DMADTOR_DTABLEEND; + + set_desc_ctl(desc, desc_ctl); + set_desc_addr(desc, desc_addr); + /* Now transfer the whole frame. */ + dmacontroller_poke_tx(ring, slot); + + return 0; +} + +int bcm43xx_dma_tx(struct bcm43xx_private *bcm, + struct ieee80211_txb *txb) +{ + /* We just received a packet from the kernel network subsystem. + * Add headers and DMA map the memory. Poke + * the device to send the stuff. + * Note that this is called from atomic context. + */ + struct bcm43xx_dmaring *ring = bcm43xx_current_dma(bcm)->tx_ring1; + u8 i; + struct sk_buff *skb; + + assert(ring->tx); + if (unlikely(free_slots(ring) < txb->nr_frags)) { + /* The queue should be stopped, + * if we are low on free slots. + * If this ever triggers, we have to lower the suspend_mark. + */ + dprintkl(KERN_ERR PFX "Out of DMA descriptor slots!\n"); + return -ENOMEM; + } + + for (i = 0; i < txb->nr_frags; i++) { + skb = txb->fragments[i]; + /* Take skb from ieee80211_txb_free */ + txb->fragments[i] = NULL; + dma_tx_fragment(ring, skb, i); + //TODO: handle failure of dma_tx_fragment + } + ieee80211_txb_free(txb); + + return 0; +} + +void bcm43xx_dma_handle_xmitstatus(struct bcm43xx_private *bcm, + struct bcm43xx_xmitstatus *status) +{ + struct bcm43xx_dmaring *ring; + struct bcm43xx_dmadesc *desc; + struct bcm43xx_dmadesc_meta *meta; + int is_last_fragment; + int slot; + + ring = parse_cookie(bcm, status->cookie, &slot); + assert(ring); + assert(ring->tx); + assert(get_desc_ctl(ring->vbase + slot) & BCM43xx_DMADTOR_FRAMESTART); + while (1) { + assert(slot >= 0 && slot < ring->nr_slots); + desc = ring->vbase + slot; + meta = ring->meta + slot; + + is_last_fragment = !!(get_desc_ctl(desc) & BCM43xx_DMADTOR_FRAMEEND); + unmap_descbuffer(ring, meta->dmaaddr, meta->skb->len, 1); + free_descriptor_buffer(ring, desc, meta, 1); + /* Everything belonging to the slot is unmapped + * and freed, so we can return it. + */ + return_slot(ring, slot); + + if (is_last_fragment) + break; + slot = next_slot(ring, slot); + } + bcm->stats.last_tx = jiffies; +} + +static void dma_rx(struct bcm43xx_dmaring *ring, + int *slot) +{ + struct bcm43xx_dmadesc *desc; + struct bcm43xx_dmadesc_meta *meta; + struct bcm43xx_rxhdr *rxhdr; + struct sk_buff *skb; + u16 len; + int err; + dma_addr_t dmaaddr; + + desc = ring->vbase + *slot; + meta = ring->meta + *slot; + + sync_descbuffer_for_cpu(ring, meta->dmaaddr, ring->rx_buffersize); + skb = meta->skb; + + if (ring->mmio_base == BCM43xx_MMIO_DMA4_BASE) { + /* We received an xmit status. */ + struct bcm43xx_hwxmitstatus *hw = (struct bcm43xx_hwxmitstatus *)skb->data; + struct bcm43xx_xmitstatus stat; + + stat.cookie = le16_to_cpu(hw->cookie); + stat.flags = hw->flags; + stat.cnt1 = hw->cnt1; + stat.cnt2 = hw->cnt2; + stat.seq = le16_to_cpu(hw->seq); + stat.unknown = le16_to_cpu(hw->unknown); + + bcm43xx_debugfs_log_txstat(ring->bcm, &stat); + bcm43xx_dma_handle_xmitstatus(ring->bcm, &stat); + /* recycle the descriptor buffer. */ + sync_descbuffer_for_device(ring, meta->dmaaddr, ring->rx_buffersize); + + return; + } + rxhdr = (struct bcm43xx_rxhdr *)skb->data; + len = le16_to_cpu(rxhdr->frame_length); + if (len == 0) { + int i = 0; + + do { + udelay(2); + barrier(); + len = le16_to_cpu(rxhdr->frame_length); + } while (len == 0 && i++ < 5); + if (unlikely(len == 0)) { + /* recycle the descriptor buffer. */ + sync_descbuffer_for_device(ring, meta->dmaaddr, + ring->rx_buffersize); + goto drop; + } + } + if (unlikely(len > ring->rx_buffersize)) { + /* The data did not fit into one descriptor buffer + * and is split over multiple buffers. + * This should never happen, as we try to allocate buffers + * big enough. So simply ignore this packet. + */ + int cnt = 0; + s32 tmp = len; + + while (1) { + desc = ring->vbase + *slot; + meta = ring->meta + *slot; + /* recycle the descriptor buffer. */ + sync_descbuffer_for_device(ring, meta->dmaaddr, + ring->rx_buffersize); + *slot = next_slot(ring, *slot); + cnt++; + tmp -= ring->rx_buffersize; + if (tmp <= 0) + break; + } + printkl(KERN_ERR PFX "DMA RX buffer too small " + "(len: %u, buffer: %u, nr-dropped: %d)\n", + len, ring->rx_buffersize, cnt); + goto drop; + } + len -= IEEE80211_FCS_LEN; + + dmaaddr = meta->dmaaddr; + err = setup_rx_descbuffer(ring, desc, meta, GFP_ATOMIC); + if (unlikely(err)) { + dprintkl(KERN_ERR PFX "DMA RX: setup_rx_descbuffer() failed\n"); + sync_descbuffer_for_device(ring, dmaaddr, + ring->rx_buffersize); + goto drop; + } + + unmap_descbuffer(ring, dmaaddr, ring->rx_buffersize, 0); + skb_put(skb, len + ring->frameoffset); + skb_pull(skb, ring->frameoffset); + + err = bcm43xx_rx(ring->bcm, skb, rxhdr); + if (err) { + dev_kfree_skb_irq(skb); + goto drop; + } + +drop: + return; +} + +void bcm43xx_dma_rx(struct bcm43xx_dmaring *ring) +{ + u32 status; + u16 descptr; + int slot, current_slot; +#ifdef CONFIG_BCM43XX_DEBUG + int used_slots = 0; +#endif + + assert(!ring->tx); + status = bcm43xx_dma_read(ring, BCM43xx_DMA_RX_STATUS); + descptr = (status & BCM43xx_DMA_RXSTAT_DPTR_MASK); + current_slot = descptr / sizeof(struct bcm43xx_dmadesc); + assert(current_slot >= 0 && current_slot < ring->nr_slots); + + slot = ring->current_slot; + for ( ; slot != current_slot; slot = next_slot(ring, slot)) { + dma_rx(ring, &slot); +#ifdef CONFIG_BCM43XX_DEBUG + if (++used_slots > ring->max_used_slots) + ring->max_used_slots = used_slots; +#endif + } + bcm43xx_dma_write(ring, BCM43xx_DMA_RX_DESC_INDEX, + (u32)(slot * sizeof(struct bcm43xx_dmadesc))); + ring->current_slot = slot; +} + +void bcm43xx_dma_tx_suspend(struct bcm43xx_dmaring *ring) +{ + assert(ring->tx); + bcm43xx_power_saving_ctl_bits(ring->bcm, -1, 1); + bcm43xx_dma_write(ring, BCM43xx_DMA_TX_CONTROL, + bcm43xx_dma_read(ring, BCM43xx_DMA_TX_CONTROL) + | BCM43xx_DMA_TXCTRL_SUSPEND); +} + +void bcm43xx_dma_tx_resume(struct bcm43xx_dmaring *ring) +{ + assert(ring->tx); + bcm43xx_dma_write(ring, BCM43xx_DMA_TX_CONTROL, + bcm43xx_dma_read(ring, BCM43xx_DMA_TX_CONTROL) + & ~BCM43xx_DMA_TXCTRL_SUSPEND); + bcm43xx_power_saving_ctl_bits(ring->bcm, -1, -1); +} diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_dma.h b/drivers/net/wireless/bcm43xx/bcm43xx_dma.h new file mode 100644 index 000000000000..2d520e4b0276 --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_dma.h @@ -0,0 +1,218 @@ +#ifndef BCM43xx_DMA_H_ +#define BCM43xx_DMA_H_ + +#include +#include +#include +#include +#include + + +/* DMA-Interrupt reasons. */ +#define BCM43xx_DMAIRQ_FATALMASK ((1 << 10) | (1 << 11) | (1 << 12) \ + | (1 << 14) | (1 << 15)) +#define BCM43xx_DMAIRQ_NONFATALMASK (1 << 13) +#define BCM43xx_DMAIRQ_RX_DONE (1 << 16) + +/* DMA controller register offsets. (relative to BCM43xx_DMA#_BASE) */ +#define BCM43xx_DMA_TX_CONTROL 0x00 +#define BCM43xx_DMA_TX_DESC_RING 0x04 +#define BCM43xx_DMA_TX_DESC_INDEX 0x08 +#define BCM43xx_DMA_TX_STATUS 0x0c +#define BCM43xx_DMA_RX_CONTROL 0x10 +#define BCM43xx_DMA_RX_DESC_RING 0x14 +#define BCM43xx_DMA_RX_DESC_INDEX 0x18 +#define BCM43xx_DMA_RX_STATUS 0x1c + +/* DMA controller channel control word values. */ +#define BCM43xx_DMA_TXCTRL_ENABLE (1 << 0) +#define BCM43xx_DMA_TXCTRL_SUSPEND (1 << 1) +#define BCM43xx_DMA_TXCTRL_LOOPBACK (1 << 2) +#define BCM43xx_DMA_TXCTRL_FLUSH (1 << 4) +#define BCM43xx_DMA_RXCTRL_ENABLE (1 << 0) +#define BCM43xx_DMA_RXCTRL_FRAMEOFF_MASK 0x000000fe +#define BCM43xx_DMA_RXCTRL_FRAMEOFF_SHIFT 1 +#define BCM43xx_DMA_RXCTRL_PIO (1 << 8) +/* DMA controller channel status word values. */ +#define BCM43xx_DMA_TXSTAT_DPTR_MASK 0x00000fff +#define BCM43xx_DMA_TXSTAT_STAT_MASK 0x0000f000 +#define BCM43xx_DMA_TXSTAT_STAT_DISABLED 0x00000000 +#define BCM43xx_DMA_TXSTAT_STAT_ACTIVE 0x00001000 +#define BCM43xx_DMA_TXSTAT_STAT_IDLEWAIT 0x00002000 +#define BCM43xx_DMA_TXSTAT_STAT_STOPPED 0x00003000 +#define BCM43xx_DMA_TXSTAT_STAT_SUSP 0x00004000 +#define BCM43xx_DMA_TXSTAT_ERROR_MASK 0x000f0000 +#define BCM43xx_DMA_TXSTAT_FLUSHED (1 << 20) +#define BCM43xx_DMA_RXSTAT_DPTR_MASK 0x00000fff +#define BCM43xx_DMA_RXSTAT_STAT_MASK 0x0000f000 +#define BCM43xx_DMA_RXSTAT_STAT_DISABLED 0x00000000 +#define BCM43xx_DMA_RXSTAT_STAT_ACTIVE 0x00001000 +#define BCM43xx_DMA_RXSTAT_STAT_IDLEWAIT 0x00002000 +#define BCM43xx_DMA_RXSTAT_STAT_RESERVED 0x00003000 +#define BCM43xx_DMA_RXSTAT_STAT_ERRORS 0x00004000 +#define BCM43xx_DMA_RXSTAT_ERROR_MASK 0x000f0000 + +/* DMA descriptor control field values. */ +#define BCM43xx_DMADTOR_BYTECNT_MASK 0x00001fff +#define BCM43xx_DMADTOR_DTABLEEND (1 << 28) /* End of descriptor table */ +#define BCM43xx_DMADTOR_COMPIRQ (1 << 29) /* IRQ on completion request */ +#define BCM43xx_DMADTOR_FRAMEEND (1 << 30) +#define BCM43xx_DMADTOR_FRAMESTART (1 << 31) + +/* Misc DMA constants */ +#define BCM43xx_DMA_RINGMEMSIZE PAGE_SIZE +#define BCM43xx_DMA_BUSADDRMAX 0x3FFFFFFF +#define BCM43xx_DMA_DMABUSADDROFFSET (1 << 30) +#define BCM43xx_DMA1_RX_FRAMEOFFSET 30 +#define BCM43xx_DMA4_RX_FRAMEOFFSET 0 + +/* DMA engine tuning knobs */ +#define BCM43xx_TXRING_SLOTS 512 +#define BCM43xx_RXRING_SLOTS 64 +#define BCM43xx_DMA1_RXBUFFERSIZE (2304 + 100) +#define BCM43xx_DMA4_RXBUFFERSIZE 16 +/* Suspend the tx queue, if less than this percent slots are free. */ +#define BCM43xx_TXSUSPEND_PERCENT 20 +/* Resume the tx queue, if more than this percent slots are free. */ +#define BCM43xx_TXRESUME_PERCENT 50 + + + +#ifdef CONFIG_BCM43XX_DMA + + +struct sk_buff; +struct bcm43xx_private; +struct bcm43xx_xmitstatus; + + +struct bcm43xx_dmadesc { + __le32 _control; + __le32 _address; +} __attribute__((__packed__)); + +/* Macros to access the bcm43xx_dmadesc struct */ +#define get_desc_ctl(desc) le32_to_cpu((desc)->_control) +#define set_desc_ctl(desc, ctl) do { (desc)->_control = cpu_to_le32(ctl); } while (0) +#define get_desc_addr(desc) le32_to_cpu((desc)->_address) +#define set_desc_addr(desc, addr) do { (desc)->_address = cpu_to_le32(addr); } while (0) + +struct bcm43xx_dmadesc_meta { + /* The kernel DMA-able buffer. */ + struct sk_buff *skb; + /* DMA base bus-address of the descriptor buffer. */ + dma_addr_t dmaaddr; +}; + +struct bcm43xx_dmaring { + struct bcm43xx_private *bcm; + /* Kernel virtual base address of the ring memory. */ + struct bcm43xx_dmadesc *vbase; + /* DMA memory offset */ + dma_addr_t memoffset; + /* (Unadjusted) DMA base bus-address of the ring memory. */ + dma_addr_t dmabase; + /* Meta data about all descriptors. */ + struct bcm43xx_dmadesc_meta *meta; + /* Number of descriptor slots in the ring. */ + int nr_slots; + /* Number of used descriptor slots. */ + int used_slots; + /* Currently used slot in the ring. */ + int current_slot; + /* Marks to suspend/resume the queue. */ + int suspend_mark; + int resume_mark; + /* Frameoffset in octets. */ + u32 frameoffset; + /* Descriptor buffer size. */ + u16 rx_buffersize; + /* The MMIO base register of the DMA controller, this + * ring is posted to. + */ + u16 mmio_base; + u8 tx:1, /* TRUE, if this is a TX ring. */ + suspended:1; /* TRUE, if transfers are suspended on this ring. */ +#ifdef CONFIG_BCM43XX_DEBUG + /* Maximum number of used slots. */ + int max_used_slots; +#endif /* CONFIG_BCM43XX_DEBUG*/ +}; + + +static inline +u32 bcm43xx_dma_read(struct bcm43xx_dmaring *ring, + u16 offset) +{ + return bcm43xx_read32(ring->bcm, ring->mmio_base + offset); +} + +static inline +void bcm43xx_dma_write(struct bcm43xx_dmaring *ring, + u16 offset, u32 value) +{ + bcm43xx_write32(ring->bcm, ring->mmio_base + offset, value); +} + + +int bcm43xx_dma_init(struct bcm43xx_private *bcm); +void bcm43xx_dma_free(struct bcm43xx_private *bcm); + +int bcm43xx_dmacontroller_rx_reset(struct bcm43xx_private *bcm, + u16 dmacontroller_mmio_base); +int bcm43xx_dmacontroller_tx_reset(struct bcm43xx_private *bcm, + u16 dmacontroller_mmio_base); + +void bcm43xx_dma_tx_suspend(struct bcm43xx_dmaring *ring); +void bcm43xx_dma_tx_resume(struct bcm43xx_dmaring *ring); + +void bcm43xx_dma_handle_xmitstatus(struct bcm43xx_private *bcm, + struct bcm43xx_xmitstatus *status); + +int bcm43xx_dma_tx(struct bcm43xx_private *bcm, + struct ieee80211_txb *txb); +void bcm43xx_dma_rx(struct bcm43xx_dmaring *ring); + + +#else /* CONFIG_BCM43XX_DMA */ + + +static inline +int bcm43xx_dma_init(struct bcm43xx_private *bcm) +{ + return 0; +} +static inline +void bcm43xx_dma_free(struct bcm43xx_private *bcm) +{ +} +static inline +int bcm43xx_dmacontroller_rx_reset(struct bcm43xx_private *bcm, + u16 dmacontroller_mmio_base) +{ + return 0; +} +static inline +int bcm43xx_dmacontroller_tx_reset(struct bcm43xx_private *bcm, + u16 dmacontroller_mmio_base) +{ + return 0; +} +static inline +int bcm43xx_dma_tx(struct bcm43xx_private *bcm, + struct ieee80211_txb *txb) +{ + return 0; +} +static inline +void bcm43xx_dma_handle_xmitstatus(struct bcm43xx_private *bcm, + struct bcm43xx_xmitstatus *status) +{ +} +static inline +void bcm43xx_dma_rx(struct bcm43xx_dmaring *ring) +{ +} + +#endif /* CONFIG_BCM43XX_DMA */ +#endif /* BCM43xx_DMA_H_ */ diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.c b/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.c new file mode 100644 index 000000000000..b3ffcf501311 --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.c @@ -0,0 +1,50 @@ +/* + + Broadcom BCM43xx wireless driver + + ethtool support + + Copyright (c) 2006 Jason Lunz + + Some code in this file is derived from the 8139too.c driver + Copyright (C) 2002 Jeff Garzik + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "bcm43xx.h" +#include "bcm43xx_ethtool.h" + +#include +#include +#include +#include + + +static void bcm43xx_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(dev); + + strncpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); + strncpy(info->version, UTS_RELEASE, sizeof(info->version)); + strncpy(info->bus_info, pci_name(bcm->pci_dev), ETHTOOL_BUSINFO_LEN); +} + +struct ethtool_ops bcm43xx_ethtool_ops = { + .get_drvinfo = bcm43xx_get_drvinfo, + .get_link = ethtool_op_get_link, +}; diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.h b/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.h new file mode 100644 index 000000000000..813704991f62 --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.h @@ -0,0 +1,8 @@ +#ifndef BCM43xx_ETHTOOL_H_ +#define BCM43xx_ETHTOOL_H_ + +#include + +extern struct ethtool_ops bcm43xx_ethtool_ops; + +#endif /* BCM43xx_ETHTOOL_H_ */ diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_ilt.c b/drivers/net/wireless/bcm43xx/bcm43xx_ilt.c new file mode 100644 index 000000000000..ad8e569d1faf --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_ilt.c @@ -0,0 +1,337 @@ +/* + + Broadcom BCM43xx wireless driver + + Copyright (c) 2005 Martin Langer , + Stefano Brivio + Michael Buesch + Danny van Dyk + Andreas Jaggi + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "bcm43xx.h" +#include "bcm43xx_ilt.h" +#include "bcm43xx_phy.h" + + +/**** Initial Internal Lookup Tables ****/ + +const u32 bcm43xx_ilt_rotor[BCM43xx_ILT_ROTOR_SIZE] = { + 0xFEB93FFD, 0xFEC63FFD, /* 0 */ + 0xFED23FFD, 0xFEDF3FFD, + 0xFEEC3FFE, 0xFEF83FFE, + 0xFF053FFE, 0xFF113FFE, + 0xFF1E3FFE, 0xFF2A3FFF, /* 8 */ + 0xFF373FFF, 0xFF443FFF, + 0xFF503FFF, 0xFF5D3FFF, + 0xFF693FFF, 0xFF763FFF, + 0xFF824000, 0xFF8F4000, /* 16 */ + 0xFF9B4000, 0xFFA84000, + 0xFFB54000, 0xFFC14000, + 0xFFCE4000, 0xFFDA4000, + 0xFFE74000, 0xFFF34000, /* 24 */ + 0x00004000, 0x000D4000, + 0x00194000, 0x00264000, + 0x00324000, 0x003F4000, + 0x004B4000, 0x00584000, /* 32 */ + 0x00654000, 0x00714000, + 0x007E4000, 0x008A3FFF, + 0x00973FFF, 0x00A33FFF, + 0x00B03FFF, 0x00BC3FFF, /* 40 */ + 0x00C93FFF, 0x00D63FFF, + 0x00E23FFE, 0x00EF3FFE, + 0x00FB3FFE, 0x01083FFE, + 0x01143FFE, 0x01213FFD, /* 48 */ + 0x012E3FFD, 0x013A3FFD, + 0x01473FFD, +}; + +const u32 bcm43xx_ilt_retard[BCM43xx_ILT_RETARD_SIZE] = { + 0xDB93CB87, 0xD666CF64, /* 0 */ + 0xD1FDD358, 0xCDA6D826, + 0xCA38DD9F, 0xC729E2B4, + 0xC469E88E, 0xC26AEE2B, + 0xC0DEF46C, 0xC073FA62, /* 8 */ + 0xC01D00D5, 0xC0760743, + 0xC1560D1E, 0xC2E51369, + 0xC4ED18FF, 0xC7AC1ED7, + 0xCB2823B2, 0xCEFA28D9, /* 16 */ + 0xD2F62D3F, 0xD7BB3197, + 0xDCE53568, 0xE1FE3875, + 0xE7D13B35, 0xED663D35, + 0xF39B3EC4, 0xF98E3FA7, /* 24 */ + 0x00004000, 0x06723FA7, + 0x0C653EC4, 0x129A3D35, + 0x182F3B35, 0x1E023875, + 0x231B3568, 0x28453197, /* 32 */ + 0x2D0A2D3F, 0x310628D9, + 0x34D823B2, 0x38541ED7, + 0x3B1318FF, 0x3D1B1369, + 0x3EAA0D1E, 0x3F8A0743, /* 40 */ + 0x3FE300D5, 0x3F8DFA62, + 0x3F22F46C, 0x3D96EE2B, + 0x3B97E88E, 0x38D7E2B4, + 0x35C8DD9F, 0x325AD826, /* 48 */ + 0x2E03D358, 0x299ACF64, + 0x246DCB87, +}; + +const u16 bcm43xx_ilt_finefreqa[BCM43xx_ILT_FINEFREQA_SIZE] = { + 0x0082, 0x0082, 0x0102, 0x0182, /* 0 */ + 0x0202, 0x0282, 0x0302, 0x0382, + 0x0402, 0x0482, 0x0502, 0x0582, + 0x05E2, 0x0662, 0x06E2, 0x0762, + 0x07E2, 0x0842, 0x08C2, 0x0942, /* 16 */ + 0x09C2, 0x0A22, 0x0AA2, 0x0B02, + 0x0B82, 0x0BE2, 0x0C62, 0x0CC2, + 0x0D42, 0x0DA2, 0x0E02, 0x0E62, + 0x0EE2, 0x0F42, 0x0FA2, 0x1002, /* 32 */ + 0x1062, 0x10C2, 0x1122, 0x1182, + 0x11E2, 0x1242, 0x12A2, 0x12E2, + 0x1342, 0x13A2, 0x1402, 0x1442, + 0x14A2, 0x14E2, 0x1542, 0x1582, /* 48 */ + 0x15E2, 0x1622, 0x1662, 0x16C1, + 0x1701, 0x1741, 0x1781, 0x17E1, + 0x1821, 0x1861, 0x18A1, 0x18E1, + 0x1921, 0x1961, 0x19A1, 0x19E1, /* 64 */ + 0x1A21, 0x1A61, 0x1AA1, 0x1AC1, + 0x1B01, 0x1B41, 0x1B81, 0x1BA1, + 0x1BE1, 0x1C21, 0x1C41, 0x1C81, + 0x1CA1, 0x1CE1, 0x1D01, 0x1D41, /* 80 */ + 0x1D61, 0x1DA1, 0x1DC1, 0x1E01, + 0x1E21, 0x1E61, 0x1E81, 0x1EA1, + 0x1EE1, 0x1F01, 0x1F21, 0x1F41, + 0x1F81, 0x1FA1, 0x1FC1, 0x1FE1, /* 96 */ + 0x2001, 0x2041, 0x2061, 0x2081, + 0x20A1, 0x20C1, 0x20E1, 0x2101, + 0x2121, 0x2141, 0x2161, 0x2181, + 0x21A1, 0x21C1, 0x21E1, 0x2201, /* 112 */ + 0x2221, 0x2241, 0x2261, 0x2281, + 0x22A1, 0x22C1, 0x22C1, 0x22E1, + 0x2301, 0x2321, 0x2341, 0x2361, + 0x2361, 0x2381, 0x23A1, 0x23C1, /* 128 */ + 0x23E1, 0x23E1, 0x2401, 0x2421, + 0x2441, 0x2441, 0x2461, 0x2481, + 0x2481, 0x24A1, 0x24C1, 0x24C1, + 0x24E1, 0x2501, 0x2501, 0x2521, /* 144 */ + 0x2541, 0x2541, 0x2561, 0x2561, + 0x2581, 0x25A1, 0x25A1, 0x25C1, + 0x25C1, 0x25E1, 0x2601, 0x2601, + 0x2621, 0x2621, 0x2641, 0x2641, /* 160 */ + 0x2661, 0x2661, 0x2681, 0x2681, + 0x26A1, 0x26A1, 0x26C1, 0x26C1, + 0x26E1, 0x26E1, 0x2701, 0x2701, + 0x2721, 0x2721, 0x2740, 0x2740, /* 176 */ + 0x2760, 0x2760, 0x2780, 0x2780, + 0x2780, 0x27A0, 0x27A0, 0x27C0, + 0x27C0, 0x27E0, 0x27E0, 0x27E0, + 0x2800, 0x2800, 0x2820, 0x2820, /* 192 */ + 0x2820, 0x2840, 0x2840, 0x2840, + 0x2860, 0x2860, 0x2880, 0x2880, + 0x2880, 0x28A0, 0x28A0, 0x28A0, + 0x28C0, 0x28C0, 0x28C0, 0x28E0, /* 208 */ + 0x28E0, 0x28E0, 0x2900, 0x2900, + 0x2900, 0x2920, 0x2920, 0x2920, + 0x2940, 0x2940, 0x2940, 0x2960, + 0x2960, 0x2960, 0x2960, 0x2980, /* 224 */ + 0x2980, 0x2980, 0x29A0, 0x29A0, + 0x29A0, 0x29A0, 0x29C0, 0x29C0, + 0x29C0, 0x29E0, 0x29E0, 0x29E0, + 0x29E0, 0x2A00, 0x2A00, 0x2A00, /* 240 */ + 0x2A00, 0x2A20, 0x2A20, 0x2A20, + 0x2A20, 0x2A40, 0x2A40, 0x2A40, + 0x2A40, 0x2A60, 0x2A60, 0x2A60, +}; + +const u16 bcm43xx_ilt_finefreqg[BCM43xx_ILT_FINEFREQG_SIZE] = { + 0x0089, 0x02E9, 0x0409, 0x04E9, /* 0 */ + 0x05A9, 0x0669, 0x0709, 0x0789, + 0x0829, 0x08A9, 0x0929, 0x0989, + 0x0A09, 0x0A69, 0x0AC9, 0x0B29, + 0x0BA9, 0x0BE9, 0x0C49, 0x0CA9, /* 16 */ + 0x0D09, 0x0D69, 0x0DA9, 0x0E09, + 0x0E69, 0x0EA9, 0x0F09, 0x0F49, + 0x0FA9, 0x0FE9, 0x1029, 0x1089, + 0x10C9, 0x1109, 0x1169, 0x11A9, /* 32 */ + 0x11E9, 0x1229, 0x1289, 0x12C9, + 0x1309, 0x1349, 0x1389, 0x13C9, + 0x1409, 0x1449, 0x14A9, 0x14E9, + 0x1529, 0x1569, 0x15A9, 0x15E9, /* 48 */ + 0x1629, 0x1669, 0x16A9, 0x16E8, + 0x1728, 0x1768, 0x17A8, 0x17E8, + 0x1828, 0x1868, 0x18A8, 0x18E8, + 0x1928, 0x1968, 0x19A8, 0x19E8, /* 64 */ + 0x1A28, 0x1A68, 0x1AA8, 0x1AE8, + 0x1B28, 0x1B68, 0x1BA8, 0x1BE8, + 0x1C28, 0x1C68, 0x1CA8, 0x1CE8, + 0x1D28, 0x1D68, 0x1DC8, 0x1E08, /* 80 */ + 0x1E48, 0x1E88, 0x1EC8, 0x1F08, + 0x1F48, 0x1F88, 0x1FE8, 0x2028, + 0x2068, 0x20A8, 0x2108, 0x2148, + 0x2188, 0x21C8, 0x2228, 0x2268, /* 96 */ + 0x22C8, 0x2308, 0x2348, 0x23A8, + 0x23E8, 0x2448, 0x24A8, 0x24E8, + 0x2548, 0x25A8, 0x2608, 0x2668, + 0x26C8, 0x2728, 0x2787, 0x27E7, /* 112 */ + 0x2847, 0x28C7, 0x2947, 0x29A7, + 0x2A27, 0x2AC7, 0x2B47, 0x2BE7, + 0x2CA7, 0x2D67, 0x2E47, 0x2F67, + 0x3247, 0x3526, 0x3646, 0x3726, /* 128 */ + 0x3806, 0x38A6, 0x3946, 0x39E6, + 0x3A66, 0x3AE6, 0x3B66, 0x3BC6, + 0x3C45, 0x3CA5, 0x3D05, 0x3D85, + 0x3DE5, 0x3E45, 0x3EA5, 0x3EE5, /* 144 */ + 0x3F45, 0x3FA5, 0x4005, 0x4045, + 0x40A5, 0x40E5, 0x4145, 0x4185, + 0x41E5, 0x4225, 0x4265, 0x42C5, + 0x4305, 0x4345, 0x43A5, 0x43E5, /* 160 */ + 0x4424, 0x4464, 0x44C4, 0x4504, + 0x4544, 0x4584, 0x45C4, 0x4604, + 0x4644, 0x46A4, 0x46E4, 0x4724, + 0x4764, 0x47A4, 0x47E4, 0x4824, /* 176 */ + 0x4864, 0x48A4, 0x48E4, 0x4924, + 0x4964, 0x49A4, 0x49E4, 0x4A24, + 0x4A64, 0x4AA4, 0x4AE4, 0x4B23, + 0x4B63, 0x4BA3, 0x4BE3, 0x4C23, /* 192 */ + 0x4C63, 0x4CA3, 0x4CE3, 0x4D23, + 0x4D63, 0x4DA3, 0x4DE3, 0x4E23, + 0x4E63, 0x4EA3, 0x4EE3, 0x4F23, + 0x4F63, 0x4FC3, 0x5003, 0x5043, /* 208 */ + 0x5083, 0x50C3, 0x5103, 0x5143, + 0x5183, 0x51E2, 0x5222, 0x5262, + 0x52A2, 0x52E2, 0x5342, 0x5382, + 0x53C2, 0x5402, 0x5462, 0x54A2, /* 224 */ + 0x5502, 0x5542, 0x55A2, 0x55E2, + 0x5642, 0x5682, 0x56E2, 0x5722, + 0x5782, 0x57E1, 0x5841, 0x58A1, + 0x5901, 0x5961, 0x59C1, 0x5A21, /* 240 */ + 0x5AA1, 0x5B01, 0x5B81, 0x5BE1, + 0x5C61, 0x5D01, 0x5D80, 0x5E20, + 0x5EE0, 0x5FA0, 0x6080, 0x61C0, +}; + +const u16 bcm43xx_ilt_noisea2[BCM43xx_ILT_NOISEA2_SIZE] = { + 0x0001, 0x0001, 0x0001, 0xFFFE, + 0xFFFE, 0x3FFF, 0x1000, 0x0393, +}; + +const u16 bcm43xx_ilt_noisea3[BCM43xx_ILT_NOISEA3_SIZE] = { + 0x4C4C, 0x4C4C, 0x4C4C, 0x2D36, + 0x4C4C, 0x4C4C, 0x4C4C, 0x2D36, +}; + +const u16 bcm43xx_ilt_noiseg1[BCM43xx_ILT_NOISEG1_SIZE] = { + 0x013C, 0x01F5, 0x031A, 0x0631, + 0x0001, 0x0001, 0x0001, 0x0001, +}; + +const u16 bcm43xx_ilt_noiseg2[BCM43xx_ILT_NOISEG2_SIZE] = { + 0x5484, 0x3C40, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, +}; + +const u16 bcm43xx_ilt_noisescaleg1[BCM43xx_ILT_NOISESCALEG_SIZE] = { + 0x6C77, 0x5162, 0x3B40, 0x3335, /* 0 */ + 0x2F2D, 0x2A2A, 0x2527, 0x1F21, + 0x1A1D, 0x1719, 0x1616, 0x1414, + 0x1414, 0x1400, 0x1414, 0x1614, + 0x1716, 0x1A19, 0x1F1D, 0x2521, /* 16 */ + 0x2A27, 0x2F2A, 0x332D, 0x3B35, + 0x5140, 0x6C62, 0x0077, +}; + +const u16 bcm43xx_ilt_noisescaleg2[BCM43xx_ILT_NOISESCALEG_SIZE] = { + 0xD8DD, 0xCBD4, 0xBCC0, 0XB6B7, /* 0 */ + 0xB2B0, 0xADAD, 0xA7A9, 0x9FA1, + 0x969B, 0x9195, 0x8F8F, 0x8A8A, + 0x8A8A, 0x8A00, 0x8A8A, 0x8F8A, + 0x918F, 0x9695, 0x9F9B, 0xA7A1, /* 16 */ + 0xADA9, 0xB2AD, 0xB6B0, 0xBCB7, + 0xCBC0, 0xD8D4, 0x00DD, +}; + +const u16 bcm43xx_ilt_noisescaleg3[BCM43xx_ILT_NOISESCALEG_SIZE] = { + 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, /* 0 */ + 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, + 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, + 0xA4A4, 0xA400, 0xA4A4, 0xA4A4, + 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, /* 16 */ + 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, + 0xA4A4, 0xA4A4, 0x00A4, +}; + +const u16 bcm43xx_ilt_sigmasqr1[BCM43xx_ILT_SIGMASQR_SIZE] = { + 0x007A, 0x0075, 0x0071, 0x006C, /* 0 */ + 0x0067, 0x0063, 0x005E, 0x0059, + 0x0054, 0x0050, 0x004B, 0x0046, + 0x0042, 0x003D, 0x003D, 0x003D, + 0x003D, 0x003D, 0x003D, 0x003D, /* 16 */ + 0x003D, 0x003D, 0x003D, 0x003D, + 0x003D, 0x003D, 0x0000, 0x003D, + 0x003D, 0x003D, 0x003D, 0x003D, + 0x003D, 0x003D, 0x003D, 0x003D, /* 32 */ + 0x003D, 0x003D, 0x003D, 0x003D, + 0x0042, 0x0046, 0x004B, 0x0050, + 0x0054, 0x0059, 0x005E, 0x0063, + 0x0067, 0x006C, 0x0071, 0x0075, /* 48 */ + 0x007A, +}; + +const u16 bcm43xx_ilt_sigmasqr2[BCM43xx_ILT_SIGMASQR_SIZE] = { + 0x00DE, 0x00DC, 0x00DA, 0x00D8, /* 0 */ + 0x00D6, 0x00D4, 0x00D2, 0x00CF, + 0x00CD, 0x00CA, 0x00C7, 0x00C4, + 0x00C1, 0x00BE, 0x00BE, 0x00BE, + 0x00BE, 0x00BE, 0x00BE, 0x00BE, /* 16 */ + 0x00BE, 0x00BE, 0x00BE, 0x00BE, + 0x00BE, 0x00BE, 0x0000, 0x00BE, + 0x00BE, 0x00BE, 0x00BE, 0x00BE, + 0x00BE, 0x00BE, 0x00BE, 0x00BE, /* 32 */ + 0x00BE, 0x00BE, 0x00BE, 0x00BE, + 0x00C1, 0x00C4, 0x00C7, 0x00CA, + 0x00CD, 0x00CF, 0x00D2, 0x00D4, + 0x00D6, 0x00D8, 0x00DA, 0x00DC, /* 48 */ + 0x00DE, +}; + +/**** Helper functions to access the device Internal Lookup Tables ****/ + +void bcm43xx_ilt_write(struct bcm43xx_private *bcm, u16 offset, u16 val) +{ + if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A) { + bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_CTRL, offset); + mmiowb(); + bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, val); + } else { + bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_G_CTRL, offset); + mmiowb(); + bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_G_DATA1, val); + } +} + +u16 bcm43xx_ilt_read(struct bcm43xx_private *bcm, u16 offset) +{ + if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A) { + bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_CTRL, offset); + return bcm43xx_phy_read(bcm, BCM43xx_PHY_ILT_A_DATA1); + } else { + bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_G_CTRL, offset); + return bcm43xx_phy_read(bcm, BCM43xx_PHY_ILT_G_DATA1); + } +} diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_ilt.h b/drivers/net/wireless/bcm43xx/bcm43xx_ilt.h new file mode 100644 index 000000000000..464521abf73c --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_ilt.h @@ -0,0 +1,32 @@ +#ifndef BCM43xx_ILT_H_ +#define BCM43xx_ILT_H_ + +#define BCM43xx_ILT_ROTOR_SIZE 53 +extern const u32 bcm43xx_ilt_rotor[BCM43xx_ILT_ROTOR_SIZE]; +#define BCM43xx_ILT_RETARD_SIZE 53 +extern const u32 bcm43xx_ilt_retard[BCM43xx_ILT_RETARD_SIZE]; +#define BCM43xx_ILT_FINEFREQA_SIZE 256 +extern const u16 bcm43xx_ilt_finefreqa[BCM43xx_ILT_FINEFREQA_SIZE]; +#define BCM43xx_ILT_FINEFREQG_SIZE 256 +extern const u16 bcm43xx_ilt_finefreqg[BCM43xx_ILT_FINEFREQG_SIZE]; +#define BCM43xx_ILT_NOISEA2_SIZE 8 +extern const u16 bcm43xx_ilt_noisea2[BCM43xx_ILT_NOISEA2_SIZE]; +#define BCM43xx_ILT_NOISEA3_SIZE 8 +extern const u16 bcm43xx_ilt_noisea3[BCM43xx_ILT_NOISEA3_SIZE]; +#define BCM43xx_ILT_NOISEG1_SIZE 8 +extern const u16 bcm43xx_ilt_noiseg1[BCM43xx_ILT_NOISEG1_SIZE]; +#define BCM43xx_ILT_NOISEG2_SIZE 8 +extern const u16 bcm43xx_ilt_noiseg2[BCM43xx_ILT_NOISEG2_SIZE]; +#define BCM43xx_ILT_NOISESCALEG_SIZE 27 +extern const u16 bcm43xx_ilt_noisescaleg1[BCM43xx_ILT_NOISESCALEG_SIZE]; +extern const u16 bcm43xx_ilt_noisescaleg2[BCM43xx_ILT_NOISESCALEG_SIZE]; +extern const u16 bcm43xx_ilt_noisescaleg3[BCM43xx_ILT_NOISESCALEG_SIZE]; +#define BCM43xx_ILT_SIGMASQR_SIZE 53 +extern const u16 bcm43xx_ilt_sigmasqr1[BCM43xx_ILT_SIGMASQR_SIZE]; +extern const u16 bcm43xx_ilt_sigmasqr2[BCM43xx_ILT_SIGMASQR_SIZE]; + + +void bcm43xx_ilt_write(struct bcm43xx_private *bcm, u16 offset, u16 val); +u16 bcm43xx_ilt_read(struct bcm43xx_private *bcm, u16 offset); + +#endif /* BCM43xx_ILT_H_ */ diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_leds.c b/drivers/net/wireless/bcm43xx/bcm43xx_leds.c new file mode 100644 index 000000000000..4b2c02c0b31e --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_leds.c @@ -0,0 +1,293 @@ +/* + + Broadcom BCM43xx wireless driver + + Copyright (c) 2005 Martin Langer , + Stefano Brivio + Michael Buesch + Danny van Dyk + Andreas Jaggi + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "bcm43xx_leds.h" +#include "bcm43xx.h" + +#include + + +static void bcm43xx_led_changestate(struct bcm43xx_led *led) +{ + struct bcm43xx_private *bcm = led->bcm; + const int index = bcm43xx_led_index(led); + const u16 mask = (1 << index); + u16 ledctl; + + assert(index >= 0 && index < BCM43xx_NR_LEDS); + assert(led->blink_interval); + ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL); + ledctl = (ledctl & mask) ? (ledctl & ~mask) : (ledctl | mask); + bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl); +} + +static void bcm43xx_led_blink(unsigned long d) +{ + struct bcm43xx_led *led = (struct bcm43xx_led *)d; + struct bcm43xx_private *bcm = led->bcm; + unsigned long flags; + + bcm43xx_lock_mmio(bcm, flags); + if (led->blink_interval) { + bcm43xx_led_changestate(led); + mod_timer(&led->blink_timer, jiffies + led->blink_interval); + } + bcm43xx_unlock_mmio(bcm, flags); +} + +static void bcm43xx_led_blink_start(struct bcm43xx_led *led, + unsigned long interval) +{ + if (led->blink_interval) + return; + led->blink_interval = interval; + bcm43xx_led_changestate(led); + led->blink_timer.expires = jiffies + interval; + add_timer(&led->blink_timer); +} + +static void bcm43xx_led_blink_stop(struct bcm43xx_led *led, int sync) +{ + struct bcm43xx_private *bcm = led->bcm; + const int index = bcm43xx_led_index(led); + u16 ledctl; + + if (!led->blink_interval) + return; + if (unlikely(sync)) + del_timer_sync(&led->blink_timer); + else + del_timer(&led->blink_timer); + led->blink_interval = 0; + + /* Make sure the LED is turned off. */ + assert(index >= 0 && index < BCM43xx_NR_LEDS); + ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL); + if (led->activelow) + ledctl |= (1 << index); + else + ledctl &= ~(1 << index); + bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl); +} + +static void bcm43xx_led_init_hardcoded(struct bcm43xx_private *bcm, + struct bcm43xx_led *led, + int led_index) +{ + /* This function is called, if the behaviour (and activelow) + * information for a LED is missing in the SPROM. + * We hardcode the behaviour values for various devices here. + * Note that the BCM43xx_LED_TEST_XXX behaviour values can + * be used to figure out which led is mapped to which index. + */ + + switch (led_index) { + case 0: + led->behaviour = BCM43xx_LED_ACTIVITY; + if (bcm->board_vendor == PCI_VENDOR_ID_COMPAQ) + led->behaviour = BCM43xx_LED_RADIO_ALL; + break; + case 1: + led->behaviour = BCM43xx_LED_RADIO_B; + if (bcm->board_vendor == PCI_VENDOR_ID_ASUSTEK) + led->behaviour = BCM43xx_LED_ASSOC; + break; + case 2: + led->behaviour = BCM43xx_LED_RADIO_A; + break; + case 3: + led->behaviour = BCM43xx_LED_OFF; + break; + default: + assert(0); + } +} + +int bcm43xx_leds_init(struct bcm43xx_private *bcm) +{ + struct bcm43xx_led *led; + u8 sprom[4]; + int i; + + sprom[0] = bcm->sprom.wl0gpio0; + sprom[1] = bcm->sprom.wl0gpio1; + sprom[2] = bcm->sprom.wl0gpio2; + sprom[3] = bcm->sprom.wl0gpio3; + + for (i = 0; i < BCM43xx_NR_LEDS; i++) { + led = &(bcm->leds[i]); + led->bcm = bcm; + setup_timer(&led->blink_timer, + bcm43xx_led_blink, + (unsigned long)led); + + if (sprom[i] == 0xFF) { + bcm43xx_led_init_hardcoded(bcm, led, i); + } else { + led->behaviour = sprom[i] & BCM43xx_LED_BEHAVIOUR; + led->activelow = !!(sprom[i] & BCM43xx_LED_ACTIVELOW); + } + } + + return 0; +} + +void bcm43xx_leds_exit(struct bcm43xx_private *bcm) +{ + struct bcm43xx_led *led; + int i; + + for (i = 0; i < BCM43xx_NR_LEDS; i++) { + led = &(bcm->leds[i]); + bcm43xx_led_blink_stop(led, 1); + } + bcm43xx_leds_switch_all(bcm, 0); +} + +void bcm43xx_leds_update(struct bcm43xx_private *bcm, int activity) +{ + struct bcm43xx_led *led; + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + const int transferring = (jiffies - bcm->stats.last_tx) < BCM43xx_LED_XFER_THRES; + int i, turn_on; + unsigned long interval = 0; + u16 ledctl; + + ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL); + for (i = 0; i < BCM43xx_NR_LEDS; i++) { + led = &(bcm->leds[i]); + + turn_on = 0; + switch (led->behaviour) { + case BCM43xx_LED_INACTIVE: + continue; + case BCM43xx_LED_OFF: + break; + case BCM43xx_LED_ON: + turn_on = 1; + break; + case BCM43xx_LED_ACTIVITY: + turn_on = activity; + break; + case BCM43xx_LED_RADIO_ALL: + turn_on = radio->enabled; + break; + case BCM43xx_LED_RADIO_A: + turn_on = (radio->enabled && phy->type == BCM43xx_PHYTYPE_A); + break; + case BCM43xx_LED_RADIO_B: + turn_on = (radio->enabled && + (phy->type == BCM43xx_PHYTYPE_B || + phy->type == BCM43xx_PHYTYPE_G)); + break; + case BCM43xx_LED_MODE_BG: + if (phy->type == BCM43xx_PHYTYPE_G && + 1/*FIXME: using G rates.*/) + turn_on = 1; + break; + case BCM43xx_LED_TRANSFER: + if (transferring) + bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_MEDIUM); + else + bcm43xx_led_blink_stop(led, 0); + continue; + case BCM43xx_LED_APTRANSFER: + if (bcm->ieee->iw_mode == IW_MODE_MASTER) { + if (transferring) { + interval = BCM43xx_LEDBLINK_FAST; + turn_on = 1; + } + } else { + turn_on = 1; + if (0/*TODO: not assoc*/) + interval = BCM43xx_LEDBLINK_SLOW; + else if (transferring) + interval = BCM43xx_LEDBLINK_FAST; + else + turn_on = 0; + } + if (turn_on) + bcm43xx_led_blink_start(led, interval); + else + bcm43xx_led_blink_stop(led, 0); + continue; + case BCM43xx_LED_WEIRD: + //TODO + break; + case BCM43xx_LED_ASSOC: + if (bcm->softmac->associated) + turn_on = 1; + break; +#ifdef CONFIG_BCM43XX_DEBUG + case BCM43xx_LED_TEST_BLINKSLOW: + bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_SLOW); + continue; + case BCM43xx_LED_TEST_BLINKMEDIUM: + bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_MEDIUM); + continue; + case BCM43xx_LED_TEST_BLINKFAST: + bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_FAST); + continue; +#endif /* CONFIG_BCM43XX_DEBUG */ + default: + assert(0); + }; + + if (led->activelow) + turn_on = !turn_on; + if (turn_on) + ledctl |= (1 << i); + else + ledctl &= ~(1 << i); + } + bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl); +} + +void bcm43xx_leds_switch_all(struct bcm43xx_private *bcm, int on) +{ + struct bcm43xx_led *led; + u16 ledctl; + int i; + int bit_on; + + ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL); + for (i = 0; i < BCM43xx_NR_LEDS; i++) { + led = &(bcm->leds[i]); + if (led->behaviour == BCM43xx_LED_INACTIVE) + continue; + if (on) + bit_on = led->activelow ? 0 : 1; + else + bit_on = led->activelow ? 1 : 0; + if (bit_on) + ledctl |= (1 << i); + else + ledctl &= ~(1 << i); + } + bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl); +} diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_leds.h b/drivers/net/wireless/bcm43xx/bcm43xx_leds.h new file mode 100644 index 000000000000..d3716cf3aebc --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_leds.h @@ -0,0 +1,56 @@ +#ifndef BCM43xx_LEDS_H_ +#define BCM43xx_LEDS_H_ + +#include +#include + + +struct bcm43xx_led { + u8 behaviour:7; + u8 activelow:1; + + struct bcm43xx_private *bcm; + struct timer_list blink_timer; + unsigned long blink_interval; +}; +#define bcm43xx_led_index(led) ((int)((led) - (led)->bcm->leds)) + +/* Delay between state changes when blinking in jiffies */ +#define BCM43xx_LEDBLINK_SLOW (HZ / 1) +#define BCM43xx_LEDBLINK_MEDIUM (HZ / 4) +#define BCM43xx_LEDBLINK_FAST (HZ / 8) + +#define BCM43xx_LED_XFER_THRES (HZ / 100) + +#define BCM43xx_LED_BEHAVIOUR 0x7F +#define BCM43xx_LED_ACTIVELOW 0x80 +enum { /* LED behaviour values */ + BCM43xx_LED_OFF, + BCM43xx_LED_ON, + BCM43xx_LED_ACTIVITY, + BCM43xx_LED_RADIO_ALL, + BCM43xx_LED_RADIO_A, + BCM43xx_LED_RADIO_B, + BCM43xx_LED_MODE_BG, + BCM43xx_LED_TRANSFER, + BCM43xx_LED_APTRANSFER, + BCM43xx_LED_WEIRD,//FIXME + BCM43xx_LED_ASSOC, + BCM43xx_LED_INACTIVE, + + /* Behaviour values for testing. + * With these values it is easier to figure out + * the real behaviour of leds, in case the SPROM + * is missing information. + */ + BCM43xx_LED_TEST_BLINKSLOW, + BCM43xx_LED_TEST_BLINKMEDIUM, + BCM43xx_LED_TEST_BLINKFAST, +}; + +int bcm43xx_leds_init(struct bcm43xx_private *bcm); +void bcm43xx_leds_exit(struct bcm43xx_private *bcm); +void bcm43xx_leds_update(struct bcm43xx_private *bcm, int activity); +void bcm43xx_leds_switch_all(struct bcm43xx_private *bcm, int on); + +#endif /* BCM43xx_LEDS_H_ */ diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c new file mode 100644 index 000000000000..c37371fc9e01 --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c @@ -0,0 +1,3973 @@ +/* + + Broadcom BCM43xx wireless driver + + Copyright (c) 2005 Martin Langer , + Stefano Brivio + Michael Buesch + Danny van Dyk + Andreas Jaggi + + Some parts of the code in this file are derived from the ipw2200 + driver Copyright(c) 2003 - 2004 Intel Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bcm43xx.h" +#include "bcm43xx_main.h" +#include "bcm43xx_debugfs.h" +#include "bcm43xx_radio.h" +#include "bcm43xx_phy.h" +#include "bcm43xx_dma.h" +#include "bcm43xx_pio.h" +#include "bcm43xx_power.h" +#include "bcm43xx_wx.h" +#include "bcm43xx_ethtool.h" +#include "bcm43xx_xmit.h" + + +MODULE_DESCRIPTION("Broadcom BCM43xx wireless driver"); +MODULE_AUTHOR("Martin Langer"); +MODULE_AUTHOR("Stefano Brivio"); +MODULE_AUTHOR("Michael Buesch"); +MODULE_LICENSE("GPL"); + +#ifdef CONFIG_BCM947XX +extern char *nvram_get(char *name); +#endif + +#if defined(CONFIG_BCM43XX_DMA) && defined(CONFIG_BCM43XX_PIO) +static int modparam_pio; +module_param_named(pio, modparam_pio, int, 0444); +MODULE_PARM_DESC(pio, "enable(1) / disable(0) PIO mode"); +#elif defined(CONFIG_BCM43XX_DMA) +# define modparam_pio 0 +#elif defined(CONFIG_BCM43XX_PIO) +# define modparam_pio 1 +#endif + +static int modparam_bad_frames_preempt; +module_param_named(bad_frames_preempt, modparam_bad_frames_preempt, int, 0444); +MODULE_PARM_DESC(bad_frames_preempt, "enable(1) / disable(0) Bad Frames Preemption"); + +static int modparam_short_retry = BCM43xx_DEFAULT_SHORT_RETRY_LIMIT; +module_param_named(short_retry, modparam_short_retry, int, 0444); +MODULE_PARM_DESC(short_retry, "Short-Retry-Limit (0 - 15)"); + +static int modparam_long_retry = BCM43xx_DEFAULT_LONG_RETRY_LIMIT; +module_param_named(long_retry, modparam_long_retry, int, 0444); +MODULE_PARM_DESC(long_retry, "Long-Retry-Limit (0 - 15)"); + +static int modparam_locale = -1; +module_param_named(locale, modparam_locale, int, 0444); +MODULE_PARM_DESC(country, "Select LocaleCode 0-11 (For travelers)"); + +static int modparam_noleds; +module_param_named(noleds, modparam_noleds, int, 0444); +MODULE_PARM_DESC(noleds, "Turn off all LED activity"); + +#ifdef CONFIG_BCM43XX_DEBUG +static char modparam_fwpostfix[64]; +module_param_string(fwpostfix, modparam_fwpostfix, 64, 0444); +MODULE_PARM_DESC(fwpostfix, "Postfix for .fw files. Useful for debugging."); +#else +# define modparam_fwpostfix "" +#endif /* CONFIG_BCM43XX_DEBUG*/ + + +/* If you want to debug with just a single device, enable this, + * where the string is the pci device ID (as given by the kernel's + * pci_name function) of the device to be used. + */ +//#define DEBUG_SINGLE_DEVICE_ONLY "0001:11:00.0" + +/* If you want to enable printing of each MMIO access, enable this. */ +//#define DEBUG_ENABLE_MMIO_PRINT + +/* If you want to enable printing of MMIO access within + * ucode/pcm upload, initvals write, enable this. + */ +//#define DEBUG_ENABLE_UCODE_MMIO_PRINT + +/* If you want to enable printing of PCI Config Space access, enable this */ +//#define DEBUG_ENABLE_PCILOG + + +/* Detailed list maintained at: + * http://openfacts.berlios.de/index-en.phtml?title=Bcm43xxDevices + */ + static struct pci_device_id bcm43xx_pci_tbl[] = { + /* Broadcom 4303 802.11b */ + { PCI_VENDOR_ID_BROADCOM, 0x4301, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + /* Broadcom 4307 802.11b */ + { PCI_VENDOR_ID_BROADCOM, 0x4307, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + /* Broadcom 4318 802.11b/g */ + { PCI_VENDOR_ID_BROADCOM, 0x4318, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + /* Broadcom 4306 802.11b/g */ + { PCI_VENDOR_ID_BROADCOM, 0x4320, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + /* Broadcom 4306 802.11a */ +// { PCI_VENDOR_ID_BROADCOM, 0x4321, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + /* Broadcom 4309 802.11a/b/g */ + { PCI_VENDOR_ID_BROADCOM, 0x4324, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + /* Broadcom 43XG 802.11b/g */ + { PCI_VENDOR_ID_BROADCOM, 0x4325, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, +#ifdef CONFIG_BCM947XX + /* SB bus on BCM947xx */ + { PCI_VENDOR_ID_BROADCOM, 0x0800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, +#endif + { 0 }, +}; +MODULE_DEVICE_TABLE(pci, bcm43xx_pci_tbl); + +static void bcm43xx_ram_write(struct bcm43xx_private *bcm, u16 offset, u32 val) +{ + u32 status; + + status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); + if (!(status & BCM43xx_SBF_XFER_REG_BYTESWAP)) + val = swab32(val); + + bcm43xx_write32(bcm, BCM43xx_MMIO_RAM_CONTROL, offset); + mmiowb(); + bcm43xx_write32(bcm, BCM43xx_MMIO_RAM_DATA, val); +} + +static inline +void bcm43xx_shm_control_word(struct bcm43xx_private *bcm, + u16 routing, u16 offset) +{ + u32 control; + + /* "offset" is the WORD offset. */ + + control = routing; + control <<= 16; + control |= offset; + bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_CONTROL, control); +} + +u32 bcm43xx_shm_read32(struct bcm43xx_private *bcm, + u16 routing, u16 offset) +{ + u32 ret; + + if (routing == BCM43xx_SHM_SHARED) { + if (offset & 0x0003) { + /* Unaligned access */ + bcm43xx_shm_control_word(bcm, routing, offset >> 2); + ret = bcm43xx_read16(bcm, BCM43xx_MMIO_SHM_DATA_UNALIGNED); + ret <<= 16; + bcm43xx_shm_control_word(bcm, routing, (offset >> 2) + 1); + ret |= bcm43xx_read16(bcm, BCM43xx_MMIO_SHM_DATA); + + return ret; + } + offset >>= 2; + } + bcm43xx_shm_control_word(bcm, routing, offset); + ret = bcm43xx_read32(bcm, BCM43xx_MMIO_SHM_DATA); + + return ret; +} + +u16 bcm43xx_shm_read16(struct bcm43xx_private *bcm, + u16 routing, u16 offset) +{ + u16 ret; + + if (routing == BCM43xx_SHM_SHARED) { + if (offset & 0x0003) { + /* Unaligned access */ + bcm43xx_shm_control_word(bcm, routing, offset >> 2); + ret = bcm43xx_read16(bcm, BCM43xx_MMIO_SHM_DATA_UNALIGNED); + + return ret; + } + offset >>= 2; + } + bcm43xx_shm_control_word(bcm, routing, offset); + ret = bcm43xx_read16(bcm, BCM43xx_MMIO_SHM_DATA); + + return ret; +} + +void bcm43xx_shm_write32(struct bcm43xx_private *bcm, + u16 routing, u16 offset, + u32 value) +{ + if (routing == BCM43xx_SHM_SHARED) { + if (offset & 0x0003) { + /* Unaligned access */ + bcm43xx_shm_control_word(bcm, routing, offset >> 2); + mmiowb(); + bcm43xx_write16(bcm, BCM43xx_MMIO_SHM_DATA_UNALIGNED, + (value >> 16) & 0xffff); + mmiowb(); + bcm43xx_shm_control_word(bcm, routing, (offset >> 2) + 1); + mmiowb(); + bcm43xx_write16(bcm, BCM43xx_MMIO_SHM_DATA, + value & 0xffff); + return; + } + offset >>= 2; + } + bcm43xx_shm_control_word(bcm, routing, offset); + mmiowb(); + bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_DATA, value); +} + +void bcm43xx_shm_write16(struct bcm43xx_private *bcm, + u16 routing, u16 offset, + u16 value) +{ + if (routing == BCM43xx_SHM_SHARED) { + if (offset & 0x0003) { + /* Unaligned access */ + bcm43xx_shm_control_word(bcm, routing, offset >> 2); + mmiowb(); + bcm43xx_write16(bcm, BCM43xx_MMIO_SHM_DATA_UNALIGNED, + value); + return; + } + offset >>= 2; + } + bcm43xx_shm_control_word(bcm, routing, offset); + mmiowb(); + bcm43xx_write16(bcm, BCM43xx_MMIO_SHM_DATA, value); +} + +void bcm43xx_tsf_read(struct bcm43xx_private *bcm, u64 *tsf) +{ + /* We need to be careful. As we read the TSF from multiple + * registers, we should take care of register overflows. + * In theory, the whole tsf read process should be atomic. + * We try to be atomic here, by restaring the read process, + * if any of the high registers changed (overflew). + */ + if (bcm->current_core->rev >= 3) { + u32 low, high, high2; + + do { + high = bcm43xx_read32(bcm, BCM43xx_MMIO_REV3PLUS_TSF_HIGH); + low = bcm43xx_read32(bcm, BCM43xx_MMIO_REV3PLUS_TSF_LOW); + high2 = bcm43xx_read32(bcm, BCM43xx_MMIO_REV3PLUS_TSF_HIGH); + } while (unlikely(high != high2)); + + *tsf = high; + *tsf <<= 32; + *tsf |= low; + } else { + u64 tmp; + u16 v0, v1, v2, v3; + u16 test1, test2, test3; + + do { + v3 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_3); + v2 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_2); + v1 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_1); + v0 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_0); + + test3 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_3); + test2 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_2); + test1 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_1); + } while (v3 != test3 || v2 != test2 || v1 != test1); + + *tsf = v3; + *tsf <<= 48; + tmp = v2; + tmp <<= 32; + *tsf |= tmp; + tmp = v1; + tmp <<= 16; + *tsf |= tmp; + *tsf |= v0; + } +} + +void bcm43xx_tsf_write(struct bcm43xx_private *bcm, u64 tsf) +{ + u32 status; + + status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); + status |= BCM43xx_SBF_TIME_UPDATE; + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status); + mmiowb(); + + /* Be careful with the in-progress timer. + * First zero out the low register, so we have a full + * register-overflow duration to complete the operation. + */ + if (bcm->current_core->rev >= 3) { + u32 lo = (tsf & 0x00000000FFFFFFFFULL); + u32 hi = (tsf & 0xFFFFFFFF00000000ULL) >> 32; + + bcm43xx_write32(bcm, BCM43xx_MMIO_REV3PLUS_TSF_LOW, 0); + mmiowb(); + bcm43xx_write32(bcm, BCM43xx_MMIO_REV3PLUS_TSF_HIGH, hi); + mmiowb(); + bcm43xx_write32(bcm, BCM43xx_MMIO_REV3PLUS_TSF_LOW, lo); + } else { + u16 v0 = (tsf & 0x000000000000FFFFULL); + u16 v1 = (tsf & 0x00000000FFFF0000ULL) >> 16; + u16 v2 = (tsf & 0x0000FFFF00000000ULL) >> 32; + u16 v3 = (tsf & 0xFFFF000000000000ULL) >> 48; + + bcm43xx_write16(bcm, BCM43xx_MMIO_TSF_0, 0); + mmiowb(); + bcm43xx_write16(bcm, BCM43xx_MMIO_TSF_3, v3); + mmiowb(); + bcm43xx_write16(bcm, BCM43xx_MMIO_TSF_2, v2); + mmiowb(); + bcm43xx_write16(bcm, BCM43xx_MMIO_TSF_1, v1); + mmiowb(); + bcm43xx_write16(bcm, BCM43xx_MMIO_TSF_0, v0); + } + + status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); + status &= ~BCM43xx_SBF_TIME_UPDATE; + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status); +} + +static +void bcm43xx_macfilter_set(struct bcm43xx_private *bcm, + u16 offset, + const u8 *mac) +{ + u16 data; + + offset |= 0x0020; + bcm43xx_write16(bcm, BCM43xx_MMIO_MACFILTER_CONTROL, offset); + + data = mac[0]; + data |= mac[1] << 8; + bcm43xx_write16(bcm, BCM43xx_MMIO_MACFILTER_DATA, data); + data = mac[2]; + data |= mac[3] << 8; + bcm43xx_write16(bcm, BCM43xx_MMIO_MACFILTER_DATA, data); + data = mac[4]; + data |= mac[5] << 8; + bcm43xx_write16(bcm, BCM43xx_MMIO_MACFILTER_DATA, data); +} + +static void bcm43xx_macfilter_clear(struct bcm43xx_private *bcm, + u16 offset) +{ + const u8 zero_addr[ETH_ALEN] = { 0 }; + + bcm43xx_macfilter_set(bcm, offset, zero_addr); +} + +static void bcm43xx_write_mac_bssid_templates(struct bcm43xx_private *bcm) +{ + const u8 *mac = (const u8 *)(bcm->net_dev->dev_addr); + const u8 *bssid = (const u8 *)(bcm->ieee->bssid); + u8 mac_bssid[ETH_ALEN * 2]; + int i; + + memcpy(mac_bssid, mac, ETH_ALEN); + memcpy(mac_bssid + ETH_ALEN, bssid, ETH_ALEN); + + /* Write our MAC address and BSSID to template ram */ + for (i = 0; i < ARRAY_SIZE(mac_bssid); i += sizeof(u32)) + bcm43xx_ram_write(bcm, 0x20 + i, *((u32 *)(mac_bssid + i))); + for (i = 0; i < ARRAY_SIZE(mac_bssid); i += sizeof(u32)) + bcm43xx_ram_write(bcm, 0x78 + i, *((u32 *)(mac_bssid + i))); + for (i = 0; i < ARRAY_SIZE(mac_bssid); i += sizeof(u32)) + bcm43xx_ram_write(bcm, 0x478 + i, *((u32 *)(mac_bssid + i))); +} + +//FIXME: Well, we should probably call them from somewhere. +#if 0 +static void bcm43xx_set_slot_time(struct bcm43xx_private *bcm, u16 slot_time) +{ + /* slot_time is in usec. */ + if (bcm43xx_current_phy(bcm)->type != BCM43xx_PHYTYPE_G) + return; + bcm43xx_write16(bcm, 0x684, 510 + slot_time); + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0010, slot_time); +} + +static void bcm43xx_short_slot_timing_enable(struct bcm43xx_private *bcm) +{ + bcm43xx_set_slot_time(bcm, 9); +} + +static void bcm43xx_short_slot_timing_disable(struct bcm43xx_private *bcm) +{ + bcm43xx_set_slot_time(bcm, 20); +} +#endif + +/* FIXME: To get the MAC-filter working, we need to implement the + * following functions (and rename them :) + */ +#if 0 +static void bcm43xx_disassociate(struct bcm43xx_private *bcm) +{ + bcm43xx_mac_suspend(bcm); + bcm43xx_macfilter_clear(bcm, BCM43xx_MACFILTER_ASSOC); + + bcm43xx_ram_write(bcm, 0x0026, 0x0000); + bcm43xx_ram_write(bcm, 0x0028, 0x0000); + bcm43xx_ram_write(bcm, 0x007E, 0x0000); + bcm43xx_ram_write(bcm, 0x0080, 0x0000); + bcm43xx_ram_write(bcm, 0x047E, 0x0000); + bcm43xx_ram_write(bcm, 0x0480, 0x0000); + + if (bcm->current_core->rev < 3) { + bcm43xx_write16(bcm, 0x0610, 0x8000); + bcm43xx_write16(bcm, 0x060E, 0x0000); + } else + bcm43xx_write32(bcm, 0x0188, 0x80000000); + + bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0004, 0x000003ff); + + if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_G && + ieee80211_is_ofdm_rate(bcm->softmac->txrates.default_rate)) + bcm43xx_short_slot_timing_enable(bcm); + + bcm43xx_mac_enable(bcm); +} + +static void bcm43xx_associate(struct bcm43xx_private *bcm, + const u8 *mac) +{ + memcpy(bcm->ieee->bssid, mac, ETH_ALEN); + + bcm43xx_mac_suspend(bcm); + bcm43xx_macfilter_set(bcm, BCM43xx_MACFILTER_ASSOC, mac); + bcm43xx_write_mac_bssid_templates(bcm); + bcm43xx_mac_enable(bcm); +} +#endif + +/* Enable a Generic IRQ. "mask" is the mask of which IRQs to enable. + * Returns the _previously_ enabled IRQ mask. + */ +static inline u32 bcm43xx_interrupt_enable(struct bcm43xx_private *bcm, u32 mask) +{ + u32 old_mask; + + old_mask = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_MASK); + bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_MASK, old_mask | mask); + + return old_mask; +} + +/* Disable a Generic IRQ. "mask" is the mask of which IRQs to disable. + * Returns the _previously_ enabled IRQ mask. + */ +static inline u32 bcm43xx_interrupt_disable(struct bcm43xx_private *bcm, u32 mask) +{ + u32 old_mask; + + old_mask = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_MASK); + bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_MASK, old_mask & ~mask); + + return old_mask; +} + +/* Make sure we don't receive more data from the device. */ +static int bcm43xx_disable_interrupts_sync(struct bcm43xx_private *bcm, u32 *oldstate) +{ + u32 old; + unsigned long flags; + + bcm43xx_lock_mmio(bcm, flags); + if (bcm43xx_is_initializing(bcm) || bcm->shutting_down) { + bcm43xx_unlock_mmio(bcm, flags); + return -EBUSY; + } + old = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); + tasklet_disable(&bcm->isr_tasklet); + bcm43xx_unlock_mmio(bcm, flags); + if (oldstate) + *oldstate = old; + + return 0; +} + +static int bcm43xx_read_radioinfo(struct bcm43xx_private *bcm) +{ + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + u32 radio_id; + u16 manufact; + u16 version; + u8 revision; + s8 i; + + if (bcm->chip_id == 0x4317) { + if (bcm->chip_rev == 0x00) + radio_id = 0x3205017F; + else if (bcm->chip_rev == 0x01) + radio_id = 0x4205017F; + else + radio_id = 0x5205017F; + } else { + bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_CONTROL, BCM43xx_RADIOCTL_ID); + radio_id = bcm43xx_read16(bcm, BCM43xx_MMIO_RADIO_DATA_HIGH); + radio_id <<= 16; + bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_CONTROL, BCM43xx_RADIOCTL_ID); + radio_id |= bcm43xx_read16(bcm, BCM43xx_MMIO_RADIO_DATA_LOW); + } + + manufact = (radio_id & 0x00000FFF); + version = (radio_id & 0x0FFFF000) >> 12; + revision = (radio_id & 0xF0000000) >> 28; + + dprintk(KERN_INFO PFX "Detected Radio: ID: %x (Manuf: %x Ver: %x Rev: %x)\n", + radio_id, manufact, version, revision); + + switch (phy->type) { + case BCM43xx_PHYTYPE_A: + if ((version != 0x2060) || (revision != 1) || (manufact != 0x17f)) + goto err_unsupported_radio; + break; + case BCM43xx_PHYTYPE_B: + if ((version & 0xFFF0) != 0x2050) + goto err_unsupported_radio; + break; + case BCM43xx_PHYTYPE_G: + if (version != 0x2050) + goto err_unsupported_radio; + break; + } + + radio->manufact = manufact; + radio->version = version; + radio->revision = revision; + + /* Set default attenuation values. */ + radio->baseband_atten = bcm43xx_default_baseband_attenuation(bcm); + radio->radio_atten = bcm43xx_default_radio_attenuation(bcm); + radio->txctl1 = bcm43xx_default_txctl1(bcm); + radio->txctl2 = 0xFFFF; + if (phy->type == BCM43xx_PHYTYPE_A) + radio->txpower_desired = bcm->sprom.maxpower_aphy; + else + radio->txpower_desired = bcm->sprom.maxpower_bgphy; + + /* Initialize the in-memory nrssi Lookup Table. */ + for (i = 0; i < 64; i++) + radio->nrssi_lt[i] = i; + + return 0; + +err_unsupported_radio: + printk(KERN_ERR PFX "Unsupported Radio connected to the PHY!\n"); + return -ENODEV; +} + +static const char * bcm43xx_locale_iso(u8 locale) +{ + /* ISO 3166-1 country codes. + * Note that there aren't ISO 3166-1 codes for + * all or locales. (Not all locales are countries) + */ + switch (locale) { + case BCM43xx_LOCALE_WORLD: + case BCM43xx_LOCALE_ALL: + return "XX"; + case BCM43xx_LOCALE_THAILAND: + return "TH"; + case BCM43xx_LOCALE_ISRAEL: + return "IL"; + case BCM43xx_LOCALE_JORDAN: + return "JO"; + case BCM43xx_LOCALE_CHINA: + return "CN"; + case BCM43xx_LOCALE_JAPAN: + case BCM43xx_LOCALE_JAPAN_HIGH: + return "JP"; + case BCM43xx_LOCALE_USA_CANADA_ANZ: + case BCM43xx_LOCALE_USA_LOW: + return "US"; + case BCM43xx_LOCALE_EUROPE: + return "EU"; + case BCM43xx_LOCALE_NONE: + return " "; + } + assert(0); + return " "; +} + +static const char * bcm43xx_locale_string(u8 locale) +{ + switch (locale) { + case BCM43xx_LOCALE_WORLD: + return "World"; + case BCM43xx_LOCALE_THAILAND: + return "Thailand"; + case BCM43xx_LOCALE_ISRAEL: + return "Israel"; + case BCM43xx_LOCALE_JORDAN: + return "Jordan"; + case BCM43xx_LOCALE_CHINA: + return "China"; + case BCM43xx_LOCALE_JAPAN: + return "Japan"; + case BCM43xx_LOCALE_USA_CANADA_ANZ: + return "USA/Canada/ANZ"; + case BCM43xx_LOCALE_EUROPE: + return "Europe"; + case BCM43xx_LOCALE_USA_LOW: + return "USAlow"; + case BCM43xx_LOCALE_JAPAN_HIGH: + return "JapanHigh"; + case BCM43xx_LOCALE_ALL: + return "All"; + case BCM43xx_LOCALE_NONE: + return "None"; + } + assert(0); + return ""; +} + +static inline u8 bcm43xx_crc8(u8 crc, u8 data) +{ + static const u8 t[] = { + 0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B, + 0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21, + 0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF, + 0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5, + 0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14, + 0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E, + 0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80, + 0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA, + 0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95, + 0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF, + 0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01, + 0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B, + 0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA, + 0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0, + 0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E, + 0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34, + 0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0, + 0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A, + 0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54, + 0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E, + 0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF, + 0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5, + 0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B, + 0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61, + 0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E, + 0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74, + 0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA, + 0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0, + 0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41, + 0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B, + 0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5, + 0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F, + }; + return t[crc ^ data]; +} + +static u8 bcm43xx_sprom_crc(const u16 *sprom) +{ + int word; + u8 crc = 0xFF; + + for (word = 0; word < BCM43xx_SPROM_SIZE - 1; word++) { + crc = bcm43xx_crc8(crc, sprom[word] & 0x00FF); + crc = bcm43xx_crc8(crc, (sprom[word] & 0xFF00) >> 8); + } + crc = bcm43xx_crc8(crc, sprom[BCM43xx_SPROM_VERSION] & 0x00FF); + crc ^= 0xFF; + + return crc; +} + +int bcm43xx_sprom_read(struct bcm43xx_private *bcm, u16 *sprom) +{ + int i; + u8 crc, expected_crc; + + for (i = 0; i < BCM43xx_SPROM_SIZE; i++) + sprom[i] = bcm43xx_read16(bcm, BCM43xx_SPROM_BASE + (i * 2)); + /* CRC-8 check. */ + crc = bcm43xx_sprom_crc(sprom); + expected_crc = (sprom[BCM43xx_SPROM_VERSION] & 0xFF00) >> 8; + if (crc != expected_crc) { + printk(KERN_WARNING PFX "WARNING: Invalid SPROM checksum " + "(0x%02X, expected: 0x%02X)\n", + crc, expected_crc); + return -EINVAL; + } + + return 0; +} + +int bcm43xx_sprom_write(struct bcm43xx_private *bcm, const u16 *sprom) +{ + int i, err; + u8 crc, expected_crc; + u32 spromctl; + + /* CRC-8 validation of the input data. */ + crc = bcm43xx_sprom_crc(sprom); + expected_crc = (sprom[BCM43xx_SPROM_VERSION] & 0xFF00) >> 8; + if (crc != expected_crc) { + printk(KERN_ERR PFX "SPROM input data: Invalid CRC\n"); + return -EINVAL; + } + + printk(KERN_INFO PFX "Writing SPROM. Do NOT turn off the power! Please stand by...\n"); + err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCICFG_SPROMCTL, &spromctl); + if (err) + goto err_ctlreg; + spromctl |= 0x10; /* SPROM WRITE enable. */ + bcm43xx_pci_write_config32(bcm, BCM43xx_PCICFG_SPROMCTL, spromctl); + if (err) + goto err_ctlreg; + /* We must burn lots of CPU cycles here, but that does not + * really matter as one does not write the SPROM every other minute... + */ + printk(KERN_INFO PFX "[ 0%%"); + mdelay(500); + for (i = 0; i < BCM43xx_SPROM_SIZE; i++) { + if (i == 16) + printk("25%%"); + else if (i == 32) + printk("50%%"); + else if (i == 48) + printk("75%%"); + else if (i % 2) + printk("."); + bcm43xx_write16(bcm, BCM43xx_SPROM_BASE + (i * 2), sprom[i]); + mmiowb(); + mdelay(20); + } + spromctl &= ~0x10; /* SPROM WRITE enable. */ + bcm43xx_pci_write_config32(bcm, BCM43xx_PCICFG_SPROMCTL, spromctl); + if (err) + goto err_ctlreg; + mdelay(500); + printk("100%% ]\n"); + printk(KERN_INFO PFX "SPROM written.\n"); + bcm43xx_controller_restart(bcm, "SPROM update"); + + return 0; +err_ctlreg: + printk(KERN_ERR PFX "Could not access SPROM control register.\n"); + return -ENODEV; +} + +static int bcm43xx_sprom_extract(struct bcm43xx_private *bcm) +{ + u16 value; + u16 *sprom; +#ifdef CONFIG_BCM947XX + char *c; +#endif + + sprom = kzalloc(BCM43xx_SPROM_SIZE * sizeof(u16), + GFP_KERNEL); + if (!sprom) { + printk(KERN_ERR PFX "sprom_extract OOM\n"); + return -ENOMEM; + } +#ifdef CONFIG_BCM947XX + sprom[BCM43xx_SPROM_BOARDFLAGS2] = atoi(nvram_get("boardflags2")); + sprom[BCM43xx_SPROM_BOARDFLAGS] = atoi(nvram_get("boardflags")); + + if ((c = nvram_get("il0macaddr")) != NULL) + e_aton(c, (char *) &(sprom[BCM43xx_SPROM_IL0MACADDR])); + + if ((c = nvram_get("et1macaddr")) != NULL) + e_aton(c, (char *) &(sprom[BCM43xx_SPROM_ET1MACADDR])); + + sprom[BCM43xx_SPROM_PA0B0] = atoi(nvram_get("pa0b0")); + sprom[BCM43xx_SPROM_PA0B1] = atoi(nvram_get("pa0b1")); + sprom[BCM43xx_SPROM_PA0B2] = atoi(nvram_get("pa0b2")); + + sprom[BCM43xx_SPROM_PA1B0] = atoi(nvram_get("pa1b0")); + sprom[BCM43xx_SPROM_PA1B1] = atoi(nvram_get("pa1b1")); + sprom[BCM43xx_SPROM_PA1B2] = atoi(nvram_get("pa1b2")); + + sprom[BCM43xx_SPROM_BOARDREV] = atoi(nvram_get("boardrev")); +#else + bcm43xx_sprom_read(bcm, sprom); +#endif + + /* boardflags2 */ + value = sprom[BCM43xx_SPROM_BOARDFLAGS2]; + bcm->sprom.boardflags2 = value; + + /* il0macaddr */ + value = sprom[BCM43xx_SPROM_IL0MACADDR + 0]; + *(((u16 *)bcm->sprom.il0macaddr) + 0) = cpu_to_be16(value); + value = sprom[BCM43xx_SPROM_IL0MACADDR + 1]; + *(((u16 *)bcm->sprom.il0macaddr) + 1) = cpu_to_be16(value); + value = sprom[BCM43xx_SPROM_IL0MACADDR + 2]; + *(((u16 *)bcm->sprom.il0macaddr) + 2) = cpu_to_be16(value); + + /* et0macaddr */ + value = sprom[BCM43xx_SPROM_ET0MACADDR + 0]; + *(((u16 *)bcm->sprom.et0macaddr) + 0) = cpu_to_be16(value); + value = sprom[BCM43xx_SPROM_ET0MACADDR + 1]; + *(((u16 *)bcm->sprom.et0macaddr) + 1) = cpu_to_be16(value); + value = sprom[BCM43xx_SPROM_ET0MACADDR + 2]; + *(((u16 *)bcm->sprom.et0macaddr) + 2) = cpu_to_be16(value); + + /* et1macaddr */ + value = sprom[BCM43xx_SPROM_ET1MACADDR + 0]; + *(((u16 *)bcm->sprom.et1macaddr) + 0) = cpu_to_be16(value); + value = sprom[BCM43xx_SPROM_ET1MACADDR + 1]; + *(((u16 *)bcm->sprom.et1macaddr) + 1) = cpu_to_be16(value); + value = sprom[BCM43xx_SPROM_ET1MACADDR + 2]; + *(((u16 *)bcm->sprom.et1macaddr) + 2) = cpu_to_be16(value); + + /* ethernet phy settings */ + value = sprom[BCM43xx_SPROM_ETHPHY]; + bcm->sprom.et0phyaddr = (value & 0x001F); + bcm->sprom.et1phyaddr = (value & 0x03E0) >> 5; + bcm->sprom.et0mdcport = (value & (1 << 14)) >> 14; + bcm->sprom.et1mdcport = (value & (1 << 15)) >> 15; + + /* boardrev, antennas, locale */ + value = sprom[BCM43xx_SPROM_BOARDREV]; + bcm->sprom.boardrev = (value & 0x00FF); + bcm->sprom.locale = (value & 0x0F00) >> 8; + bcm->sprom.antennas_aphy = (value & 0x3000) >> 12; + bcm->sprom.antennas_bgphy = (value & 0xC000) >> 14; + if (modparam_locale != -1) { + if (modparam_locale >= 0 && modparam_locale <= 11) { + bcm->sprom.locale = modparam_locale; + printk(KERN_WARNING PFX "Operating with modified " + "LocaleCode %u (%s)\n", + bcm->sprom.locale, + bcm43xx_locale_string(bcm->sprom.locale)); + } else { + printk(KERN_WARNING PFX "Module parameter \"locale\" " + "invalid value. (0 - 11)\n"); + } + } + + /* pa0b* */ + value = sprom[BCM43xx_SPROM_PA0B0]; + bcm->sprom.pa0b0 = value; + value = sprom[BCM43xx_SPROM_PA0B1]; + bcm->sprom.pa0b1 = value; + value = sprom[BCM43xx_SPROM_PA0B2]; + bcm->sprom.pa0b2 = value; + + /* wl0gpio* */ + value = sprom[BCM43xx_SPROM_WL0GPIO0]; + if (value == 0x0000) + value = 0xFFFF; + bcm->sprom.wl0gpio0 = value & 0x00FF; + bcm->sprom.wl0gpio1 = (value & 0xFF00) >> 8; + value = sprom[BCM43xx_SPROM_WL0GPIO2]; + if (value == 0x0000) + value = 0xFFFF; + bcm->sprom.wl0gpio2 = value & 0x00FF; + bcm->sprom.wl0gpio3 = (value & 0xFF00) >> 8; + + /* maxpower */ + value = sprom[BCM43xx_SPROM_MAXPWR]; + bcm->sprom.maxpower_aphy = (value & 0xFF00) >> 8; + bcm->sprom.maxpower_bgphy = value & 0x00FF; + + /* pa1b* */ + value = sprom[BCM43xx_SPROM_PA1B0]; + bcm->sprom.pa1b0 = value; + value = sprom[BCM43xx_SPROM_PA1B1]; + bcm->sprom.pa1b1 = value; + value = sprom[BCM43xx_SPROM_PA1B2]; + bcm->sprom.pa1b2 = value; + + /* idle tssi target */ + value = sprom[BCM43xx_SPROM_IDL_TSSI_TGT]; + bcm->sprom.idle_tssi_tgt_aphy = value & 0x00FF; + bcm->sprom.idle_tssi_tgt_bgphy = (value & 0xFF00) >> 8; + + /* boardflags */ + value = sprom[BCM43xx_SPROM_BOARDFLAGS]; + if (value == 0xFFFF) + value = 0x0000; + bcm->sprom.boardflags = value; + /* boardflags workarounds */ + if (bcm->board_vendor == PCI_VENDOR_ID_DELL && + bcm->chip_id == 0x4301 && + bcm->board_revision == 0x74) + bcm->sprom.boardflags |= BCM43xx_BFL_BTCOEXIST; + if (bcm->board_vendor == PCI_VENDOR_ID_APPLE && + bcm->board_type == 0x4E && + bcm->board_revision > 0x40) + bcm->sprom.boardflags |= BCM43xx_BFL_PACTRL; + + /* antenna gain */ + value = sprom[BCM43xx_SPROM_ANTENNA_GAIN]; + if (value == 0x0000 || value == 0xFFFF) + value = 0x0202; + /* convert values to Q5.2 */ + bcm->sprom.antennagain_aphy = ((value & 0xFF00) >> 8) * 4; + bcm->sprom.antennagain_bgphy = (value & 0x00FF) * 4; + + kfree(sprom); + + return 0; +} + +static void bcm43xx_geo_init(struct bcm43xx_private *bcm) +{ + struct ieee80211_geo geo; + struct ieee80211_channel *chan; + int have_a = 0, have_bg = 0; + int i; + u8 channel; + struct bcm43xx_phyinfo *phy; + const char *iso_country; + + memset(&geo, 0, sizeof(geo)); + for (i = 0; i < bcm->nr_80211_available; i++) { + phy = &(bcm->core_80211_ext[i].phy); + switch (phy->type) { + case BCM43xx_PHYTYPE_B: + case BCM43xx_PHYTYPE_G: + have_bg = 1; + break; + case BCM43xx_PHYTYPE_A: + have_a = 1; + break; + default: + assert(0); + } + } + iso_country = bcm43xx_locale_iso(bcm->sprom.locale); + + if (have_a) { + for (i = 0, channel = 0; channel < 201; channel++) { + chan = &geo.a[i++]; + chan->freq = bcm43xx_channel_to_freq_a(channel); + chan->channel = channel; + } + geo.a_channels = i; + } + if (have_bg) { + for (i = 0, channel = 1; channel < 15; channel++) { + chan = &geo.bg[i++]; + chan->freq = bcm43xx_channel_to_freq_bg(channel); + chan->channel = channel; + } + geo.bg_channels = i; + } + memcpy(geo.name, iso_country, 2); + if (0 /*TODO: Outdoor use only */) + geo.name[2] = 'O'; + else if (0 /*TODO: Indoor use only */) + geo.name[2] = 'I'; + else + geo.name[2] = ' '; + geo.name[3] = '\0'; + + ieee80211_set_geo(bcm->ieee, &geo); +} + +/* DummyTransmission function, as documented on + * http://bcm-specs.sipsolutions.net/DummyTransmission + */ +void bcm43xx_dummy_transmission(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + unsigned int i, max_loop; + u16 value = 0; + u32 buffer[5] = { + 0x00000000, + 0x0000D400, + 0x00000000, + 0x00000001, + 0x00000000, + }; + + switch (phy->type) { + case BCM43xx_PHYTYPE_A: + max_loop = 0x1E; + buffer[0] = 0xCC010200; + break; + case BCM43xx_PHYTYPE_B: + case BCM43xx_PHYTYPE_G: + max_loop = 0xFA; + buffer[0] = 0x6E840B00; + break; + default: + assert(0); + return; + } + + for (i = 0; i < 5; i++) + bcm43xx_ram_write(bcm, i * 4, buffer[i]); + + bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* dummy read */ + + bcm43xx_write16(bcm, 0x0568, 0x0000); + bcm43xx_write16(bcm, 0x07C0, 0x0000); + bcm43xx_write16(bcm, 0x050C, ((phy->type == BCM43xx_PHYTYPE_A) ? 1 : 0)); + bcm43xx_write16(bcm, 0x0508, 0x0000); + bcm43xx_write16(bcm, 0x050A, 0x0000); + bcm43xx_write16(bcm, 0x054C, 0x0000); + bcm43xx_write16(bcm, 0x056A, 0x0014); + bcm43xx_write16(bcm, 0x0568, 0x0826); + bcm43xx_write16(bcm, 0x0500, 0x0000); + bcm43xx_write16(bcm, 0x0502, 0x0030); + + if (radio->version == 0x2050 && radio->revision <= 0x5) + bcm43xx_radio_write16(bcm, 0x0051, 0x0017); + for (i = 0x00; i < max_loop; i++) { + value = bcm43xx_read16(bcm, 0x050E); + if (value & 0x0080) + break; + udelay(10); + } + for (i = 0x00; i < 0x0A; i++) { + value = bcm43xx_read16(bcm, 0x050E); + if (value & 0x0400) + break; + udelay(10); + } + for (i = 0x00; i < 0x0A; i++) { + value = bcm43xx_read16(bcm, 0x0690); + if (!(value & 0x0100)) + break; + udelay(10); + } + if (radio->version == 0x2050 && radio->revision <= 0x5) + bcm43xx_radio_write16(bcm, 0x0051, 0x0037); +} + +static void key_write(struct bcm43xx_private *bcm, + u8 index, u8 algorithm, const u16 *key) +{ + unsigned int i, basic_wep = 0; + u32 offset; + u16 value; + + /* Write associated key information */ + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x100 + (index * 2), + ((index << 4) | (algorithm & 0x0F))); + + /* The first 4 WEP keys need extra love */ + if (((algorithm == BCM43xx_SEC_ALGO_WEP) || + (algorithm == BCM43xx_SEC_ALGO_WEP104)) && (index < 4)) + basic_wep = 1; + + /* Write key payload, 8 little endian words */ + offset = bcm->security_offset + (index * BCM43xx_SEC_KEYSIZE); + for (i = 0; i < (BCM43xx_SEC_KEYSIZE / sizeof(u16)); i++) { + value = cpu_to_le16(key[i]); + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, + offset + (i * 2), value); + + if (!basic_wep) + continue; + + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, + offset + (i * 2) + 4 * BCM43xx_SEC_KEYSIZE, + value); + } +} + +static void keymac_write(struct bcm43xx_private *bcm, + u8 index, const u32 *addr) +{ + /* for keys 0-3 there is no associated mac address */ + if (index < 4) + return; + + index -= 4; + if (bcm->current_core->rev >= 5) { + bcm43xx_shm_write32(bcm, + BCM43xx_SHM_HWMAC, + index * 2, + cpu_to_be32(*addr)); + bcm43xx_shm_write16(bcm, + BCM43xx_SHM_HWMAC, + (index * 2) + 1, + cpu_to_be16(*((u16 *)(addr + 1)))); + } else { + if (index < 8) { + TODO(); /* Put them in the macaddress filter */ + } else { + TODO(); + /* Put them BCM43xx_SHM_SHARED, stating index 0x0120. + Keep in mind to update the count of keymacs in 0x003E as well! */ + } + } +} + +static int bcm43xx_key_write(struct bcm43xx_private *bcm, + u8 index, u8 algorithm, + const u8 *_key, int key_len, + const u8 *mac_addr) +{ + u8 key[BCM43xx_SEC_KEYSIZE] = { 0 }; + + if (index >= ARRAY_SIZE(bcm->key)) + return -EINVAL; + if (key_len > ARRAY_SIZE(key)) + return -EINVAL; + if (algorithm < 1 || algorithm > 5) + return -EINVAL; + + memcpy(key, _key, key_len); + key_write(bcm, index, algorithm, (const u16 *)key); + keymac_write(bcm, index, (const u32 *)mac_addr); + + bcm->key[index].algorithm = algorithm; + + return 0; +} + +static void bcm43xx_clear_keys(struct bcm43xx_private *bcm) +{ + static const u32 zero_mac[2] = { 0 }; + unsigned int i,j, nr_keys = 54; + u16 offset; + + if (bcm->current_core->rev < 5) + nr_keys = 16; + assert(nr_keys <= ARRAY_SIZE(bcm->key)); + + for (i = 0; i < nr_keys; i++) { + bcm->key[i].enabled = 0; + /* returns for i < 4 immediately */ + keymac_write(bcm, i, zero_mac); + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, + 0x100 + (i * 2), 0x0000); + for (j = 0; j < 8; j++) { + offset = bcm->security_offset + (j * 4) + (i * BCM43xx_SEC_KEYSIZE); + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, + offset, 0x0000); + } + } + dprintk(KERN_INFO PFX "Keys cleared\n"); +} + +/* Lowlevel core-switch function. This is only to be used in + * bcm43xx_switch_core() and bcm43xx_probe_cores() + */ +static int _switch_core(struct bcm43xx_private *bcm, int core) +{ + int err; + int attempts = 0; + u32 current_core; + + assert(core >= 0); + while (1) { + err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCICFG_ACTIVE_CORE, + (core * 0x1000) + 0x18000000); + if (unlikely(err)) + goto error; + err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCICFG_ACTIVE_CORE, + ¤t_core); + if (unlikely(err)) + goto error; + current_core = (current_core - 0x18000000) / 0x1000; + if (current_core == core) + break; + + if (unlikely(attempts++ > BCM43xx_SWITCH_CORE_MAX_RETRIES)) + goto error; + udelay(10); + } +#ifdef CONFIG_BCM947XX + if (bcm->pci_dev->bus->number == 0) + bcm->current_core_offset = 0x1000 * core; + else + bcm->current_core_offset = 0; +#endif + + return 0; +error: + printk(KERN_ERR PFX "Failed to switch to core %d\n", core); + return -ENODEV; +} + +int bcm43xx_switch_core(struct bcm43xx_private *bcm, struct bcm43xx_coreinfo *new_core) +{ + int err; + + if (unlikely(!new_core)) + return 0; + if (!new_core->available) + return -ENODEV; + if (bcm->current_core == new_core) + return 0; + err = _switch_core(bcm, new_core->index); + if (unlikely(err)) + goto out; + + bcm->current_core = new_core; + bcm->current_80211_core_idx = -1; + if (new_core->id == BCM43xx_COREID_80211) + bcm->current_80211_core_idx = (int)(new_core - &(bcm->core_80211[0])); + +out: + return err; +} + +static int bcm43xx_core_enabled(struct bcm43xx_private *bcm) +{ + u32 value; + + value = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); + value &= BCM43xx_SBTMSTATELOW_CLOCK | BCM43xx_SBTMSTATELOW_RESET + | BCM43xx_SBTMSTATELOW_REJECT; + + return (value == BCM43xx_SBTMSTATELOW_CLOCK); +} + +/* disable current core */ +static int bcm43xx_core_disable(struct bcm43xx_private *bcm, u32 core_flags) +{ + u32 sbtmstatelow; + u32 sbtmstatehigh; + int i; + + /* fetch sbtmstatelow from core information registers */ + sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); + + /* core is already in reset */ + if (sbtmstatelow & BCM43xx_SBTMSTATELOW_RESET) + goto out; + + if (sbtmstatelow & BCM43xx_SBTMSTATELOW_CLOCK) { + sbtmstatelow = BCM43xx_SBTMSTATELOW_CLOCK | + BCM43xx_SBTMSTATELOW_REJECT; + bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); + + for (i = 0; i < 1000; i++) { + sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); + if (sbtmstatelow & BCM43xx_SBTMSTATELOW_REJECT) { + i = -1; + break; + } + udelay(10); + } + if (i != -1) { + printk(KERN_ERR PFX "Error: core_disable() REJECT timeout!\n"); + return -EBUSY; + } + + for (i = 0; i < 1000; i++) { + sbtmstatehigh = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATEHIGH); + if (!(sbtmstatehigh & BCM43xx_SBTMSTATEHIGH_BUSY)) { + i = -1; + break; + } + udelay(10); + } + if (i != -1) { + printk(KERN_ERR PFX "Error: core_disable() BUSY timeout!\n"); + return -EBUSY; + } + + sbtmstatelow = BCM43xx_SBTMSTATELOW_FORCE_GATE_CLOCK | + BCM43xx_SBTMSTATELOW_REJECT | + BCM43xx_SBTMSTATELOW_RESET | + BCM43xx_SBTMSTATELOW_CLOCK | + core_flags; + bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); + udelay(10); + } + + sbtmstatelow = BCM43xx_SBTMSTATELOW_RESET | + BCM43xx_SBTMSTATELOW_REJECT | + core_flags; + bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); + +out: + bcm->current_core->enabled = 0; + + return 0; +} + +/* enable (reset) current core */ +static int bcm43xx_core_enable(struct bcm43xx_private *bcm, u32 core_flags) +{ + u32 sbtmstatelow; + u32 sbtmstatehigh; + u32 sbimstate; + int err; + + err = bcm43xx_core_disable(bcm, core_flags); + if (err) + goto out; + + sbtmstatelow = BCM43xx_SBTMSTATELOW_CLOCK | + BCM43xx_SBTMSTATELOW_RESET | + BCM43xx_SBTMSTATELOW_FORCE_GATE_CLOCK | + core_flags; + bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); + udelay(1); + + sbtmstatehigh = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATEHIGH); + if (sbtmstatehigh & BCM43xx_SBTMSTATEHIGH_SERROR) { + sbtmstatehigh = 0x00000000; + bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATEHIGH, sbtmstatehigh); + } + + sbimstate = bcm43xx_read32(bcm, BCM43xx_CIR_SBIMSTATE); + if (sbimstate & (BCM43xx_SBIMSTATE_IB_ERROR | BCM43xx_SBIMSTATE_TIMEOUT)) { + sbimstate &= ~(BCM43xx_SBIMSTATE_IB_ERROR | BCM43xx_SBIMSTATE_TIMEOUT); + bcm43xx_write32(bcm, BCM43xx_CIR_SBIMSTATE, sbimstate); + } + + sbtmstatelow = BCM43xx_SBTMSTATELOW_CLOCK | + BCM43xx_SBTMSTATELOW_FORCE_GATE_CLOCK | + core_flags; + bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); + udelay(1); + + sbtmstatelow = BCM43xx_SBTMSTATELOW_CLOCK | core_flags; + bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); + udelay(1); + + bcm->current_core->enabled = 1; + assert(err == 0); +out: + return err; +} + +/* http://bcm-specs.sipsolutions.net/80211CoreReset */ +void bcm43xx_wireless_core_reset(struct bcm43xx_private *bcm, int connect_phy) +{ + u32 flags = 0x00040000; + + if ((bcm43xx_core_enabled(bcm)) && + !bcm43xx_using_pio(bcm)) { +//FIXME: Do we _really_ want #ifndef CONFIG_BCM947XX here? +#ifndef CONFIG_BCM947XX + /* reset all used DMA controllers. */ + bcm43xx_dmacontroller_tx_reset(bcm, BCM43xx_MMIO_DMA1_BASE); + bcm43xx_dmacontroller_tx_reset(bcm, BCM43xx_MMIO_DMA2_BASE); + bcm43xx_dmacontroller_tx_reset(bcm, BCM43xx_MMIO_DMA3_BASE); + bcm43xx_dmacontroller_tx_reset(bcm, BCM43xx_MMIO_DMA4_BASE); + bcm43xx_dmacontroller_rx_reset(bcm, BCM43xx_MMIO_DMA1_BASE); + if (bcm->current_core->rev < 5) + bcm43xx_dmacontroller_rx_reset(bcm, BCM43xx_MMIO_DMA4_BASE); +#endif + } + if (bcm->shutting_down) { + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, + bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD) + & ~(BCM43xx_SBF_MAC_ENABLED | 0x00000002)); + } else { + if (connect_phy) + flags |= 0x20000000; + bcm43xx_phy_connect(bcm, connect_phy); + bcm43xx_core_enable(bcm, flags); + bcm43xx_write16(bcm, 0x03E6, 0x0000); + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, + bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD) + | BCM43xx_SBF_400); + } +} + +static void bcm43xx_wireless_core_disable(struct bcm43xx_private *bcm) +{ + bcm43xx_radio_turn_off(bcm); + bcm43xx_write16(bcm, 0x03E6, 0x00F4); + bcm43xx_core_disable(bcm, 0); +} + +/* Mark the current 80211 core inactive. + * "active_80211_core" is the other 80211 core, which is used. + */ +static int bcm43xx_wireless_core_mark_inactive(struct bcm43xx_private *bcm, + struct bcm43xx_coreinfo *active_80211_core) +{ + u32 sbtmstatelow; + struct bcm43xx_coreinfo *old_core; + int err = 0; + + bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); + bcm43xx_radio_turn_off(bcm); + sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); + sbtmstatelow &= ~0x200a0000; + sbtmstatelow |= 0xa0000; + bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); + udelay(1); + sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); + sbtmstatelow &= ~0xa0000; + sbtmstatelow |= 0x80000; + bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); + udelay(1); + + if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_G) { + old_core = bcm->current_core; + err = bcm43xx_switch_core(bcm, active_80211_core); + if (err) + goto out; + sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); + sbtmstatelow &= ~0x20000000; + sbtmstatelow |= 0x20000000; + bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); + err = bcm43xx_switch_core(bcm, old_core); + } + +out: + return err; +} + +static void handle_irq_transmit_status(struct bcm43xx_private *bcm) +{ + u32 v0, v1; + u16 tmp; + struct bcm43xx_xmitstatus stat; + + while (1) { + v0 = bcm43xx_read32(bcm, BCM43xx_MMIO_XMITSTAT_0); + if (!v0) + break; + v1 = bcm43xx_read32(bcm, BCM43xx_MMIO_XMITSTAT_1); + + stat.cookie = (v0 >> 16) & 0x0000FFFF; + tmp = (u16)((v0 & 0xFFF0) | ((v0 & 0xF) >> 1)); + stat.flags = tmp & 0xFF; + stat.cnt1 = (tmp & 0x0F00) >> 8; + stat.cnt2 = (tmp & 0xF000) >> 12; + stat.seq = (u16)(v1 & 0xFFFF); + stat.unknown = (u16)((v1 >> 16) & 0xFF); + + bcm43xx_debugfs_log_txstat(bcm, &stat); + + if (stat.flags & BCM43xx_TXSTAT_FLAG_IGNORE) + continue; + if (!(stat.flags & BCM43xx_TXSTAT_FLAG_ACK)) { + //TODO: packet was not acked (was lost) + } + //TODO: There are more (unknown) flags to test. see bcm43xx_main.h + + if (bcm43xx_using_pio(bcm)) + bcm43xx_pio_handle_xmitstatus(bcm, &stat); + else + bcm43xx_dma_handle_xmitstatus(bcm, &stat); + } +} + +static void bcm43xx_generate_noise_sample(struct bcm43xx_private *bcm) +{ + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x408, 0x7F7F); + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x40A, 0x7F7F); + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD, + bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD) | (1 << 4)); + assert(bcm->noisecalc.core_at_start == bcm->current_core); + assert(bcm->noisecalc.channel_at_start == bcm43xx_current_radio(bcm)->channel); +} + +static void bcm43xx_calculate_link_quality(struct bcm43xx_private *bcm) +{ + /* Top half of Link Quality calculation. */ + + if (bcm->noisecalc.calculation_running) + return; + bcm->noisecalc.core_at_start = bcm->current_core; + bcm->noisecalc.channel_at_start = bcm43xx_current_radio(bcm)->channel; + bcm->noisecalc.calculation_running = 1; + bcm->noisecalc.nr_samples = 0; + + bcm43xx_generate_noise_sample(bcm); +} + +static void handle_irq_noise(struct bcm43xx_private *bcm) +{ + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + u16 tmp; + u8 noise[4]; + u8 i, j; + s32 average; + + /* Bottom half of Link Quality calculation. */ + + assert(bcm->noisecalc.calculation_running); + if (bcm->noisecalc.core_at_start != bcm->current_core || + bcm->noisecalc.channel_at_start != radio->channel) + goto drop_calculation; + tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x408); + noise[0] = (tmp & 0x00FF); + noise[1] = (tmp & 0xFF00) >> 8; + tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x40A); + noise[2] = (tmp & 0x00FF); + noise[3] = (tmp & 0xFF00) >> 8; + if (noise[0] == 0x7F || noise[1] == 0x7F || + noise[2] == 0x7F || noise[3] == 0x7F) + goto generate_new; + + /* Get the noise samples. */ + assert(bcm->noisecalc.nr_samples <= 8); + i = bcm->noisecalc.nr_samples; + noise[0] = limit_value(noise[0], 0, ARRAY_SIZE(radio->nrssi_lt) - 1); + noise[1] = limit_value(noise[1], 0, ARRAY_SIZE(radio->nrssi_lt) - 1); + noise[2] = limit_value(noise[2], 0, ARRAY_SIZE(radio->nrssi_lt) - 1); + noise[3] = limit_value(noise[3], 0, ARRAY_SIZE(radio->nrssi_lt) - 1); + bcm->noisecalc.samples[i][0] = radio->nrssi_lt[noise[0]]; + bcm->noisecalc.samples[i][1] = radio->nrssi_lt[noise[1]]; + bcm->noisecalc.samples[i][2] = radio->nrssi_lt[noise[2]]; + bcm->noisecalc.samples[i][3] = radio->nrssi_lt[noise[3]]; + bcm->noisecalc.nr_samples++; + if (bcm->noisecalc.nr_samples == 8) { + /* Calculate the Link Quality by the noise samples. */ + average = 0; + for (i = 0; i < 8; i++) { + for (j = 0; j < 4; j++) + average += bcm->noisecalc.samples[i][j]; + } + average /= (8 * 4); + average *= 125; + average += 64; + average /= 128; + + tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x40C); + tmp = (tmp / 128) & 0x1F; + if (tmp >= 8) + average += 2; + else + average -= 25; + if (tmp == 8) + average -= 72; + else + average -= 48; + +/* FIXME: This is wrong, but people want fancy stats. well... */ +bcm->stats.noise = average; + if (average > -65) + bcm->stats.link_quality = 0; + else if (average > -75) + bcm->stats.link_quality = 1; + else if (average > -85) + bcm->stats.link_quality = 2; + else + bcm->stats.link_quality = 3; +// dprintk(KERN_INFO PFX "Link Quality: %u (avg was %d)\n", bcm->stats.link_quality, average); +drop_calculation: + bcm->noisecalc.calculation_running = 0; + return; + } +generate_new: + bcm43xx_generate_noise_sample(bcm); +} + +static void handle_irq_ps(struct bcm43xx_private *bcm) +{ + if (bcm->ieee->iw_mode == IW_MODE_MASTER) { + ///TODO: PS TBTT + } else { + if (1/*FIXME: the last PSpoll frame was sent successfully */) + bcm43xx_power_saving_ctl_bits(bcm, -1, -1); + } + if (bcm->ieee->iw_mode == IW_MODE_ADHOC) + bcm->reg124_set_0x4 = 1; + //FIXME else set to false? +} + +static void handle_irq_reg124(struct bcm43xx_private *bcm) +{ + if (!bcm->reg124_set_0x4) + return; + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD, + bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD) + | 0x4); + //FIXME: reset reg124_set_0x4 to false? +} + +static void handle_irq_pmq(struct bcm43xx_private *bcm) +{ + u32 tmp; + + //TODO: AP mode. + + while (1) { + tmp = bcm43xx_read32(bcm, BCM43xx_MMIO_PS_STATUS); + if (!(tmp & 0x00000008)) + break; + } + /* 16bit write is odd, but correct. */ + bcm43xx_write16(bcm, BCM43xx_MMIO_PS_STATUS, 0x0002); +} + +static void bcm43xx_generate_beacon_template(struct bcm43xx_private *bcm, + u16 ram_offset, u16 shm_size_offset) +{ + u32 value; + u16 size = 0; + + /* Timestamp. */ + //FIXME: assumption: The chip sets the timestamp + value = 0; + bcm43xx_ram_write(bcm, ram_offset++, value); + bcm43xx_ram_write(bcm, ram_offset++, value); + size += 8; + + /* Beacon Interval / Capability Information */ + value = 0x0000;//FIXME: Which interval? + value |= (1 << 0) << 16; /* ESS */ + value |= (1 << 2) << 16; /* CF Pollable */ //FIXME? + value |= (1 << 3) << 16; /* CF Poll Request */ //FIXME? + if (!bcm->ieee->open_wep) + value |= (1 << 4) << 16; /* Privacy */ + bcm43xx_ram_write(bcm, ram_offset++, value); + size += 4; + + /* SSID */ + //TODO + + /* FH Parameter Set */ + //TODO + + /* DS Parameter Set */ + //TODO + + /* CF Parameter Set */ + //TODO + + /* TIM */ + //TODO + + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, shm_size_offset, size); +} + +static void handle_irq_beacon(struct bcm43xx_private *bcm) +{ + u32 status; + + bcm->irq_savedstate &= ~BCM43xx_IRQ_BEACON; + status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD); + + if ((status & 0x1) && (status & 0x2)) { + /* ACK beacon IRQ. */ + bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, + BCM43xx_IRQ_BEACON); + bcm->irq_savedstate |= BCM43xx_IRQ_BEACON; + return; + } + if (!(status & 0x1)) { + bcm43xx_generate_beacon_template(bcm, 0x68, 0x18); + status |= 0x1; + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD, status); + } + if (!(status & 0x2)) { + bcm43xx_generate_beacon_template(bcm, 0x468, 0x1A); + status |= 0x2; + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD, status); + } +} + +/* Interrupt handler bottom-half */ +static void bcm43xx_interrupt_tasklet(struct bcm43xx_private *bcm) +{ + u32 reason; + u32 dma_reason[4]; + int activity = 0; + unsigned long flags; + +#ifdef CONFIG_BCM43XX_DEBUG + u32 _handled = 0x00000000; +# define bcmirq_handled(irq) do { _handled |= (irq); } while (0) +#else +# define bcmirq_handled(irq) do { /* nothing */ } while (0) +#endif /* CONFIG_BCM43XX_DEBUG*/ + + bcm43xx_lock_mmio(bcm, flags); + reason = bcm->irq_reason; + dma_reason[0] = bcm->dma_reason[0]; + dma_reason[1] = bcm->dma_reason[1]; + dma_reason[2] = bcm->dma_reason[2]; + dma_reason[3] = bcm->dma_reason[3]; + + if (unlikely(reason & BCM43xx_IRQ_XMIT_ERROR)) { + /* TX error. We get this when Template Ram is written in wrong endianess + * in dummy_tx(). We also get this if something is wrong with the TX header + * on DMA or PIO queues. + * Maybe we get this in other error conditions, too. + */ + printkl(KERN_ERR PFX "FATAL ERROR: BCM43xx_IRQ_XMIT_ERROR\n"); + bcmirq_handled(BCM43xx_IRQ_XMIT_ERROR); + } + if (unlikely((dma_reason[0] & BCM43xx_DMAIRQ_FATALMASK) | + (dma_reason[1] & BCM43xx_DMAIRQ_FATALMASK) | + (dma_reason[2] & BCM43xx_DMAIRQ_FATALMASK) | + (dma_reason[3] & BCM43xx_DMAIRQ_FATALMASK))) { + printkl(KERN_ERR PFX "FATAL ERROR: Fatal DMA error: " + "0x%08X, 0x%08X, 0x%08X, 0x%08X\n", + dma_reason[0], dma_reason[1], + dma_reason[2], dma_reason[3]); + bcm43xx_controller_restart(bcm, "DMA error"); + bcm43xx_unlock_mmio(bcm, flags); + return; + } + if (unlikely((dma_reason[0] & BCM43xx_DMAIRQ_NONFATALMASK) | + (dma_reason[1] & BCM43xx_DMAIRQ_NONFATALMASK) | + (dma_reason[2] & BCM43xx_DMAIRQ_NONFATALMASK) | + (dma_reason[3] & BCM43xx_DMAIRQ_NONFATALMASK))) { + printkl(KERN_ERR PFX "DMA error: " + "0x%08X, 0x%08X, 0x%08X, 0x%08X\n", + dma_reason[0], dma_reason[1], + dma_reason[2], dma_reason[3]); + } + + if (reason & BCM43xx_IRQ_PS) { + handle_irq_ps(bcm); + bcmirq_handled(BCM43xx_IRQ_PS); + } + + if (reason & BCM43xx_IRQ_REG124) { + handle_irq_reg124(bcm); + bcmirq_handled(BCM43xx_IRQ_REG124); + } + + if (reason & BCM43xx_IRQ_BEACON) { + if (bcm->ieee->iw_mode == IW_MODE_MASTER) + handle_irq_beacon(bcm); + bcmirq_handled(BCM43xx_IRQ_BEACON); + } + + if (reason & BCM43xx_IRQ_PMQ) { + handle_irq_pmq(bcm); + bcmirq_handled(BCM43xx_IRQ_PMQ); + } + + if (reason & BCM43xx_IRQ_SCAN) { + /*TODO*/ + //bcmirq_handled(BCM43xx_IRQ_SCAN); + } + + if (reason & BCM43xx_IRQ_NOISE) { + handle_irq_noise(bcm); + bcmirq_handled(BCM43xx_IRQ_NOISE); + } + + /* Check the DMA reason registers for received data. */ + assert(!(dma_reason[1] & BCM43xx_DMAIRQ_RX_DONE)); + assert(!(dma_reason[2] & BCM43xx_DMAIRQ_RX_DONE)); + if (dma_reason[0] & BCM43xx_DMAIRQ_RX_DONE) { + if (bcm43xx_using_pio(bcm)) + bcm43xx_pio_rx(bcm43xx_current_pio(bcm)->queue0); + else + bcm43xx_dma_rx(bcm43xx_current_dma(bcm)->rx_ring0); + /* We intentionally don't set "activity" to 1, here. */ + } + if (dma_reason[3] & BCM43xx_DMAIRQ_RX_DONE) { + if (bcm43xx_using_pio(bcm)) + bcm43xx_pio_rx(bcm43xx_current_pio(bcm)->queue3); + else + bcm43xx_dma_rx(bcm43xx_current_dma(bcm)->rx_ring1); + activity = 1; + } + bcmirq_handled(BCM43xx_IRQ_RX); + + if (reason & BCM43xx_IRQ_XMIT_STATUS) { + handle_irq_transmit_status(bcm); + activity = 1; + //TODO: In AP mode, this also causes sending of powersave responses. + bcmirq_handled(BCM43xx_IRQ_XMIT_STATUS); + } + + /* IRQ_PIO_WORKAROUND is handled in the top-half. */ + bcmirq_handled(BCM43xx_IRQ_PIO_WORKAROUND); +#ifdef CONFIG_BCM43XX_DEBUG + if (unlikely(reason & ~_handled)) { + printkl(KERN_WARNING PFX + "Unhandled IRQ! Reason: 0x%08x, Unhandled: 0x%08x, " + "DMA: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", + reason, (reason & ~_handled), + dma_reason[0], dma_reason[1], + dma_reason[2], dma_reason[3]); + } +#endif +#undef bcmirq_handled + + if (!modparam_noleds) + bcm43xx_leds_update(bcm, activity); + bcm43xx_interrupt_enable(bcm, bcm->irq_savedstate); + bcm43xx_unlock_mmio(bcm, flags); +} + +static void pio_irq_workaround(struct bcm43xx_private *bcm, + u16 base, int queueidx) +{ + u16 rxctl; + + rxctl = bcm43xx_read16(bcm, base + BCM43xx_PIO_RXCTL); + if (rxctl & BCM43xx_PIO_RXCTL_DATAAVAILABLE) + bcm->dma_reason[queueidx] |= BCM43xx_DMAIRQ_RX_DONE; + else + bcm->dma_reason[queueidx] &= ~BCM43xx_DMAIRQ_RX_DONE; +} + +static void bcm43xx_interrupt_ack(struct bcm43xx_private *bcm, u32 reason) +{ + if (bcm43xx_using_pio(bcm) && + (bcm->current_core->rev < 3) && + (!(reason & BCM43xx_IRQ_PIO_WORKAROUND))) { + /* Apply a PIO specific workaround to the dma_reasons */ + pio_irq_workaround(bcm, BCM43xx_MMIO_PIO1_BASE, 0); + pio_irq_workaround(bcm, BCM43xx_MMIO_PIO2_BASE, 1); + pio_irq_workaround(bcm, BCM43xx_MMIO_PIO3_BASE, 2); + pio_irq_workaround(bcm, BCM43xx_MMIO_PIO4_BASE, 3); + } + + bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, reason); + + bcm43xx_write32(bcm, BCM43xx_MMIO_DMA1_REASON, + bcm->dma_reason[0]); + bcm43xx_write32(bcm, BCM43xx_MMIO_DMA2_REASON, + bcm->dma_reason[1]); + bcm43xx_write32(bcm, BCM43xx_MMIO_DMA3_REASON, + bcm->dma_reason[2]); + bcm43xx_write32(bcm, BCM43xx_MMIO_DMA4_REASON, + bcm->dma_reason[3]); +} + +/* Interrupt handler top-half */ +static irqreturn_t bcm43xx_interrupt_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + irqreturn_t ret = IRQ_HANDLED; + struct bcm43xx_private *bcm = dev_id; + u32 reason; + + if (!bcm) + return IRQ_NONE; + + spin_lock(&bcm->_lock); + + reason = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); + if (reason == 0xffffffff) { + /* irq not for us (shared irq) */ + ret = IRQ_NONE; + goto out; + } + reason &= bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_MASK); + if (!reason) + goto out; + + bcm->dma_reason[0] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA1_REASON) + & 0x0001dc00; + bcm->dma_reason[1] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA2_REASON) + & 0x0000dc00; + bcm->dma_reason[2] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA3_REASON) + & 0x0000dc00; + bcm->dma_reason[3] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA4_REASON) + & 0x0001dc00; + + bcm43xx_interrupt_ack(bcm, reason); + + /* Only accept IRQs, if we are initialized properly. + * This avoids an RX race while initializing. + * We should probably not enable IRQs before we are initialized + * completely, but some careful work is needed to fix this. I think it + * is best to stay with this cheap workaround for now... . + */ + if (likely(bcm->initialized)) { + /* disable all IRQs. They are enabled again in the bottom half. */ + bcm->irq_savedstate = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); + /* save the reason code and call our bottom half. */ + bcm->irq_reason = reason; + tasklet_schedule(&bcm->isr_tasklet); + } + +out: + mmiowb(); + spin_unlock(&bcm->_lock); + + return ret; +} + +static void bcm43xx_release_firmware(struct bcm43xx_private *bcm, int force) +{ + if (bcm->firmware_norelease && !force) + return; /* Suspending or controller reset. */ + release_firmware(bcm->ucode); + bcm->ucode = NULL; + release_firmware(bcm->pcm); + bcm->pcm = NULL; + release_firmware(bcm->initvals0); + bcm->initvals0 = NULL; + release_firmware(bcm->initvals1); + bcm->initvals1 = NULL; +} + +static int bcm43xx_request_firmware(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + u8 rev = bcm->current_core->rev; + int err = 0; + int nr; + char buf[22 + sizeof(modparam_fwpostfix) - 1] = { 0 }; + + if (!bcm->ucode) { + snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_microcode%d%s.fw", + (rev >= 5 ? 5 : rev), + modparam_fwpostfix); + err = request_firmware(&bcm->ucode, buf, &bcm->pci_dev->dev); + if (err) { + printk(KERN_ERR PFX + "Error: Microcode \"%s\" not available or load failed.\n", + buf); + goto error; + } + } + + if (!bcm->pcm) { + snprintf(buf, ARRAY_SIZE(buf), + "bcm43xx_pcm%d%s.fw", + (rev < 5 ? 4 : 5), + modparam_fwpostfix); + err = request_firmware(&bcm->pcm, buf, &bcm->pci_dev->dev); + if (err) { + printk(KERN_ERR PFX + "Error: PCM \"%s\" not available or load failed.\n", + buf); + goto error; + } + } + + if (!bcm->initvals0) { + if (rev == 2 || rev == 4) { + switch (phy->type) { + case BCM43xx_PHYTYPE_A: + nr = 3; + break; + case BCM43xx_PHYTYPE_B: + case BCM43xx_PHYTYPE_G: + nr = 1; + break; + default: + goto err_noinitval; + } + + } else if (rev >= 5) { + switch (phy->type) { + case BCM43xx_PHYTYPE_A: + nr = 7; + break; + case BCM43xx_PHYTYPE_B: + case BCM43xx_PHYTYPE_G: + nr = 5; + break; + default: + goto err_noinitval; + } + } else + goto err_noinitval; + snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_initval%02d%s.fw", + nr, modparam_fwpostfix); + + err = request_firmware(&bcm->initvals0, buf, &bcm->pci_dev->dev); + if (err) { + printk(KERN_ERR PFX + "Error: InitVals \"%s\" not available or load failed.\n", + buf); + goto error; + } + if (bcm->initvals0->size % sizeof(struct bcm43xx_initval)) { + printk(KERN_ERR PFX "InitVals fileformat error.\n"); + goto error; + } + } + + if (!bcm->initvals1) { + if (rev >= 5) { + u32 sbtmstatehigh; + + switch (phy->type) { + case BCM43xx_PHYTYPE_A: + sbtmstatehigh = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATEHIGH); + if (sbtmstatehigh & 0x00010000) + nr = 9; + else + nr = 10; + break; + case BCM43xx_PHYTYPE_B: + case BCM43xx_PHYTYPE_G: + nr = 6; + break; + default: + goto err_noinitval; + } + snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_initval%02d%s.fw", + nr, modparam_fwpostfix); + + err = request_firmware(&bcm->initvals1, buf, &bcm->pci_dev->dev); + if (err) { + printk(KERN_ERR PFX + "Error: InitVals \"%s\" not available or load failed.\n", + buf); + goto error; + } + if (bcm->initvals1->size % sizeof(struct bcm43xx_initval)) { + printk(KERN_ERR PFX "InitVals fileformat error.\n"); + goto error; + } + } + } + +out: + return err; +error: + bcm43xx_release_firmware(bcm, 1); + goto out; +err_noinitval: + printk(KERN_ERR PFX "Error: No InitVals available!\n"); + err = -ENOENT; + goto error; +} + +static void bcm43xx_upload_microcode(struct bcm43xx_private *bcm) +{ + const u32 *data; + unsigned int i, len; + + /* Upload Microcode. */ + data = (u32 *)(bcm->ucode->data); + len = bcm->ucode->size / sizeof(u32); + bcm43xx_shm_control_word(bcm, BCM43xx_SHM_UCODE, 0x0000); + for (i = 0; i < len; i++) { + bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_DATA, + be32_to_cpu(data[i])); + udelay(10); + } + + /* Upload PCM data. */ + data = (u32 *)(bcm->pcm->data); + len = bcm->pcm->size / sizeof(u32); + bcm43xx_shm_control_word(bcm, BCM43xx_SHM_PCM, 0x01ea); + bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_DATA, 0x00004000); + bcm43xx_shm_control_word(bcm, BCM43xx_SHM_PCM, 0x01eb); + for (i = 0; i < len; i++) { + bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_DATA, + be32_to_cpu(data[i])); + udelay(10); + } +} + +static int bcm43xx_write_initvals(struct bcm43xx_private *bcm, + const struct bcm43xx_initval *data, + const unsigned int len) +{ + u16 offset, size; + u32 value; + unsigned int i; + + for (i = 0; i < len; i++) { + offset = be16_to_cpu(data[i].offset); + size = be16_to_cpu(data[i].size); + value = be32_to_cpu(data[i].value); + + if (unlikely(offset >= 0x1000)) + goto err_format; + if (size == 2) { + if (unlikely(value & 0xFFFF0000)) + goto err_format; + bcm43xx_write16(bcm, offset, (u16)value); + } else if (size == 4) { + bcm43xx_write32(bcm, offset, value); + } else + goto err_format; + } + + return 0; + +err_format: + printk(KERN_ERR PFX "InitVals (bcm43xx_initvalXX.fw) file-format error. " + "Please fix your bcm43xx firmware files.\n"); + return -EPROTO; +} + +static int bcm43xx_upload_initvals(struct bcm43xx_private *bcm) +{ + int err; + + err = bcm43xx_write_initvals(bcm, (struct bcm43xx_initval *)bcm->initvals0->data, + bcm->initvals0->size / sizeof(struct bcm43xx_initval)); + if (err) + goto out; + if (bcm->initvals1) { + err = bcm43xx_write_initvals(bcm, (struct bcm43xx_initval *)bcm->initvals1->data, + bcm->initvals1->size / sizeof(struct bcm43xx_initval)); + if (err) + goto out; + } +out: + return err; +} + +static int bcm43xx_initialize_irq(struct bcm43xx_private *bcm) +{ + int res; + unsigned int i; + u32 data; + + bcm->irq = bcm->pci_dev->irq; +#ifdef CONFIG_BCM947XX + if (bcm->pci_dev->bus->number == 0) { + struct pci_dev *d = NULL; + /* FIXME: we will probably need more device IDs here... */ + d = pci_find_device(PCI_VENDOR_ID_BROADCOM, 0x4324, NULL); + if (d != NULL) { + bcm->irq = d->irq; + } + } +#endif + res = request_irq(bcm->irq, bcm43xx_interrupt_handler, + SA_SHIRQ, KBUILD_MODNAME, bcm); + if (res) { + printk(KERN_ERR PFX "Cannot register IRQ%d\n", bcm->irq); + return -ENODEV; + } + bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, 0xffffffff); + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, 0x00020402); + i = 0; + while (1) { + data = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); + if (data == BCM43xx_IRQ_READY) + break; + i++; + if (i >= BCM43xx_IRQWAIT_MAX_RETRIES) { + printk(KERN_ERR PFX "Card IRQ register not responding. " + "Giving up.\n"); + free_irq(bcm->irq, bcm); + return -ENODEV; + } + udelay(10); + } + // dummy read + bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); + + return 0; +} + +/* Switch to the core used to write the GPIO register. + * This is either the ChipCommon, or the PCI core. + */ +static int switch_to_gpio_core(struct bcm43xx_private *bcm) +{ + int err; + + /* Where to find the GPIO register depends on the chipset. + * If it has a ChipCommon, its register at offset 0x6c is the GPIO + * control register. Otherwise the register at offset 0x6c in the + * PCI core is the GPIO control register. + */ + err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon); + if (err == -ENODEV) { + err = bcm43xx_switch_core(bcm, &bcm->core_pci); + if (unlikely(err == -ENODEV)) { + printk(KERN_ERR PFX "gpio error: " + "Neither ChipCommon nor PCI core available!\n"); + } + } + + return err; +} + +/* Initialize the GPIOs + * http://bcm-specs.sipsolutions.net/GPIO + */ +static int bcm43xx_gpio_init(struct bcm43xx_private *bcm) +{ + struct bcm43xx_coreinfo *old_core; + int err; + u32 mask, set; + + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, + bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD) + & 0xFFFF3FFF); + + bcm43xx_leds_switch_all(bcm, 0); + bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_MASK, + bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_MASK) | 0x000F); + + mask = 0x0000001F; + set = 0x0000000F; + if (bcm->chip_id == 0x4301) { + mask |= 0x0060; + set |= 0x0060; + } + if (0 /* FIXME: conditional unknown */) { + bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_MASK, + bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_MASK) + | 0x0100); + mask |= 0x0180; + set |= 0x0180; + } + if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL) { + bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_MASK, + bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_MASK) + | 0x0200); + mask |= 0x0200; + set |= 0x0200; + } + if (bcm->current_core->rev >= 2) + mask |= 0x0010; /* FIXME: This is redundant. */ + + old_core = bcm->current_core; + err = switch_to_gpio_core(bcm); + if (err) + goto out; + bcm43xx_write32(bcm, BCM43xx_GPIO_CONTROL, + (bcm43xx_read32(bcm, BCM43xx_GPIO_CONTROL) & mask) | set); + err = bcm43xx_switch_core(bcm, old_core); +out: + return err; +} + +/* Turn off all GPIO stuff. Call this on module unload, for example. */ +static int bcm43xx_gpio_cleanup(struct bcm43xx_private *bcm) +{ + struct bcm43xx_coreinfo *old_core; + int err; + + old_core = bcm->current_core; + err = switch_to_gpio_core(bcm); + if (err) + return err; + bcm43xx_write32(bcm, BCM43xx_GPIO_CONTROL, 0x00000000); + err = bcm43xx_switch_core(bcm, old_core); + assert(err == 0); + + return 0; +} + +/* http://bcm-specs.sipsolutions.net/EnableMac */ +void bcm43xx_mac_enable(struct bcm43xx_private *bcm) +{ + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, + bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD) + | BCM43xx_SBF_MAC_ENABLED); + bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, BCM43xx_IRQ_READY); + bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* dummy read */ + bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */ + bcm43xx_power_saving_ctl_bits(bcm, -1, -1); +} + +/* http://bcm-specs.sipsolutions.net/SuspendMAC */ +void bcm43xx_mac_suspend(struct bcm43xx_private *bcm) +{ + int i; + u32 tmp; + + bcm43xx_power_saving_ctl_bits(bcm, -1, 1); + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, + bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD) + & ~BCM43xx_SBF_MAC_ENABLED); + bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */ + for (i = 100000; i; i--) { + tmp = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); + if (tmp & BCM43xx_IRQ_READY) + return; + udelay(10); + } + printkl(KERN_ERR PFX "MAC suspend failed\n"); +} + +void bcm43xx_set_iwmode(struct bcm43xx_private *bcm, + int iw_mode) +{ + unsigned long flags; + struct net_device *net_dev = bcm->net_dev; + u32 status; + u16 value; + + spin_lock_irqsave(&bcm->ieee->lock, flags); + bcm->ieee->iw_mode = iw_mode; + spin_unlock_irqrestore(&bcm->ieee->lock, flags); + if (iw_mode == IW_MODE_MONITOR) + net_dev->type = ARPHRD_IEEE80211; + else + net_dev->type = ARPHRD_ETHER; + + status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); + /* Reset status to infrastructured mode */ + status &= ~(BCM43xx_SBF_MODE_AP | BCM43xx_SBF_MODE_MONITOR); + status &= ~BCM43xx_SBF_MODE_PROMISC; + status |= BCM43xx_SBF_MODE_NOTADHOC; + +/* FIXME: Always enable promisc mode, until we get the MAC filters working correctly. */ +status |= BCM43xx_SBF_MODE_PROMISC; + + switch (iw_mode) { + case IW_MODE_MONITOR: + status |= BCM43xx_SBF_MODE_MONITOR; + status |= BCM43xx_SBF_MODE_PROMISC; + break; + case IW_MODE_ADHOC: + status &= ~BCM43xx_SBF_MODE_NOTADHOC; + break; + case IW_MODE_MASTER: + status |= BCM43xx_SBF_MODE_AP; + break; + case IW_MODE_SECOND: + case IW_MODE_REPEAT: + TODO(); /* TODO */ + break; + case IW_MODE_INFRA: + /* nothing to be done here... */ + break; + default: + dprintk(KERN_ERR PFX "Unknown mode in set_iwmode: %d\n", iw_mode); + } + if (net_dev->flags & IFF_PROMISC) + status |= BCM43xx_SBF_MODE_PROMISC; + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status); + + value = 0x0002; + if (iw_mode != IW_MODE_ADHOC && iw_mode != IW_MODE_MASTER) { + if (bcm->chip_id == 0x4306 && bcm->chip_rev == 3) + value = 0x0064; + else + value = 0x0032; + } + bcm43xx_write16(bcm, 0x0612, value); +} + +/* This is the opposite of bcm43xx_chip_init() */ +static void bcm43xx_chip_cleanup(struct bcm43xx_private *bcm) +{ + bcm43xx_radio_turn_off(bcm); + if (!modparam_noleds) + bcm43xx_leds_exit(bcm); + bcm43xx_gpio_cleanup(bcm); + free_irq(bcm->irq, bcm); + bcm43xx_release_firmware(bcm, 0); +} + +/* Initialize the chip + * http://bcm-specs.sipsolutions.net/ChipInit + */ +static int bcm43xx_chip_init(struct bcm43xx_private *bcm) +{ + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + int err; + int tmp; + u32 value32; + u16 value16; + + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, + BCM43xx_SBF_CORE_READY + | BCM43xx_SBF_400); + + err = bcm43xx_request_firmware(bcm); + if (err) + goto out; + bcm43xx_upload_microcode(bcm); + + err = bcm43xx_initialize_irq(bcm); + if (err) + goto err_release_fw; + + err = bcm43xx_gpio_init(bcm); + if (err) + goto err_free_irq; + + err = bcm43xx_upload_initvals(bcm); + if (err) + goto err_gpio_cleanup; + bcm43xx_radio_turn_on(bcm); + + bcm43xx_write16(bcm, 0x03E6, 0x0000); + err = bcm43xx_phy_init(bcm); + if (err) + goto err_radio_off; + + /* Select initial Interference Mitigation. */ + tmp = radio->interfmode; + radio->interfmode = BCM43xx_RADIO_INTERFMODE_NONE; + bcm43xx_radio_set_interference_mitigation(bcm, tmp); + + bcm43xx_phy_set_antenna_diversity(bcm); + bcm43xx_radio_set_txantenna(bcm, BCM43xx_RADIO_TXANTENNA_DEFAULT); + if (phy->type == BCM43xx_PHYTYPE_B) { + value16 = bcm43xx_read16(bcm, 0x005E); + value16 |= 0x0004; + bcm43xx_write16(bcm, 0x005E, value16); + } + bcm43xx_write32(bcm, 0x0100, 0x01000000); + if (bcm->current_core->rev < 5) + bcm43xx_write32(bcm, 0x010C, 0x01000000); + + value32 = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); + value32 &= ~ BCM43xx_SBF_MODE_NOTADHOC; + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, value32); + value32 = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); + value32 |= BCM43xx_SBF_MODE_NOTADHOC; + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, value32); + + value32 = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); + value32 |= 0x100000; + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, value32); + + if (bcm43xx_using_pio(bcm)) { + bcm43xx_write32(bcm, 0x0210, 0x00000100); + bcm43xx_write32(bcm, 0x0230, 0x00000100); + bcm43xx_write32(bcm, 0x0250, 0x00000100); + bcm43xx_write32(bcm, 0x0270, 0x00000100); + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0034, 0x0000); + } + + /* Probe Response Timeout value */ + /* FIXME: Default to 0, has to be set by ioctl probably... :-/ */ + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0074, 0x0000); + + /* Initially set the wireless operation mode. */ + bcm43xx_set_iwmode(bcm, bcm->ieee->iw_mode); + + if (bcm->current_core->rev < 3) { + bcm43xx_write16(bcm, 0x060E, 0x0000); + bcm43xx_write16(bcm, 0x0610, 0x8000); + bcm43xx_write16(bcm, 0x0604, 0x0000); + bcm43xx_write16(bcm, 0x0606, 0x0200); + } else { + bcm43xx_write32(bcm, 0x0188, 0x80000000); + bcm43xx_write32(bcm, 0x018C, 0x02000000); + } + bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, 0x00004000); + bcm43xx_write32(bcm, BCM43xx_MMIO_DMA1_IRQ_MASK, 0x0001DC00); + bcm43xx_write32(bcm, BCM43xx_MMIO_DMA2_IRQ_MASK, 0x0000DC00); + bcm43xx_write32(bcm, BCM43xx_MMIO_DMA3_IRQ_MASK, 0x0000DC00); + bcm43xx_write32(bcm, BCM43xx_MMIO_DMA4_IRQ_MASK, 0x0001DC00); + + value32 = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); + value32 |= 0x00100000; + bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, value32); + + bcm43xx_write16(bcm, BCM43xx_MMIO_POWERUP_DELAY, bcm43xx_pctl_powerup_delay(bcm)); + + assert(err == 0); + dprintk(KERN_INFO PFX "Chip initialized\n"); +out: + return err; + +err_radio_off: + bcm43xx_radio_turn_off(bcm); +err_gpio_cleanup: + bcm43xx_gpio_cleanup(bcm); +err_free_irq: + free_irq(bcm->irq, bcm); +err_release_fw: + bcm43xx_release_firmware(bcm, 1); + goto out; +} + +/* Validate chip access + * http://bcm-specs.sipsolutions.net/ValidateChipAccess */ +static int bcm43xx_validate_chip(struct bcm43xx_private *bcm) +{ + u32 value; + u32 shm_backup; + + shm_backup = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0000); + bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, 0x0000, 0xAA5555AA); + if (bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0000) != 0xAA5555AA) + goto error; + bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, 0x0000, 0x55AAAA55); + if (bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0000) != 0x55AAAA55) + goto error; + bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, 0x0000, shm_backup); + + value = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); + if ((value | 0x80000000) != 0x80000400) + goto error; + + value = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); + if (value != 0x00000000) + goto error; + + return 0; +error: + printk(KERN_ERR PFX "Failed to validate the chipaccess\n"); + return -ENODEV; +} + +static void bcm43xx_init_struct_phyinfo(struct bcm43xx_phyinfo *phy) +{ + /* Initialize a "phyinfo" structure. The structure is already + * zeroed out. + */ + phy->antenna_diversity = 0xFFFF; + phy->savedpctlreg = 0xFFFF; + phy->minlowsig[0] = 0xFFFF; + phy->minlowsig[1] = 0xFFFF; + spin_lock_init(&phy->lock); +} + +static void bcm43xx_init_struct_radioinfo(struct bcm43xx_radioinfo *radio) +{ + /* Initialize a "radioinfo" structure. The structure is already + * zeroed out. + */ + radio->interfmode = BCM43xx_RADIO_INTERFMODE_NONE; + radio->channel = 0xFF; + radio->initial_channel = 0xFF; + radio->lofcal = 0xFFFF; + radio->initval = 0xFFFF; + radio->nrssi[0] = -1000; + radio->nrssi[1] = -1000; +} + +static int bcm43xx_probe_cores(struct bcm43xx_private *bcm) +{ + int err, i; + int current_core; + u32 core_vendor, core_id, core_rev; + u32 sb_id_hi, chip_id_32 = 0; + u16 pci_device, chip_id_16; + u8 core_count; + + memset(&bcm->core_chipcommon, 0, sizeof(struct bcm43xx_coreinfo)); + memset(&bcm->core_pci, 0, sizeof(struct bcm43xx_coreinfo)); + memset(&bcm->core_80211, 0, sizeof(struct bcm43xx_coreinfo) + * BCM43xx_MAX_80211_CORES); + memset(&bcm->core_80211_ext, 0, sizeof(struct bcm43xx_coreinfo_80211) + * BCM43xx_MAX_80211_CORES); + bcm->current_80211_core_idx = -1; + bcm->nr_80211_available = 0; + bcm->current_core = NULL; + bcm->active_80211_core = NULL; + + /* map core 0 */ + err = _switch_core(bcm, 0); + if (err) + goto out; + + /* fetch sb_id_hi from core information registers */ + sb_id_hi = bcm43xx_read32(bcm, BCM43xx_CIR_SB_ID_HI); + + core_id = (sb_id_hi & 0xFFF0) >> 4; + core_rev = (sb_id_hi & 0xF); + core_vendor = (sb_id_hi & 0xFFFF0000) >> 16; + + /* if present, chipcommon is always core 0; read the chipid from it */ + if (core_id == BCM43xx_COREID_CHIPCOMMON) { + chip_id_32 = bcm43xx_read32(bcm, 0); + chip_id_16 = chip_id_32 & 0xFFFF; + bcm->core_chipcommon.available = 1; + bcm->core_chipcommon.id = core_id; + bcm->core_chipcommon.rev = core_rev; + bcm->core_chipcommon.index = 0; + /* While we are at it, also read the capabilities. */ + bcm->chipcommon_capabilities = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_CAPABILITIES); + } else { + /* without a chipCommon, use a hard coded table. */ + pci_device = bcm->pci_dev->device; + if (pci_device == 0x4301) + chip_id_16 = 0x4301; + else if ((pci_device >= 0x4305) && (pci_device <= 0x4307)) + chip_id_16 = 0x4307; + else if ((pci_device >= 0x4402) && (pci_device <= 0x4403)) + chip_id_16 = 0x4402; + else if ((pci_device >= 0x4610) && (pci_device <= 0x4615)) + chip_id_16 = 0x4610; + else if ((pci_device >= 0x4710) && (pci_device <= 0x4715)) + chip_id_16 = 0x4710; +#ifdef CONFIG_BCM947XX + else if ((pci_device >= 0x4320) && (pci_device <= 0x4325)) + chip_id_16 = 0x4309; +#endif + else { + printk(KERN_ERR PFX "Could not determine Chip ID\n"); + return -ENODEV; + } + } + + /* ChipCommon with Core Rev >=4 encodes number of cores, + * otherwise consult hardcoded table */ + if ((core_id == BCM43xx_COREID_CHIPCOMMON) && (core_rev >= 4)) { + core_count = (chip_id_32 & 0x0F000000) >> 24; + } else { + switch (chip_id_16) { + case 0x4610: + case 0x4704: + case 0x4710: + core_count = 9; + break; + case 0x4310: + core_count = 8; + break; + case 0x5365: + core_count = 7; + break; + case 0x4306: + core_count = 6; + break; + case 0x4301: + case 0x4307: + core_count = 5; + break; + case 0x4402: + core_count = 3; + break; + default: + /* SOL if we get here */ + assert(0); + core_count = 1; + } + } + + bcm->chip_id = chip_id_16; + bcm->chip_rev = (chip_id_32 & 0x000F0000) >> 16; + bcm->chip_package = (chip_id_32 & 0x00F00000) >> 20; + + dprintk(KERN_INFO PFX "Chip ID 0x%x, rev 0x%x\n", + bcm->chip_id, bcm->chip_rev); + dprintk(KERN_INFO PFX "Number of cores: %d\n", core_count); + if (bcm->core_chipcommon.available) { + dprintk(KERN_INFO PFX "Core 0: ID 0x%x, rev 0x%x, vendor 0x%x, %s\n", + core_id, core_rev, core_vendor, + bcm43xx_core_enabled(bcm) ? "enabled" : "disabled"); + } + + if (bcm->core_chipcommon.available) + current_core = 1; + else + current_core = 0; + for ( ; current_core < core_count; current_core++) { + struct bcm43xx_coreinfo *core; + struct bcm43xx_coreinfo_80211 *ext_80211; + + err = _switch_core(bcm, current_core); + if (err) + goto out; + /* Gather information */ + /* fetch sb_id_hi from core information registers */ + sb_id_hi = bcm43xx_read32(bcm, BCM43xx_CIR_SB_ID_HI); + + /* extract core_id, core_rev, core_vendor */ + core_id = (sb_id_hi & 0xFFF0) >> 4; + core_rev = (sb_id_hi & 0xF); + core_vendor = (sb_id_hi & 0xFFFF0000) >> 16; + + dprintk(KERN_INFO PFX "Core %d: ID 0x%x, rev 0x%x, vendor 0x%x, %s\n", + current_core, core_id, core_rev, core_vendor, + bcm43xx_core_enabled(bcm) ? "enabled" : "disabled" ); + + core = NULL; + switch (core_id) { + case BCM43xx_COREID_PCI: + core = &bcm->core_pci; + if (core->available) { + printk(KERN_WARNING PFX "Multiple PCI cores found.\n"); + continue; + } + break; + case BCM43xx_COREID_80211: + for (i = 0; i < BCM43xx_MAX_80211_CORES; i++) { + core = &(bcm->core_80211[i]); + ext_80211 = &(bcm->core_80211_ext[i]); + if (!core->available) + break; + core = NULL; + } + if (!core) { + printk(KERN_WARNING PFX "More than %d cores of type 802.11 found.\n", + BCM43xx_MAX_80211_CORES); + continue; + } + if (i != 0) { + /* More than one 80211 core is only supported + * by special chips. + * There are chips with two 80211 cores, but with + * dangling pins on the second core. Be careful + * and ignore these cores here. + */ + if (bcm->pci_dev->device != 0x4324) { + dprintk(KERN_INFO PFX "Ignoring additional 802.11 core.\n"); + continue; + } + } + switch (core_rev) { + case 2: + case 4: + case 5: + case 6: + case 7: + case 9: + break; + default: + printk(KERN_ERR PFX "Error: Unsupported 80211 core revision %u\n", + core_rev); + err = -ENODEV; + goto out; + } + bcm->nr_80211_available++; + bcm43xx_init_struct_phyinfo(&ext_80211->phy); + bcm43xx_init_struct_radioinfo(&ext_80211->radio); + break; + case BCM43xx_COREID_CHIPCOMMON: + printk(KERN_WARNING PFX "Multiple CHIPCOMMON cores found.\n"); + break; + } + if (core) { + core->available = 1; + core->id = core_id; + core->rev = core_rev; + core->index = current_core; + } + } + + if (!bcm->core_80211[0].available) { + printk(KERN_ERR PFX "Error: No 80211 core found!\n"); + err = -ENODEV; + goto out; + } + + err = bcm43xx_switch_core(bcm, &bcm->core_80211[0]); + + assert(err == 0); +out: + return err; +} + +static void bcm43xx_gen_bssid(struct bcm43xx_private *bcm) +{ + const u8 *mac = (const u8*)(bcm->net_dev->dev_addr); + u8 *bssid = bcm->ieee->bssid; + + switch (bcm->ieee->iw_mode) { + case IW_MODE_ADHOC: + random_ether_addr(bssid); + break; + case IW_MODE_MASTER: + case IW_MODE_INFRA: + case IW_MODE_REPEAT: + case IW_MODE_SECOND: + case IW_MODE_MONITOR: + memcpy(bssid, mac, ETH_ALEN); + break; + default: + assert(0); + } +} + +static void bcm43xx_rate_memory_write(struct bcm43xx_private *bcm, + u16 rate, + int is_ofdm) +{ + u16 offset; + + if (is_ofdm) { + offset = 0x480; + offset += (bcm43xx_plcp_get_ratecode_ofdm(rate) & 0x000F) * 2; + } + else { + offset = 0x4C0; + offset += (bcm43xx_plcp_get_ratecode_cck(rate) & 0x000F) * 2; + } + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, offset + 0x20, + bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, offset)); +} + +static void bcm43xx_rate_memory_init(struct bcm43xx_private *bcm) +{ + switch (bcm43xx_current_phy(bcm)->type) { + case BCM43xx_PHYTYPE_A: + case BCM43xx_PHYTYPE_G: + bcm43xx_rate_memory_write(bcm, IEEE80211_OFDM_RATE_6MB, 1); + bcm43xx_rate_memory_write(bcm, IEEE80211_OFDM_RATE_12MB, 1); + bcm43xx_rate_memory_write(bcm, IEEE80211_OFDM_RATE_18MB, 1); + bcm43xx_rate_memory_write(bcm, IEEE80211_OFDM_RATE_24MB, 1); + bcm43xx_rate_memory_write(bcm, IEEE80211_OFDM_RATE_36MB, 1); + bcm43xx_rate_memory_write(bcm, IEEE80211_OFDM_RATE_48MB, 1); + bcm43xx_rate_memory_write(bcm, IEEE80211_OFDM_RATE_54MB, 1); + case BCM43xx_PHYTYPE_B: + bcm43xx_rate_memory_write(bcm, IEEE80211_CCK_RATE_1MB, 0); + bcm43xx_rate_memory_write(bcm, IEEE80211_CCK_RATE_2MB, 0); + bcm43xx_rate_memory_write(bcm, IEEE80211_CCK_RATE_5MB, 0); + bcm43xx_rate_memory_write(bcm, IEEE80211_CCK_RATE_11MB, 0); + break; + default: + assert(0); + } +} + +static void bcm43xx_wireless_core_cleanup(struct bcm43xx_private *bcm) +{ + bcm43xx_chip_cleanup(bcm); + bcm43xx_pio_free(bcm); + bcm43xx_dma_free(bcm); + + bcm->current_core->initialized = 0; +} + +/* http://bcm-specs.sipsolutions.net/80211Init */ +static int bcm43xx_wireless_core_init(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + u32 ucodeflags; + int err; + u32 sbimconfiglow; + u8 limit; + + if (bcm->chip_rev < 5) { + sbimconfiglow = bcm43xx_read32(bcm, BCM43xx_CIR_SBIMCONFIGLOW); + sbimconfiglow &= ~ BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_MASK; + sbimconfiglow &= ~ BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_MASK; + if (bcm->bustype == BCM43xx_BUSTYPE_PCI) + sbimconfiglow |= 0x32; + else if (bcm->bustype == BCM43xx_BUSTYPE_SB) + sbimconfiglow |= 0x53; + else + assert(0); + bcm43xx_write32(bcm, BCM43xx_CIR_SBIMCONFIGLOW, sbimconfiglow); + } + + bcm43xx_phy_calibrate(bcm); + err = bcm43xx_chip_init(bcm); + if (err) + goto out; + + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0016, bcm->current_core->rev); + ucodeflags = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, BCM43xx_UCODEFLAGS_OFFSET); + + if (0 /*FIXME: which condition has to be used here? */) + ucodeflags |= 0x00000010; + + /* HW decryption needs to be set now */ + ucodeflags |= 0x40000000; + + if (phy->type == BCM43xx_PHYTYPE_G) { + ucodeflags |= BCM43xx_UCODEFLAG_UNKBGPHY; + if (phy->rev == 1) + ucodeflags |= BCM43xx_UCODEFLAG_UNKGPHY; + if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL) + ucodeflags |= BCM43xx_UCODEFLAG_UNKPACTRL; + } else if (phy->type == BCM43xx_PHYTYPE_B) { + ucodeflags |= BCM43xx_UCODEFLAG_UNKBGPHY; + if (phy->rev >= 2 && radio->version == 0x2050) + ucodeflags &= ~BCM43xx_UCODEFLAG_UNKGPHY; + } + + if (ucodeflags != bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, + BCM43xx_UCODEFLAGS_OFFSET)) { + bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, + BCM43xx_UCODEFLAGS_OFFSET, ucodeflags); + } + + /* Short/Long Retry Limit. + * The retry-limit is a 4-bit counter. Enforce this to avoid overflowing + * the chip-internal counter. + */ + limit = limit_value(modparam_short_retry, 0, 0xF); + bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0006, limit); + limit = limit_value(modparam_long_retry, 0, 0xF); + bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0007, limit); + + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0044, 3); + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0046, 2); + + bcm43xx_rate_memory_init(bcm); + + /* Minimum Contention Window */ + if (phy->type == BCM43xx_PHYTYPE_B) + bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0003, 0x0000001f); + else + bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0003, 0x0000000f); + /* Maximum Contention Window */ + bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0004, 0x000003ff); + + bcm43xx_gen_bssid(bcm); + bcm43xx_write_mac_bssid_templates(bcm); + + if (bcm->current_core->rev >= 5) + bcm43xx_write16(bcm, 0x043C, 0x000C); + + if (bcm43xx_using_pio(bcm)) + err = bcm43xx_pio_init(bcm); + else + err = bcm43xx_dma_init(bcm); + if (err) + goto err_chip_cleanup; + bcm43xx_write16(bcm, 0x0612, 0x0050); + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0416, 0x0050); + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0414, 0x01F4); + + bcm43xx_mac_enable(bcm); + bcm43xx_interrupt_enable(bcm, bcm->irq_savedstate); + + bcm->current_core->initialized = 1; +out: + return err; + +err_chip_cleanup: + bcm43xx_chip_cleanup(bcm); + goto out; +} + +static int bcm43xx_chipset_attach(struct bcm43xx_private *bcm) +{ + int err; + u16 pci_status; + + err = bcm43xx_pctl_set_crystal(bcm, 1); + if (err) + goto out; + bcm43xx_pci_read_config16(bcm, PCI_STATUS, &pci_status); + bcm43xx_pci_write_config16(bcm, PCI_STATUS, pci_status & ~PCI_STATUS_SIG_TARGET_ABORT); + +out: + return err; +} + +static void bcm43xx_chipset_detach(struct bcm43xx_private *bcm) +{ + bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_SLOW); + bcm43xx_pctl_set_crystal(bcm, 0); +} + +static void bcm43xx_pcicore_broadcast_value(struct bcm43xx_private *bcm, + u32 address, + u32 data) +{ + bcm43xx_write32(bcm, BCM43xx_PCICORE_BCAST_ADDR, address); + bcm43xx_write32(bcm, BCM43xx_PCICORE_BCAST_DATA, data); +} + +static int bcm43xx_pcicore_commit_settings(struct bcm43xx_private *bcm) +{ + int err; + struct bcm43xx_coreinfo *old_core; + + old_core = bcm->current_core; + err = bcm43xx_switch_core(bcm, &bcm->core_pci); + if (err) + goto out; + + bcm43xx_pcicore_broadcast_value(bcm, 0xfd8, 0x00000000); + + bcm43xx_switch_core(bcm, old_core); + assert(err == 0); +out: + return err; +} + +/* Make an I/O Core usable. "core_mask" is the bitmask of the cores to enable. + * To enable core 0, pass a core_mask of 1<<0 + */ +static int bcm43xx_setup_backplane_pci_connection(struct bcm43xx_private *bcm, + u32 core_mask) +{ + u32 backplane_flag_nr; + u32 value; + struct bcm43xx_coreinfo *old_core; + int err = 0; + + value = bcm43xx_read32(bcm, BCM43xx_CIR_SBTPSFLAG); + backplane_flag_nr = value & BCM43xx_BACKPLANE_FLAG_NR_MASK; + + old_core = bcm->current_core; + err = bcm43xx_switch_core(bcm, &bcm->core_pci); + if (err) + goto out; + + if (bcm->core_pci.rev < 6) { + value = bcm43xx_read32(bcm, BCM43xx_CIR_SBINTVEC); + value |= (1 << backplane_flag_nr); + bcm43xx_write32(bcm, BCM43xx_CIR_SBINTVEC, value); + } else { + err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCICFG_ICR, &value); + if (err) { + printk(KERN_ERR PFX "Error: ICR setup failure!\n"); + goto out_switch_back; + } + value |= core_mask << 8; + err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCICFG_ICR, value); + if (err) { + printk(KERN_ERR PFX "Error: ICR setup failure!\n"); + goto out_switch_back; + } + } + + value = bcm43xx_read32(bcm, BCM43xx_PCICORE_SBTOPCI2); + value |= BCM43xx_SBTOPCI2_PREFETCH | BCM43xx_SBTOPCI2_BURST; + bcm43xx_write32(bcm, BCM43xx_PCICORE_SBTOPCI2, value); + + if (bcm->core_pci.rev < 5) { + value = bcm43xx_read32(bcm, BCM43xx_CIR_SBIMCONFIGLOW); + value |= (2 << BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_SHIFT) + & BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_MASK; + value |= (3 << BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_SHIFT) + & BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_MASK; + bcm43xx_write32(bcm, BCM43xx_CIR_SBIMCONFIGLOW, value); + err = bcm43xx_pcicore_commit_settings(bcm); + assert(err == 0); + } + +out_switch_back: + err = bcm43xx_switch_core(bcm, old_core); +out: + return err; +} + +static void bcm43xx_softmac_init(struct bcm43xx_private *bcm) +{ + ieee80211softmac_start(bcm->net_dev); +} + +static void bcm43xx_periodic_every120sec(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + + if (phy->type != BCM43xx_PHYTYPE_G || phy->rev < 2) + return; + + bcm43xx_mac_suspend(bcm); + bcm43xx_phy_lo_g_measure(bcm); + bcm43xx_mac_enable(bcm); +} + +static void bcm43xx_periodic_every60sec(struct bcm43xx_private *bcm) +{ + bcm43xx_phy_lo_mark_all_unused(bcm); + if (bcm->sprom.boardflags & BCM43xx_BFL_RSSI) { + bcm43xx_mac_suspend(bcm); + bcm43xx_calc_nrssi_slope(bcm); + bcm43xx_mac_enable(bcm); + } +} + +static void bcm43xx_periodic_every30sec(struct bcm43xx_private *bcm) +{ + /* Update device statistics. */ + bcm43xx_calculate_link_quality(bcm); +} + +static void bcm43xx_periodic_every15sec(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + + if (phy->type == BCM43xx_PHYTYPE_G) { + //TODO: update_aci_moving_average + if (radio->aci_enable && radio->aci_wlan_automatic) { + bcm43xx_mac_suspend(bcm); + if (!radio->aci_enable && 1 /*TODO: not scanning? */) { + if (0 /*TODO: bunch of conditions*/) { + bcm43xx_radio_set_interference_mitigation(bcm, + BCM43xx_RADIO_INTERFMODE_MANUALWLAN); + } + } else if (1/*TODO*/) { + /* + if ((aci_average > 1000) && !(bcm43xx_radio_aci_scan(bcm))) { + bcm43xx_radio_set_interference_mitigation(bcm, + BCM43xx_RADIO_INTERFMODE_NONE); + } + */ + } + bcm43xx_mac_enable(bcm); + } else if (radio->interfmode == BCM43xx_RADIO_INTERFMODE_NONWLAN && + phy->rev == 1) { + //TODO: implement rev1 workaround + } + } + bcm43xx_phy_xmitpower(bcm); //FIXME: unless scanning? + //TODO for APHY (temperature?) +} + +static void bcm43xx_periodic_task_handler(unsigned long d) +{ + struct bcm43xx_private *bcm = (struct bcm43xx_private *)d; + unsigned long flags; + unsigned int state; + + bcm43xx_lock_mmio(bcm, flags); + + assert(bcm->initialized); + state = bcm->periodic_state; + if (state % 8 == 0) + bcm43xx_periodic_every120sec(bcm); + if (state % 4 == 0) + bcm43xx_periodic_every60sec(bcm); + if (state % 2 == 0) + bcm43xx_periodic_every30sec(bcm); + bcm43xx_periodic_every15sec(bcm); + bcm->periodic_state = state + 1; + + mod_timer(&bcm->periodic_tasks, jiffies + (HZ * 15)); + + bcm43xx_unlock_mmio(bcm, flags); +} + +static void bcm43xx_periodic_tasks_delete(struct bcm43xx_private *bcm) +{ + del_timer_sync(&bcm->periodic_tasks); +} + +static void bcm43xx_periodic_tasks_setup(struct bcm43xx_private *bcm) +{ + struct timer_list *timer = &(bcm->periodic_tasks); + + assert(bcm->initialized); + setup_timer(timer, + bcm43xx_periodic_task_handler, + (unsigned long)bcm); + timer->expires = jiffies; + add_timer(timer); +} + +static void bcm43xx_security_init(struct bcm43xx_private *bcm) +{ + bcm->security_offset = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, + 0x0056) * 2; + bcm43xx_clear_keys(bcm); +} + +/* This is the opposite of bcm43xx_init_board() */ +static void bcm43xx_free_board(struct bcm43xx_private *bcm) +{ + int i, err; + unsigned long flags; + + bcm43xx_sysfs_unregister(bcm); + + bcm43xx_periodic_tasks_delete(bcm); + + bcm43xx_lock(bcm, flags); + bcm->initialized = 0; + bcm->shutting_down = 1; + bcm43xx_unlock(bcm, flags); + + for (i = 0; i < BCM43xx_MAX_80211_CORES; i++) { + if (!bcm->core_80211[i].available) + continue; + if (!bcm->core_80211[i].initialized) + continue; + + err = bcm43xx_switch_core(bcm, &bcm->core_80211[i]); + assert(err == 0); + bcm43xx_wireless_core_cleanup(bcm); + } + + bcm43xx_pctl_set_crystal(bcm, 0); + + bcm43xx_lock(bcm, flags); + bcm->shutting_down = 0; + bcm43xx_unlock(bcm, flags); +} + +static int bcm43xx_init_board(struct bcm43xx_private *bcm) +{ + int i, err; + int connect_phy; + unsigned long flags; + + might_sleep(); + + bcm43xx_lock(bcm, flags); + bcm->initialized = 0; + bcm->shutting_down = 0; + bcm43xx_unlock(bcm, flags); + + err = bcm43xx_pctl_set_crystal(bcm, 1); + if (err) + goto out; + err = bcm43xx_pctl_init(bcm); + if (err) + goto err_crystal_off; + err = bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_FAST); + if (err) + goto err_crystal_off; + + tasklet_enable(&bcm->isr_tasklet); + for (i = 0; i < bcm->nr_80211_available; i++) { + err = bcm43xx_switch_core(bcm, &bcm->core_80211[i]); + assert(err != -ENODEV); + if (err) + goto err_80211_unwind; + + /* Enable the selected wireless core. + * Connect PHY only on the first core. + */ + if (!bcm43xx_core_enabled(bcm)) { + if (bcm->nr_80211_available == 1) { + connect_phy = bcm43xx_current_phy(bcm)->connected; + } else { + if (i == 0) + connect_phy = 1; + else + connect_phy = 0; + } + bcm43xx_wireless_core_reset(bcm, connect_phy); + } + + if (i != 0) + bcm43xx_wireless_core_mark_inactive(bcm, &bcm->core_80211[0]); + + err = bcm43xx_wireless_core_init(bcm); + if (err) + goto err_80211_unwind; + + if (i != 0) { + bcm43xx_mac_suspend(bcm); + bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); + bcm43xx_radio_turn_off(bcm); + } + } + bcm->active_80211_core = &bcm->core_80211[0]; + if (bcm->nr_80211_available >= 2) { + bcm43xx_switch_core(bcm, &bcm->core_80211[0]); + bcm43xx_mac_enable(bcm); + } + bcm43xx_macfilter_clear(bcm, BCM43xx_MACFILTER_ASSOC); + bcm43xx_macfilter_set(bcm, BCM43xx_MACFILTER_SELF, (u8 *)(bcm->net_dev->dev_addr)); + dprintk(KERN_INFO PFX "80211 cores initialized\n"); + bcm43xx_security_init(bcm); + bcm43xx_softmac_init(bcm); + + bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_DYNAMIC); + + if (bcm43xx_current_radio(bcm)->initial_channel != 0xFF) { + bcm43xx_mac_suspend(bcm); + bcm43xx_radio_selectchannel(bcm, bcm43xx_current_radio(bcm)->initial_channel, 0); + bcm43xx_mac_enable(bcm); + } + + /* Initialization of the board is done. Flag it as such. */ + bcm43xx_lock(bcm, flags); + bcm->initialized = 1; + bcm43xx_unlock(bcm, flags); + + bcm43xx_periodic_tasks_setup(bcm); + bcm43xx_sysfs_register(bcm); + //FIXME: check for bcm43xx_sysfs_register failure. This function is a bit messy regarding unwinding, though... + + assert(err == 0); +out: + return err; + +err_80211_unwind: + tasklet_disable(&bcm->isr_tasklet); + /* unwind all 80211 initialization */ + for (i = 0; i < bcm->nr_80211_available; i++) { + if (!bcm->core_80211[i].initialized) + continue; + bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); + bcm43xx_wireless_core_cleanup(bcm); + } +err_crystal_off: + bcm43xx_pctl_set_crystal(bcm, 0); + goto out; +} + +static void bcm43xx_detach_board(struct bcm43xx_private *bcm) +{ + struct pci_dev *pci_dev = bcm->pci_dev; + int i; + + bcm43xx_chipset_detach(bcm); + /* Do _not_ access the chip, after it is detached. */ + iounmap(bcm->mmio_addr); + + pci_release_regions(pci_dev); + pci_disable_device(pci_dev); + + /* Free allocated structures/fields */ + for (i = 0; i < BCM43xx_MAX_80211_CORES; i++) { + kfree(bcm->core_80211_ext[i].phy._lo_pairs); + if (bcm->core_80211_ext[i].phy.dyn_tssi_tbl) + kfree(bcm->core_80211_ext[i].phy.tssi2dbm); + } +} + +static int bcm43xx_read_phyinfo(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + u16 value; + u8 phy_version; + u8 phy_type; + u8 phy_rev; + int phy_rev_ok = 1; + void *p; + + value = bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_VER); + + phy_version = (value & 0xF000) >> 12; + phy_type = (value & 0x0F00) >> 8; + phy_rev = (value & 0x000F); + + dprintk(KERN_INFO PFX "Detected PHY: Version: %x, Type %x, Revision %x\n", + phy_version, phy_type, phy_rev); + + switch (phy_type) { + case BCM43xx_PHYTYPE_A: + if (phy_rev >= 4) + phy_rev_ok = 0; + /*FIXME: We need to switch the ieee->modulation, etc.. flags, + * if we switch 80211 cores after init is done. + * As we do not implement on the fly switching between + * wireless cores, I will leave this as a future task. + */ + bcm->ieee->modulation = IEEE80211_OFDM_MODULATION; + bcm->ieee->mode = IEEE_A; + bcm->ieee->freq_band = IEEE80211_52GHZ_BAND | + IEEE80211_24GHZ_BAND; + break; + case BCM43xx_PHYTYPE_B: + if (phy_rev != 2 && phy_rev != 4 && phy_rev != 6 && phy_rev != 7) + phy_rev_ok = 0; + bcm->ieee->modulation = IEEE80211_CCK_MODULATION; + bcm->ieee->mode = IEEE_B; + bcm->ieee->freq_band = IEEE80211_24GHZ_BAND; + break; + case BCM43xx_PHYTYPE_G: + if (phy_rev > 7) + phy_rev_ok = 0; + bcm->ieee->modulation = IEEE80211_OFDM_MODULATION | + IEEE80211_CCK_MODULATION; + bcm->ieee->mode = IEEE_G; + bcm->ieee->freq_band = IEEE80211_24GHZ_BAND; + break; + default: + printk(KERN_ERR PFX "Error: Unknown PHY Type %x\n", + phy_type); + return -ENODEV; + }; + if (!phy_rev_ok) { + printk(KERN_WARNING PFX "Invalid PHY Revision %x\n", + phy_rev); + } + + phy->version = phy_version; + phy->type = phy_type; + phy->rev = phy_rev; + if ((phy_type == BCM43xx_PHYTYPE_B) || (phy_type == BCM43xx_PHYTYPE_G)) { + p = kzalloc(sizeof(struct bcm43xx_lopair) * BCM43xx_LO_COUNT, + GFP_KERNEL); + if (!p) + return -ENOMEM; + phy->_lo_pairs = p; + } + + return 0; +} + +static int bcm43xx_attach_board(struct bcm43xx_private *bcm) +{ + struct pci_dev *pci_dev = bcm->pci_dev; + struct net_device *net_dev = bcm->net_dev; + int err; + int i; + unsigned long mmio_start, mmio_flags, mmio_len; + u32 coremask; + + err = pci_enable_device(pci_dev); + if (err) { + printk(KERN_ERR PFX "unable to wake up pci device (%i)\n", err); + goto out; + } + mmio_start = pci_resource_start(pci_dev, 0); + mmio_flags = pci_resource_flags(pci_dev, 0); + mmio_len = pci_resource_len(pci_dev, 0); + if (!(mmio_flags & IORESOURCE_MEM)) { + printk(KERN_ERR PFX + "%s, region #0 not an MMIO resource, aborting\n", + pci_name(pci_dev)); + err = -ENODEV; + goto err_pci_disable; + } + err = pci_request_regions(pci_dev, KBUILD_MODNAME); + if (err) { + printk(KERN_ERR PFX + "could not access PCI resources (%i)\n", err); + goto err_pci_disable; + } + /* enable PCI bus-mastering */ + pci_set_master(pci_dev); + bcm->mmio_addr = ioremap(mmio_start, mmio_len); + if (!bcm->mmio_addr) { + printk(KERN_ERR PFX "%s: cannot remap MMIO, aborting\n", + pci_name(pci_dev)); + err = -EIO; + goto err_pci_release; + } + bcm->mmio_len = mmio_len; + net_dev->base_addr = (unsigned long)bcm->mmio_addr; + + bcm43xx_pci_read_config16(bcm, PCI_SUBSYSTEM_VENDOR_ID, + &bcm->board_vendor); + bcm43xx_pci_read_config16(bcm, PCI_SUBSYSTEM_ID, + &bcm->board_type); + bcm43xx_pci_read_config16(bcm, PCI_REVISION_ID, + &bcm->board_revision); + + err = bcm43xx_chipset_attach(bcm); + if (err) + goto err_iounmap; + err = bcm43xx_pctl_init(bcm); + if (err) + goto err_chipset_detach; + err = bcm43xx_probe_cores(bcm); + if (err) + goto err_chipset_detach; + + /* Attach all IO cores to the backplane. */ + coremask = 0; + for (i = 0; i < bcm->nr_80211_available; i++) + coremask |= (1 << bcm->core_80211[i].index); + //FIXME: Also attach some non80211 cores? + err = bcm43xx_setup_backplane_pci_connection(bcm, coremask); + if (err) { + printk(KERN_ERR PFX "Backplane->PCI connection failed!\n"); + goto err_chipset_detach; + } + + err = bcm43xx_sprom_extract(bcm); + if (err) + goto err_chipset_detach; + err = bcm43xx_leds_init(bcm); + if (err) + goto err_chipset_detach; + + for (i = 0; i < bcm->nr_80211_available; i++) { + err = bcm43xx_switch_core(bcm, &bcm->core_80211[i]); + assert(err != -ENODEV); + if (err) + goto err_80211_unwind; + + /* Enable the selected wireless core. + * Connect PHY only on the first core. + */ + bcm43xx_wireless_core_reset(bcm, (i == 0)); + + err = bcm43xx_read_phyinfo(bcm); + if (err && (i == 0)) + goto err_80211_unwind; + + err = bcm43xx_read_radioinfo(bcm); + if (err && (i == 0)) + goto err_80211_unwind; + + err = bcm43xx_validate_chip(bcm); + if (err && (i == 0)) + goto err_80211_unwind; + + bcm43xx_radio_turn_off(bcm); + err = bcm43xx_phy_init_tssi2dbm_table(bcm); + if (err) + goto err_80211_unwind; + bcm43xx_wireless_core_disable(bcm); + } + bcm43xx_pctl_set_crystal(bcm, 0); + + /* Set the MAC address in the networking subsystem */ + if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A) + memcpy(bcm->net_dev->dev_addr, bcm->sprom.et1macaddr, 6); + else + memcpy(bcm->net_dev->dev_addr, bcm->sprom.il0macaddr, 6); + + bcm43xx_geo_init(bcm); + + snprintf(bcm->nick, IW_ESSID_MAX_SIZE, + "Broadcom %04X", bcm->chip_id); + + assert(err == 0); +out: + return err; + +err_80211_unwind: + for (i = 0; i < BCM43xx_MAX_80211_CORES; i++) { + kfree(bcm->core_80211_ext[i].phy._lo_pairs); + if (bcm->core_80211_ext[i].phy.dyn_tssi_tbl) + kfree(bcm->core_80211_ext[i].phy.tssi2dbm); + } +err_chipset_detach: + bcm43xx_chipset_detach(bcm); +err_iounmap: + iounmap(bcm->mmio_addr); +err_pci_release: + pci_release_regions(pci_dev); +err_pci_disable: + pci_disable_device(pci_dev); + goto out; +} + +/* Do the Hardware IO operations to send the txb */ +static inline int bcm43xx_tx(struct bcm43xx_private *bcm, + struct ieee80211_txb *txb) +{ + int err = -ENODEV; + + if (bcm43xx_using_pio(bcm)) + err = bcm43xx_pio_tx(bcm, txb); + else + err = bcm43xx_dma_tx(bcm, txb); + + return err; +} + +static void bcm43xx_ieee80211_set_chan(struct net_device *net_dev, + u8 channel) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + struct bcm43xx_radioinfo *radio; + unsigned long flags; + + bcm43xx_lock_mmio(bcm, flags); + if (bcm->initialized) { + bcm43xx_mac_suspend(bcm); + bcm43xx_radio_selectchannel(bcm, channel, 0); + bcm43xx_mac_enable(bcm); + } else { + radio = bcm43xx_current_radio(bcm); + radio->initial_channel = channel; + } + bcm43xx_unlock_mmio(bcm, flags); +} + +/* set_security() callback in struct ieee80211_device */ +static void bcm43xx_ieee80211_set_security(struct net_device *net_dev, + struct ieee80211_security *sec) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + struct ieee80211_security *secinfo = &bcm->ieee->sec; + unsigned long flags; + int keyidx; + + dprintk(KERN_INFO PFX "set security called\n"); + + bcm43xx_lock_mmio(bcm, flags); + + for (keyidx = 0; keyidxflags & (1<encode_alg[keyidx] = sec->encode_alg[keyidx]; + secinfo->key_sizes[keyidx] = sec->key_sizes[keyidx]; + memcpy(secinfo->keys[keyidx], sec->keys[keyidx], SCM_KEY_LEN); + } + + if (sec->flags & SEC_ACTIVE_KEY) { + secinfo->active_key = sec->active_key; + dprintk(KERN_INFO PFX " .active_key = %d\n", sec->active_key); + } + if (sec->flags & SEC_UNICAST_GROUP) { + secinfo->unicast_uses_group = sec->unicast_uses_group; + dprintk(KERN_INFO PFX " .unicast_uses_group = %d\n", sec->unicast_uses_group); + } + if (sec->flags & SEC_LEVEL) { + secinfo->level = sec->level; + dprintk(KERN_INFO PFX " .level = %d\n", sec->level); + } + if (sec->flags & SEC_ENABLED) { + secinfo->enabled = sec->enabled; + dprintk(KERN_INFO PFX " .enabled = %d\n", sec->enabled); + } + if (sec->flags & SEC_ENCRYPT) { + secinfo->encrypt = sec->encrypt; + dprintk(KERN_INFO PFX " .encrypt = %d\n", sec->encrypt); + } + if (bcm->initialized && !bcm->ieee->host_encrypt) { + if (secinfo->enabled) { + /* upload WEP keys to hardware */ + char null_address[6] = { 0 }; + u8 algorithm = 0; + for (keyidx = 0; keyidxflags & (1<encode_alg[keyidx]) { + case SEC_ALG_NONE: algorithm = BCM43xx_SEC_ALGO_NONE; break; + case SEC_ALG_WEP: + algorithm = BCM43xx_SEC_ALGO_WEP; + if (secinfo->key_sizes[keyidx] == 13) + algorithm = BCM43xx_SEC_ALGO_WEP104; + break; + case SEC_ALG_TKIP: + FIXME(); + algorithm = BCM43xx_SEC_ALGO_TKIP; + break; + case SEC_ALG_CCMP: + FIXME(); + algorithm = BCM43xx_SEC_ALGO_AES; + break; + default: + assert(0); + break; + } + bcm43xx_key_write(bcm, keyidx, algorithm, sec->keys[keyidx], secinfo->key_sizes[keyidx], &null_address[0]); + bcm->key[keyidx].enabled = 1; + bcm->key[keyidx].algorithm = algorithm; + } + } else + bcm43xx_clear_keys(bcm); + } + bcm43xx_unlock_mmio(bcm, flags); +} + +/* hard_start_xmit() callback in struct ieee80211_device */ +static int bcm43xx_ieee80211_hard_start_xmit(struct ieee80211_txb *txb, + struct net_device *net_dev, + int pri) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + int err = -ENODEV; + unsigned long flags; + + bcm43xx_lock_mmio(bcm, flags); + if (likely(bcm->initialized)) + err = bcm43xx_tx(bcm, txb); + bcm43xx_unlock_mmio(bcm, flags); + + return err; +} + +static struct net_device_stats * bcm43xx_net_get_stats(struct net_device *net_dev) +{ + return &(bcm43xx_priv(net_dev)->ieee->stats); +} + +static void bcm43xx_net_tx_timeout(struct net_device *net_dev) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + unsigned long flags; + + bcm43xx_lock_mmio(bcm, flags); + bcm43xx_controller_restart(bcm, "TX timeout"); + bcm43xx_unlock_mmio(bcm, flags); +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void bcm43xx_net_poll_controller(struct net_device *net_dev) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + unsigned long flags; + + local_irq_save(flags); + bcm43xx_interrupt_handler(bcm->irq, bcm, NULL); + local_irq_restore(flags); +} +#endif /* CONFIG_NET_POLL_CONTROLLER */ + +static int bcm43xx_net_open(struct net_device *net_dev) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + + return bcm43xx_init_board(bcm); +} + +static int bcm43xx_net_stop(struct net_device *net_dev) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + + ieee80211softmac_stop(net_dev); + bcm43xx_disable_interrupts_sync(bcm, NULL); + bcm43xx_free_board(bcm); + + return 0; +} + +static int bcm43xx_init_private(struct bcm43xx_private *bcm, + struct net_device *net_dev, + struct pci_dev *pci_dev) +{ + int err; + + bcm->ieee = netdev_priv(net_dev); + bcm->softmac = ieee80211_priv(net_dev); + bcm->softmac->set_channel = bcm43xx_ieee80211_set_chan; + + bcm->irq_savedstate = BCM43xx_IRQ_INITIAL; + bcm->pci_dev = pci_dev; + bcm->net_dev = net_dev; + bcm->bad_frames_preempt = modparam_bad_frames_preempt; + spin_lock_init(&bcm->_lock); + tasklet_init(&bcm->isr_tasklet, + (void (*)(unsigned long))bcm43xx_interrupt_tasklet, + (unsigned long)bcm); + tasklet_disable_nosync(&bcm->isr_tasklet); + if (modparam_pio) { + bcm->__using_pio = 1; + } else { + err = pci_set_dma_mask(pci_dev, DMA_30BIT_MASK); + err |= pci_set_consistent_dma_mask(pci_dev, DMA_30BIT_MASK); + if (err) { +#ifdef CONFIG_BCM43XX_PIO + printk(KERN_WARNING PFX "DMA not supported. Falling back to PIO.\n"); + bcm->__using_pio = 1; +#else + printk(KERN_ERR PFX "FATAL: DMA not supported and PIO not configured. " + "Recompile the driver with PIO support, please.\n"); + return -ENODEV; +#endif /* CONFIG_BCM43XX_PIO */ + } + } + bcm->rts_threshold = BCM43xx_DEFAULT_RTS_THRESHOLD; + + /* default to sw encryption for now */ + bcm->ieee->host_build_iv = 0; + bcm->ieee->host_encrypt = 1; + bcm->ieee->host_decrypt = 1; + + bcm->ieee->iw_mode = BCM43xx_INITIAL_IWMODE; + bcm->ieee->tx_headroom = sizeof(struct bcm43xx_txhdr); + bcm->ieee->set_security = bcm43xx_ieee80211_set_security; + bcm->ieee->hard_start_xmit = bcm43xx_ieee80211_hard_start_xmit; + + return 0; +} + +static int __devinit bcm43xx_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *net_dev; + struct bcm43xx_private *bcm; + int err; + +#ifdef CONFIG_BCM947XX + if ((pdev->bus->number == 0) && (pdev->device != 0x0800)) + return -ENODEV; +#endif + +#ifdef DEBUG_SINGLE_DEVICE_ONLY + if (strcmp(pci_name(pdev), DEBUG_SINGLE_DEVICE_ONLY)) + return -ENODEV; +#endif + + net_dev = alloc_ieee80211softmac(sizeof(*bcm)); + if (!net_dev) { + printk(KERN_ERR PFX + "could not allocate ieee80211 device %s\n", + pci_name(pdev)); + err = -ENOMEM; + goto out; + } + /* initialize the net_device struct */ + SET_MODULE_OWNER(net_dev); + SET_NETDEV_DEV(net_dev, &pdev->dev); + + net_dev->open = bcm43xx_net_open; + net_dev->stop = bcm43xx_net_stop; + net_dev->get_stats = bcm43xx_net_get_stats; + net_dev->tx_timeout = bcm43xx_net_tx_timeout; +#ifdef CONFIG_NET_POLL_CONTROLLER + net_dev->poll_controller = bcm43xx_net_poll_controller; +#endif + net_dev->wireless_handlers = &bcm43xx_wx_handlers_def; + net_dev->irq = pdev->irq; + SET_ETHTOOL_OPS(net_dev, &bcm43xx_ethtool_ops); + + /* initialize the bcm43xx_private struct */ + bcm = bcm43xx_priv(net_dev); + memset(bcm, 0, sizeof(*bcm)); + err = bcm43xx_init_private(bcm, net_dev, pdev); + if (err) + goto err_free_netdev; + + pci_set_drvdata(pdev, net_dev); + + err = bcm43xx_attach_board(bcm); + if (err) + goto err_free_netdev; + + err = register_netdev(net_dev); + if (err) { + printk(KERN_ERR PFX "Cannot register net device, " + "aborting.\n"); + err = -ENOMEM; + goto err_detach_board; + } + + bcm43xx_debugfs_add_device(bcm); + + assert(err == 0); +out: + return err; + +err_detach_board: + bcm43xx_detach_board(bcm); +err_free_netdev: + free_ieee80211softmac(net_dev); + goto out; +} + +static void __devexit bcm43xx_remove_one(struct pci_dev *pdev) +{ + struct net_device *net_dev = pci_get_drvdata(pdev); + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + + bcm43xx_debugfs_remove_device(bcm); + unregister_netdev(net_dev); + bcm43xx_detach_board(bcm); + assert(bcm->ucode == NULL); + free_ieee80211softmac(net_dev); +} + +/* Hard-reset the chip. Do not call this directly. + * Use bcm43xx_controller_restart() + */ +static void bcm43xx_chip_reset(void *_bcm) +{ + struct bcm43xx_private *bcm = _bcm; + struct net_device *net_dev = bcm->net_dev; + struct pci_dev *pci_dev = bcm->pci_dev; + int err; + int was_initialized = bcm->initialized; + + netif_stop_queue(bcm->net_dev); + tasklet_disable(&bcm->isr_tasklet); + + bcm->firmware_norelease = 1; + if (was_initialized) + bcm43xx_free_board(bcm); + bcm->firmware_norelease = 0; + bcm43xx_detach_board(bcm); + err = bcm43xx_init_private(bcm, net_dev, pci_dev); + if (err) + goto failure; + err = bcm43xx_attach_board(bcm); + if (err) + goto failure; + if (was_initialized) { + err = bcm43xx_init_board(bcm); + if (err) + goto failure; + } + netif_wake_queue(bcm->net_dev); + printk(KERN_INFO PFX "Controller restarted\n"); + + return; +failure: + printk(KERN_ERR PFX "Controller restart failed\n"); +} + +/* Hard-reset the chip. + * This can be called from interrupt or process context. + * Make sure to _not_ re-enable device interrupts after this has been called. +*/ +void bcm43xx_controller_restart(struct bcm43xx_private *bcm, const char *reason) +{ + bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); + bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* dummy read */ + printk(KERN_ERR PFX "Controller RESET (%s) ...\n", reason); + INIT_WORK(&bcm->restart_work, bcm43xx_chip_reset, bcm); + schedule_work(&bcm->restart_work); +} + +#ifdef CONFIG_PM + +static int bcm43xx_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *net_dev = pci_get_drvdata(pdev); + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + unsigned long flags; + int try_to_shutdown = 0, err; + + dprintk(KERN_INFO PFX "Suspending...\n"); + + bcm43xx_lock(bcm, flags); + bcm->was_initialized = bcm->initialized; + if (bcm->initialized) + try_to_shutdown = 1; + bcm43xx_unlock(bcm, flags); + + netif_device_detach(net_dev); + if (try_to_shutdown) { + ieee80211softmac_stop(net_dev); + err = bcm43xx_disable_interrupts_sync(bcm, &bcm->irq_savedstate); + if (unlikely(err)) { + dprintk(KERN_ERR PFX "Suspend failed.\n"); + return -EAGAIN; + } + bcm->firmware_norelease = 1; + bcm43xx_free_board(bcm); + bcm->firmware_norelease = 0; + } + bcm43xx_chipset_detach(bcm); + + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + dprintk(KERN_INFO PFX "Device suspended.\n"); + + return 0; +} + +static int bcm43xx_resume(struct pci_dev *pdev) +{ + struct net_device *net_dev = pci_get_drvdata(pdev); + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + int err = 0; + + dprintk(KERN_INFO PFX "Resuming...\n"); + + pci_set_power_state(pdev, 0); + pci_enable_device(pdev); + pci_restore_state(pdev); + + bcm43xx_chipset_attach(bcm); + if (bcm->was_initialized) { + bcm->irq_savedstate = BCM43xx_IRQ_INITIAL; + err = bcm43xx_init_board(bcm); + } + if (err) { + printk(KERN_ERR PFX "Resume failed!\n"); + return err; + } + + netif_device_attach(net_dev); + + /*FIXME: This should be handled by softmac instead. */ + schedule_work(&bcm->softmac->associnfo.work); + + dprintk(KERN_INFO PFX "Device resumed.\n"); + + return 0; +} + +#endif /* CONFIG_PM */ + +static struct pci_driver bcm43xx_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = bcm43xx_pci_tbl, + .probe = bcm43xx_init_one, + .remove = __devexit_p(bcm43xx_remove_one), +#ifdef CONFIG_PM + .suspend = bcm43xx_suspend, + .resume = bcm43xx_resume, +#endif /* CONFIG_PM */ +}; + +static int __init bcm43xx_init(void) +{ + printk(KERN_INFO KBUILD_MODNAME " driver\n"); + bcm43xx_debugfs_init(); + return pci_register_driver(&bcm43xx_pci_driver); +} + +static void __exit bcm43xx_exit(void) +{ + pci_unregister_driver(&bcm43xx_pci_driver); + bcm43xx_debugfs_exit(); +} + +module_init(bcm43xx_init) +module_exit(bcm43xx_exit) diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.h b/drivers/net/wireless/bcm43xx/bcm43xx_main.h new file mode 100644 index 000000000000..eca79a38594a --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.h @@ -0,0 +1,168 @@ +/* + + Broadcom BCM43xx wireless driver + + Copyright (c) 2005 Martin Langer , + Stefano Brivio + Michael Buesch + Danny van Dyk + Andreas Jaggi + + Some parts of the code in this file are derived from the ipw2200 + driver Copyright(c) 2003 - 2004 Intel Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#ifndef BCM43xx_MAIN_H_ +#define BCM43xx_MAIN_H_ + +#include "bcm43xx.h" + +#ifdef CONFIG_BCM947XX +#define atoi(str) simple_strtoul(((str != NULL) ? str : ""), NULL, 0) + +static inline void e_aton(char *str, char *dest) +{ + int i = 0; + u16 *d = (u16 *) dest; + + for (;;) { + dest[i++] = (char) simple_strtoul(str, NULL, 16); + str += 2; + if (!*str++ || i == 6) + break; + } + for (i = 0; i < 3; i++) + d[i] = cpu_to_be16(d[i]); +} +#endif + +#define P4D_BYT3S(magic, nr_bytes) u8 __p4dding##magic[nr_bytes] +#define P4D_BYTES(line, nr_bytes) P4D_BYT3S(line, nr_bytes) +/* Magic helper macro to pad structures. Ignore those above. It's magic. */ +#define PAD_BYTES(nr_bytes) P4D_BYTES( __LINE__ , (nr_bytes)) + + +/* Lightweight function to convert a frequency (in Mhz) to a channel number. */ +static inline +u8 bcm43xx_freq_to_channel_a(int freq) +{ + return ((freq - 5000) / 5); +} +static inline +u8 bcm43xx_freq_to_channel_bg(int freq) +{ + u8 channel; + + if (freq == 2484) + channel = 14; + else + channel = (freq - 2407) / 5; + + return channel; +} +static inline +u8 bcm43xx_freq_to_channel(struct bcm43xx_private *bcm, + int freq) +{ + if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A) + return bcm43xx_freq_to_channel_a(freq); + return bcm43xx_freq_to_channel_bg(freq); +} + +/* Lightweight function to convert a channel number to a frequency (in Mhz). */ +static inline +int bcm43xx_channel_to_freq_a(u8 channel) +{ + return (5000 + (5 * channel)); +} +static inline +int bcm43xx_channel_to_freq_bg(u8 channel) +{ + int freq; + + if (channel == 14) + freq = 2484; + else + freq = 2407 + (5 * channel); + + return freq; +} +static inline +int bcm43xx_channel_to_freq(struct bcm43xx_private *bcm, + u8 channel) +{ + if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A) + return bcm43xx_channel_to_freq_a(channel); + return bcm43xx_channel_to_freq_bg(channel); +} + +/* Lightweight function to check if a channel number is valid. + * Note that this does _NOT_ check for geographical restrictions! + */ +static inline +int bcm43xx_is_valid_channel_a(u8 channel) +{ + return (channel <= 200); +} +static inline +int bcm43xx_is_valid_channel_bg(u8 channel) +{ + return (channel >= 1 && channel <= 14); +} +static inline +int bcm43xx_is_valid_channel(struct bcm43xx_private *bcm, + u8 channel) +{ + if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A) + return bcm43xx_is_valid_channel_a(channel); + return bcm43xx_is_valid_channel_bg(channel); +} + +void bcm43xx_tsf_read(struct bcm43xx_private *bcm, u64 *tsf); +void bcm43xx_tsf_write(struct bcm43xx_private *bcm, u64 tsf); + +void bcm43xx_set_iwmode(struct bcm43xx_private *bcm, + int iw_mode); + +u32 bcm43xx_shm_read32(struct bcm43xx_private *bcm, + u16 routing, u16 offset); +u16 bcm43xx_shm_read16(struct bcm43xx_private *bcm, + u16 routing, u16 offset); +void bcm43xx_shm_write32(struct bcm43xx_private *bcm, + u16 routing, u16 offset, + u32 value); +void bcm43xx_shm_write16(struct bcm43xx_private *bcm, + u16 routing, u16 offset, + u16 value); + +void bcm43xx_dummy_transmission(struct bcm43xx_private *bcm); + +int bcm43xx_switch_core(struct bcm43xx_private *bcm, struct bcm43xx_coreinfo *new_core); + +void bcm43xx_wireless_core_reset(struct bcm43xx_private *bcm, int connect_phy); + +void bcm43xx_mac_suspend(struct bcm43xx_private *bcm); +void bcm43xx_mac_enable(struct bcm43xx_private *bcm); + +void bcm43xx_controller_restart(struct bcm43xx_private *bcm, const char *reason); + +int bcm43xx_sprom_read(struct bcm43xx_private *bcm, u16 *sprom); +int bcm43xx_sprom_write(struct bcm43xx_private *bcm, const u16 *sprom); + +#endif /* BCM43xx_MAIN_H_ */ diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c new file mode 100644 index 000000000000..0a66f43ca0c0 --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c @@ -0,0 +1,2345 @@ +/* + + Broadcom BCM43xx wireless driver + + Copyright (c) 2005 Martin Langer , + Stefano Brivio + Michael Buesch + Danny van Dyk + Andreas Jaggi + + Some parts of the code in this file are derived from the ipw2200 + driver Copyright(c) 2003 - 2004 Intel Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include +#include +#include + +#include "bcm43xx.h" +#include "bcm43xx_phy.h" +#include "bcm43xx_main.h" +#include "bcm43xx_radio.h" +#include "bcm43xx_ilt.h" +#include "bcm43xx_power.h" + + +static const s8 bcm43xx_tssi2dbm_b_table[] = { + 0x4D, 0x4C, 0x4B, 0x4A, + 0x4A, 0x49, 0x48, 0x47, + 0x47, 0x46, 0x45, 0x45, + 0x44, 0x43, 0x42, 0x42, + 0x41, 0x40, 0x3F, 0x3E, + 0x3D, 0x3C, 0x3B, 0x3A, + 0x39, 0x38, 0x37, 0x36, + 0x35, 0x34, 0x32, 0x31, + 0x30, 0x2F, 0x2D, 0x2C, + 0x2B, 0x29, 0x28, 0x26, + 0x25, 0x23, 0x21, 0x1F, + 0x1D, 0x1A, 0x17, 0x14, + 0x10, 0x0C, 0x06, 0x00, + -7, -7, -7, -7, + -7, -7, -7, -7, + -7, -7, -7, -7, +}; + +static const s8 bcm43xx_tssi2dbm_g_table[] = { + 77, 77, 77, 76, + 76, 76, 75, 75, + 74, 74, 73, 73, + 73, 72, 72, 71, + 71, 70, 70, 69, + 68, 68, 67, 67, + 66, 65, 65, 64, + 63, 63, 62, 61, + 60, 59, 58, 57, + 56, 55, 54, 53, + 52, 50, 49, 47, + 45, 43, 40, 37, + 33, 28, 22, 14, + 5, -7, -20, -20, + -20, -20, -20, -20, + -20, -20, -20, -20, +}; + +static void bcm43xx_phy_initg(struct bcm43xx_private *bcm); + + +void bcm43xx_raw_phy_lock(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + + assert(irqs_disabled()); + if (bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD) == 0x00000000) { + phy->is_locked = 0; + return; + } + if (bcm->current_core->rev < 3) { + bcm43xx_mac_suspend(bcm); + spin_lock(&phy->lock); + } else { + if (bcm->ieee->iw_mode != IW_MODE_MASTER) + bcm43xx_power_saving_ctl_bits(bcm, -1, 1); + } + phy->is_locked = 1; +} + +void bcm43xx_raw_phy_unlock(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + + assert(irqs_disabled()); + if (bcm->current_core->rev < 3) { + if (phy->is_locked) { + spin_unlock(&phy->lock); + bcm43xx_mac_enable(bcm); + } + } else { + if (bcm->ieee->iw_mode != IW_MODE_MASTER) + bcm43xx_power_saving_ctl_bits(bcm, -1, -1); + } + phy->is_locked = 0; +} + +u16 bcm43xx_phy_read(struct bcm43xx_private *bcm, u16 offset) +{ + bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_CONTROL, offset); + return bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_DATA); +} + +void bcm43xx_phy_write(struct bcm43xx_private *bcm, u16 offset, u16 val) +{ + bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_CONTROL, offset); + mmiowb(); + bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_DATA, val); +} + +void bcm43xx_phy_calibrate(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + unsigned long flags; + + bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* Dummy read. */ + if (phy->calibrated) + return; + if (phy->type == BCM43xx_PHYTYPE_G && phy->rev == 1) { + /* We do not want to be preempted while calibrating + * the hardware. + */ + local_irq_save(flags); + + bcm43xx_wireless_core_reset(bcm, 0); + bcm43xx_phy_initg(bcm); + bcm43xx_wireless_core_reset(bcm, 1); + + local_irq_restore(flags); + } + phy->calibrated = 1; +} + +/* Connect the PHY + * http://bcm-specs.sipsolutions.net/SetPHY + */ +int bcm43xx_phy_connect(struct bcm43xx_private *bcm, int connect) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + u32 flags; + + if (bcm->current_core->rev < 5) + goto out; + + flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATEHIGH); + if (connect) { + if (!(flags & 0x00010000)) + return -ENODEV; + flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); + flags |= (0x800 << 18); + bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, flags); + } else { + if (!(flags & 0x00020000)) + return -ENODEV; + flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); + flags &= ~(0x800 << 18); + bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, flags); + } +out: + phy->connected = connect; + if (connect) + dprintk(KERN_INFO PFX "PHY connected\n"); + else + dprintk(KERN_INFO PFX "PHY disconnected\n"); + + return 0; +} + +/* intialize B PHY power control + * as described in http://bcm-specs.sipsolutions.net/InitPowerControl + */ +static void bcm43xx_phy_init_pctl(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + u16 saved_batt = 0, saved_ratt = 0, saved_txctl1 = 0; + int must_reset_txpower = 0; + + assert(phy->type != BCM43xx_PHYTYPE_A); + if ((bcm->board_vendor == PCI_VENDOR_ID_BROADCOM) && + (bcm->board_type == 0x0416)) + return; + + bcm43xx_write16(bcm, 0x03E6, bcm43xx_read16(bcm, 0x03E6) & 0xFFDF); + bcm43xx_phy_write(bcm, 0x0028, 0x8018); + + if (phy->type == BCM43xx_PHYTYPE_G) { + if (!phy->connected) + return; + bcm43xx_phy_write(bcm, 0x047A, 0xC111); + } + if (phy->savedpctlreg != 0xFFFF) + return; + + if (phy->type == BCM43xx_PHYTYPE_B && + phy->rev >= 2 && + radio->version == 0x2050) { + bcm43xx_radio_write16(bcm, 0x0076, + bcm43xx_radio_read16(bcm, 0x0076) | 0x0084); + } else { + saved_batt = radio->baseband_atten; + saved_ratt = radio->radio_atten; + saved_txctl1 = radio->txctl1; + if ((radio->revision >= 6) && (radio->revision <= 8) + && /*FIXME: incomplete specs for 5 < revision < 9 */ 0) + bcm43xx_radio_set_txpower_bg(bcm, 0xB, 0x1F, 0); + else + bcm43xx_radio_set_txpower_bg(bcm, 0xB, 9, 0); + must_reset_txpower = 1; + } + bcm43xx_dummy_transmission(bcm); + + phy->savedpctlreg = bcm43xx_phy_read(bcm, BCM43xx_PHY_G_PCTL); + + if (must_reset_txpower) + bcm43xx_radio_set_txpower_bg(bcm, saved_batt, saved_ratt, saved_txctl1); + else + bcm43xx_radio_write16(bcm, 0x0076, bcm43xx_radio_read16(bcm, 0x0076) & 0xFF7B); + bcm43xx_radio_clear_tssi(bcm); +} + +static void bcm43xx_phy_agcsetup(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + u16 offset = 0x0000; + + if (phy->rev == 1) + offset = 0x4C00; + + bcm43xx_ilt_write(bcm, offset, 0x00FE); + bcm43xx_ilt_write(bcm, offset + 1, 0x000D); + bcm43xx_ilt_write(bcm, offset + 2, 0x0013); + bcm43xx_ilt_write(bcm, offset + 3, 0x0019); + + if (phy->rev == 1) { + bcm43xx_ilt_write(bcm, 0x1800, 0x2710); + bcm43xx_ilt_write(bcm, 0x1801, 0x9B83); + bcm43xx_ilt_write(bcm, 0x1802, 0x9B83); + bcm43xx_ilt_write(bcm, 0x1803, 0x0F8D); + bcm43xx_phy_write(bcm, 0x0455, 0x0004); + } + + bcm43xx_phy_write(bcm, 0x04A5, (bcm43xx_phy_read(bcm, 0x04A5) & 0x00FF) | 0x5700); + bcm43xx_phy_write(bcm, 0x041A, (bcm43xx_phy_read(bcm, 0x041A) & 0xFF80) | 0x000F); + bcm43xx_phy_write(bcm, 0x041A, (bcm43xx_phy_read(bcm, 0x041A) & 0xC07F) | 0x2B80); + bcm43xx_phy_write(bcm, 0x048C, (bcm43xx_phy_read(bcm, 0x048C) & 0xF0FF) | 0x0300); + + bcm43xx_radio_write16(bcm, 0x007A, bcm43xx_radio_read16(bcm, 0x007A) | 0x0008); + + bcm43xx_phy_write(bcm, 0x04A0, (bcm43xx_phy_read(bcm, 0x04A0) & 0xFFF0) | 0x0008); + bcm43xx_phy_write(bcm, 0x04A1, (bcm43xx_phy_read(bcm, 0x04A1) & 0xF0FF) | 0x0600); + bcm43xx_phy_write(bcm, 0x04A2, (bcm43xx_phy_read(bcm, 0x04A2) & 0xF0FF) | 0x0700); + bcm43xx_phy_write(bcm, 0x04A0, (bcm43xx_phy_read(bcm, 0x04A0) & 0xF0FF) | 0x0100); + + if (phy->rev == 1) + bcm43xx_phy_write(bcm, 0x04A2, (bcm43xx_phy_read(bcm, 0x04A2) & 0xFFF0) | 0x0007); + + bcm43xx_phy_write(bcm, 0x0488, (bcm43xx_phy_read(bcm, 0x0488) & 0xFF00) | 0x001C); + bcm43xx_phy_write(bcm, 0x0488, (bcm43xx_phy_read(bcm, 0x0488) & 0xC0FF) | 0x0200); + bcm43xx_phy_write(bcm, 0x0496, (bcm43xx_phy_read(bcm, 0x0496) & 0xFF00) | 0x001C); + bcm43xx_phy_write(bcm, 0x0489, (bcm43xx_phy_read(bcm, 0x0489) & 0xFF00) | 0x0020); + bcm43xx_phy_write(bcm, 0x0489, (bcm43xx_phy_read(bcm, 0x0489) & 0xC0FF) | 0x0200); + bcm43xx_phy_write(bcm, 0x0482, (bcm43xx_phy_read(bcm, 0x0482) & 0xFF00) | 0x002E); + bcm43xx_phy_write(bcm, 0x0496, (bcm43xx_phy_read(bcm, 0x0496) & 0x00FF) | 0x1A00); + bcm43xx_phy_write(bcm, 0x0481, (bcm43xx_phy_read(bcm, 0x0481) & 0xFF00) | 0x0028); + bcm43xx_phy_write(bcm, 0x0481, (bcm43xx_phy_read(bcm, 0x0481) & 0x00FF) | 0x2C00); + + if (phy->rev == 1) { + bcm43xx_phy_write(bcm, 0x0430, 0x092B); + bcm43xx_phy_write(bcm, 0x041B, (bcm43xx_phy_read(bcm, 0x041B) & 0xFFE1) | 0x0002); + } else { + bcm43xx_phy_write(bcm, 0x041B, bcm43xx_phy_read(bcm, 0x041B) & 0xFFE1); + bcm43xx_phy_write(bcm, 0x041F, 0x287A); + bcm43xx_phy_write(bcm, 0x0420, (bcm43xx_phy_read(bcm, 0x0420) & 0xFFF0) | 0x0004); + } + + if (phy->rev > 2) { + bcm43xx_phy_write(bcm, 0x0422, 0x287A); + bcm43xx_phy_write(bcm, 0x0420, (bcm43xx_phy_read(bcm, 0x0420) & 0x0FFF) | 0x3000); + } + + bcm43xx_phy_write(bcm, 0x04A8, (bcm43xx_phy_read(bcm, 0x04A8) & 0x8080) | 0x7874); + bcm43xx_phy_write(bcm, 0x048E, 0x1C00); + + if (phy->rev == 1) { + bcm43xx_phy_write(bcm, 0x04AB, (bcm43xx_phy_read(bcm, 0x04AB) & 0xF0FF) | 0x0600); + bcm43xx_phy_write(bcm, 0x048B, 0x005E); + bcm43xx_phy_write(bcm, 0x048C, (bcm43xx_phy_read(bcm, 0x048C) & 0xFF00) | 0x001E); + bcm43xx_phy_write(bcm, 0x048D, 0x0002); + } + + bcm43xx_ilt_write(bcm, offset + 0x0800, 0); + bcm43xx_ilt_write(bcm, offset + 0x0801, 7); + bcm43xx_ilt_write(bcm, offset + 0x0802, 16); + bcm43xx_ilt_write(bcm, offset + 0x0803, 28); +} + +static void bcm43xx_phy_setupg(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + u16 i; + + assert(phy->type == BCM43xx_PHYTYPE_G); + if (phy->rev == 1) { + bcm43xx_phy_write(bcm, 0x0406, 0x4F19); + bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, + (bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & 0xFC3F) | 0x0340); + bcm43xx_phy_write(bcm, 0x042C, 0x005A); + bcm43xx_phy_write(bcm, 0x0427, 0x001A); + + for (i = 0; i < BCM43xx_ILT_FINEFREQG_SIZE; i++) + bcm43xx_ilt_write(bcm, 0x5800 + i, bcm43xx_ilt_finefreqg[i]); + for (i = 0; i < BCM43xx_ILT_NOISEG1_SIZE; i++) + bcm43xx_ilt_write(bcm, 0x1800 + i, bcm43xx_ilt_noiseg1[i]); + for (i = 0; i < BCM43xx_ILT_ROTOR_SIZE; i++) + bcm43xx_ilt_write(bcm, 0x2000 + i, bcm43xx_ilt_rotor[i]); + } else { + /* nrssi values are signed 6-bit values. Not sure why we write 0x7654 here... */ + bcm43xx_nrssi_hw_write(bcm, 0xBA98, (s16)0x7654); + + if (phy->rev == 2) { + bcm43xx_phy_write(bcm, 0x04C0, 0x1861); + bcm43xx_phy_write(bcm, 0x04C1, 0x0271); + } else if (phy->rev > 2) { + bcm43xx_phy_write(bcm, 0x04C0, 0x0098); + bcm43xx_phy_write(bcm, 0x04C1, 0x0070); + bcm43xx_phy_write(bcm, 0x04C9, 0x0080); + } + bcm43xx_phy_write(bcm, 0x042B, bcm43xx_phy_read(bcm, 0x042B) | 0x800); + + for (i = 0; i < 64; i++) + bcm43xx_ilt_write(bcm, 0x4000 + i, i); + for (i = 0; i < BCM43xx_ILT_NOISEG2_SIZE; i++) + bcm43xx_ilt_write(bcm, 0x1800 + i, bcm43xx_ilt_noiseg2[i]); + } + + if (phy->rev <= 2) + for (i = 0; i < BCM43xx_ILT_NOISESCALEG_SIZE; i++) + bcm43xx_ilt_write(bcm, 0x1400 + i, bcm43xx_ilt_noisescaleg1[i]); + else if ((phy->rev == 7) && (bcm43xx_phy_read(bcm, 0x0449) & 0x0200)) + for (i = 0; i < BCM43xx_ILT_NOISESCALEG_SIZE; i++) + bcm43xx_ilt_write(bcm, 0x1400 + i, bcm43xx_ilt_noisescaleg3[i]); + else + for (i = 0; i < BCM43xx_ILT_NOISESCALEG_SIZE; i++) + bcm43xx_ilt_write(bcm, 0x1400 + i, bcm43xx_ilt_noisescaleg2[i]); + + if (phy->rev == 2) + for (i = 0; i < BCM43xx_ILT_SIGMASQR_SIZE; i++) + bcm43xx_ilt_write(bcm, 0x5000 + i, bcm43xx_ilt_sigmasqr1[i]); + else if ((phy->rev > 2) && (phy->rev <= 7)) + for (i = 0; i < BCM43xx_ILT_SIGMASQR_SIZE; i++) + bcm43xx_ilt_write(bcm, 0x5000 + i, bcm43xx_ilt_sigmasqr2[i]); + + if (phy->rev == 1) { + for (i = 0; i < BCM43xx_ILT_RETARD_SIZE; i++) + bcm43xx_ilt_write(bcm, 0x2400 + i, bcm43xx_ilt_retard[i]); + for (i = 0; i < 4; i++) { + bcm43xx_ilt_write(bcm, 0x5404 + i, 0x0020); + bcm43xx_ilt_write(bcm, 0x5408 + i, 0x0020); + bcm43xx_ilt_write(bcm, 0x540C + i, 0x0020); + bcm43xx_ilt_write(bcm, 0x5410 + i, 0x0020); + } + bcm43xx_phy_agcsetup(bcm); + + if ((bcm->board_vendor == PCI_VENDOR_ID_BROADCOM) && + (bcm->board_type == 0x0416) && + (bcm->board_revision == 0x0017)) + return; + + bcm43xx_ilt_write(bcm, 0x5001, 0x0002); + bcm43xx_ilt_write(bcm, 0x5002, 0x0001); + } else { + for (i = 0; i <= 0x2F; i++) + bcm43xx_ilt_write(bcm, 0x1000 + i, 0x0820); + bcm43xx_phy_agcsetup(bcm); + bcm43xx_phy_read(bcm, 0x0400); /* dummy read */ + bcm43xx_phy_write(bcm, 0x0403, 0x1000); + bcm43xx_ilt_write(bcm, 0x3C02, 0x000F); + bcm43xx_ilt_write(bcm, 0x3C03, 0x0014); + + if ((bcm->board_vendor == PCI_VENDOR_ID_BROADCOM) && + (bcm->board_type == 0x0416) && + (bcm->board_revision == 0x0017)) + return; + + bcm43xx_ilt_write(bcm, 0x0401, 0x0002); + bcm43xx_ilt_write(bcm, 0x0402, 0x0001); + } +} + +/* Initialize the noisescaletable for APHY */ +static void bcm43xx_phy_init_noisescaletbl(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + int i; + + bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_CTRL, 0x1400); + for (i = 0; i < 12; i++) { + if (phy->rev == 2) + bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x6767); + else + bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x2323); + } + if (phy->rev == 2) + bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x6700); + else + bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x2300); + for (i = 0; i < 11; i++) { + if (phy->rev == 2) + bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x6767); + else + bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x2323); + } + if (phy->rev == 2) + bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x0067); + else + bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x0023); +} + +static void bcm43xx_phy_setupa(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + u16 i; + + assert(phy->type == BCM43xx_PHYTYPE_A); + switch (phy->rev) { + case 2: + bcm43xx_phy_write(bcm, 0x008E, 0x3800); + bcm43xx_phy_write(bcm, 0x0035, 0x03FF); + bcm43xx_phy_write(bcm, 0x0036, 0x0400); + + bcm43xx_ilt_write(bcm, 0x3807, 0x0051); + + bcm43xx_phy_write(bcm, 0x001C, 0x0FF9); + bcm43xx_phy_write(bcm, 0x0020, bcm43xx_phy_read(bcm, 0x0020) & 0xFF0F); + bcm43xx_ilt_write(bcm, 0x3C0C, 0x07BF); + bcm43xx_radio_write16(bcm, 0x0002, 0x07BF); + + bcm43xx_phy_write(bcm, 0x0024, 0x4680); + bcm43xx_phy_write(bcm, 0x0020, 0x0003); + bcm43xx_phy_write(bcm, 0x001D, 0x0F40); + bcm43xx_phy_write(bcm, 0x001F, 0x1C00); + + bcm43xx_phy_write(bcm, 0x002A, (bcm43xx_phy_read(bcm, 0x002A) & 0x00FF) | 0x0400); + bcm43xx_phy_write(bcm, 0x002B, bcm43xx_phy_read(bcm, 0x002B) & 0xFBFF); + bcm43xx_phy_write(bcm, 0x008E, 0x58C1); + + bcm43xx_ilt_write(bcm, 0x0803, 0x000F); + bcm43xx_ilt_write(bcm, 0x0804, 0x001F); + bcm43xx_ilt_write(bcm, 0x0805, 0x002A); + bcm43xx_ilt_write(bcm, 0x0805, 0x0030); + bcm43xx_ilt_write(bcm, 0x0807, 0x003A); + + bcm43xx_ilt_write(bcm, 0x0000, 0x0013); + bcm43xx_ilt_write(bcm, 0x0001, 0x0013); + bcm43xx_ilt_write(bcm, 0x0002, 0x0013); + bcm43xx_ilt_write(bcm, 0x0003, 0x0013); + bcm43xx_ilt_write(bcm, 0x0004, 0x0015); + bcm43xx_ilt_write(bcm, 0x0005, 0x0015); + bcm43xx_ilt_write(bcm, 0x0006, 0x0019); + + bcm43xx_ilt_write(bcm, 0x0404, 0x0003); + bcm43xx_ilt_write(bcm, 0x0405, 0x0003); + bcm43xx_ilt_write(bcm, 0x0406, 0x0007); + + for (i = 0; i < 16; i++) + bcm43xx_ilt_write(bcm, 0x4000 + i, (0x8 + i) & 0x000F); + + bcm43xx_ilt_write(bcm, 0x3003, 0x1044); + bcm43xx_ilt_write(bcm, 0x3004, 0x7201); + bcm43xx_ilt_write(bcm, 0x3006, 0x0040); + bcm43xx_ilt_write(bcm, 0x3001, (bcm43xx_ilt_read(bcm, 0x3001) & 0x0010) | 0x0008); + + for (i = 0; i < BCM43xx_ILT_FINEFREQA_SIZE; i++) + bcm43xx_ilt_write(bcm, 0x5800 + i, bcm43xx_ilt_finefreqa[i]); + for (i = 0; i < BCM43xx_ILT_NOISEA2_SIZE; i++) + bcm43xx_ilt_write(bcm, 0x1800 + i, bcm43xx_ilt_noisea2[i]); + for (i = 0; i < BCM43xx_ILT_ROTOR_SIZE; i++) + bcm43xx_ilt_write(bcm, 0x2000 + i, bcm43xx_ilt_rotor[i]); + bcm43xx_phy_init_noisescaletbl(bcm); + for (i = 0; i < BCM43xx_ILT_RETARD_SIZE; i++) + bcm43xx_ilt_write(bcm, 0x2400 + i, bcm43xx_ilt_retard[i]); + break; + case 3: + for (i = 0; i < 64; i++) + bcm43xx_ilt_write(bcm, 0x4000 + i, i); + + bcm43xx_ilt_write(bcm, 0x3807, 0x0051); + + bcm43xx_phy_write(bcm, 0x001C, 0x0FF9); + bcm43xx_phy_write(bcm, 0x0020, bcm43xx_phy_read(bcm, 0x0020) & 0xFF0F); + bcm43xx_radio_write16(bcm, 0x0002, 0x07BF); + + bcm43xx_phy_write(bcm, 0x0024, 0x4680); + bcm43xx_phy_write(bcm, 0x0020, 0x0003); + bcm43xx_phy_write(bcm, 0x001D, 0x0F40); + bcm43xx_phy_write(bcm, 0x001F, 0x1C00); + bcm43xx_phy_write(bcm, 0x002A, (bcm43xx_phy_read(bcm, 0x002A) & 0x00FF) | 0x0400); + + bcm43xx_ilt_write(bcm, 0x3001, (bcm43xx_ilt_read(bcm, 0x3001) & 0x0010) | 0x0008); + for (i = 0; i < BCM43xx_ILT_NOISEA3_SIZE; i++) + bcm43xx_ilt_write(bcm, 0x1800 + i, bcm43xx_ilt_noisea3[i]); + bcm43xx_phy_init_noisescaletbl(bcm); + for (i = 0; i < BCM43xx_ILT_SIGMASQR_SIZE; i++) + bcm43xx_ilt_write(bcm, 0x5000 + i, bcm43xx_ilt_sigmasqr1[i]); + + bcm43xx_phy_write(bcm, 0x0003, 0x1808); + + bcm43xx_ilt_write(bcm, 0x0803, 0x000F); + bcm43xx_ilt_write(bcm, 0x0804, 0x001F); + bcm43xx_ilt_write(bcm, 0x0805, 0x002A); + bcm43xx_ilt_write(bcm, 0x0805, 0x0030); + bcm43xx_ilt_write(bcm, 0x0807, 0x003A); + + bcm43xx_ilt_write(bcm, 0x0000, 0x0013); + bcm43xx_ilt_write(bcm, 0x0001, 0x0013); + bcm43xx_ilt_write(bcm, 0x0002, 0x0013); + bcm43xx_ilt_write(bcm, 0x0003, 0x0013); + bcm43xx_ilt_write(bcm, 0x0004, 0x0015); + bcm43xx_ilt_write(bcm, 0x0005, 0x0015); + bcm43xx_ilt_write(bcm, 0x0006, 0x0019); + + bcm43xx_ilt_write(bcm, 0x0404, 0x0003); + bcm43xx_ilt_write(bcm, 0x0405, 0x0003); + bcm43xx_ilt_write(bcm, 0x0406, 0x0007); + + bcm43xx_ilt_write(bcm, 0x3C02, 0x000F); + bcm43xx_ilt_write(bcm, 0x3C03, 0x0014); + break; + default: + assert(0); + } +} + +/* Initialize APHY. This is also called for the GPHY in some cases. */ +static void bcm43xx_phy_inita(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + u16 tval; + + if (phy->type == BCM43xx_PHYTYPE_A) { + bcm43xx_phy_setupa(bcm); + } else { + bcm43xx_phy_setupg(bcm); + if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL) + bcm43xx_phy_write(bcm, 0x046E, 0x03CF); + return; + } + + bcm43xx_phy_write(bcm, BCM43xx_PHY_A_CRS, + (bcm43xx_phy_read(bcm, BCM43xx_PHY_A_CRS) & 0xF83C) | 0x0340); + bcm43xx_phy_write(bcm, 0x0034, 0x0001); + + TODO();//TODO: RSSI AGC + bcm43xx_phy_write(bcm, BCM43xx_PHY_A_CRS, + bcm43xx_phy_read(bcm, BCM43xx_PHY_A_CRS) | (1 << 14)); + bcm43xx_radio_init2060(bcm); + + if ((bcm->board_vendor == PCI_VENDOR_ID_BROADCOM) + && ((bcm->board_type == 0x0416) || (bcm->board_type == 0x040A))) { + if (radio->lofcal == 0xFFFF) { + TODO();//TODO: LOF Cal + bcm43xx_radio_set_tx_iq(bcm); + } else + bcm43xx_radio_write16(bcm, 0x001E, radio->lofcal); + } + + bcm43xx_phy_write(bcm, 0x007A, 0xF111); + + if (phy->savedpctlreg == 0xFFFF) { + bcm43xx_radio_write16(bcm, 0x0019, 0x0000); + bcm43xx_radio_write16(bcm, 0x0017, 0x0020); + + tval = bcm43xx_ilt_read(bcm, 0x3001); + if (phy->rev == 1) { + bcm43xx_ilt_write(bcm, 0x3001, + (bcm43xx_ilt_read(bcm, 0x3001) & 0xFF87) + | 0x0058); + } else { + bcm43xx_ilt_write(bcm, 0x3001, + (bcm43xx_ilt_read(bcm, 0x3001) & 0xFFC3) + | 0x002C); + } + bcm43xx_dummy_transmission(bcm); + phy->savedpctlreg = bcm43xx_phy_read(bcm, BCM43xx_PHY_A_PCTL); + bcm43xx_ilt_write(bcm, 0x3001, tval); + + bcm43xx_radio_set_txpower_a(bcm, 0x0018); + } + bcm43xx_radio_clear_tssi(bcm); +} + +static void bcm43xx_phy_initb2(struct bcm43xx_private *bcm) +{ + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + u16 offset, val; + + bcm43xx_write16(bcm, 0x03EC, 0x3F22); + bcm43xx_phy_write(bcm, 0x0020, 0x301C); + bcm43xx_phy_write(bcm, 0x0026, 0x0000); + bcm43xx_phy_write(bcm, 0x0030, 0x00C6); + bcm43xx_phy_write(bcm, 0x0088, 0x3E00); + val = 0x3C3D; + for (offset = 0x0089; offset < 0x00A7; offset++) { + bcm43xx_phy_write(bcm, offset, val); + val -= 0x0202; + } + bcm43xx_phy_write(bcm, 0x03E4, 0x3000); + if (radio->channel == 0xFF) + bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_BG, 0); + else + bcm43xx_radio_selectchannel(bcm, radio->channel, 0); + if (radio->version != 0x2050) { + bcm43xx_radio_write16(bcm, 0x0075, 0x0080); + bcm43xx_radio_write16(bcm, 0x0079, 0x0081); + } + bcm43xx_radio_write16(bcm, 0x0050, 0x0020); + bcm43xx_radio_write16(bcm, 0x0050, 0x0023); + if (radio->version == 0x2050) { + bcm43xx_radio_write16(bcm, 0x0050, 0x0020); + bcm43xx_radio_write16(bcm, 0x005A, 0x0070); + bcm43xx_radio_write16(bcm, 0x005B, 0x007B); + bcm43xx_radio_write16(bcm, 0x005C, 0x00B0); + bcm43xx_radio_write16(bcm, 0x007A, 0x000F); + bcm43xx_phy_write(bcm, 0x0038, 0x0677); + bcm43xx_radio_init2050(bcm); + } + bcm43xx_phy_write(bcm, 0x0014, 0x0080); + bcm43xx_phy_write(bcm, 0x0032, 0x00CA); + bcm43xx_phy_write(bcm, 0x0032, 0x00CC); + bcm43xx_phy_write(bcm, 0x0035, 0x07C2); + bcm43xx_phy_lo_b_measure(bcm); + bcm43xx_phy_write(bcm, 0x0026, 0xCC00); + if (radio->version != 0x2050) + bcm43xx_phy_write(bcm, 0x0026, 0xCE00); + bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, 0x1000); + bcm43xx_phy_write(bcm, 0x002A, 0x88A3); + if (radio->version != 0x2050) + bcm43xx_phy_write(bcm, 0x002A, 0x88C2); + bcm43xx_radio_set_txpower_bg(bcm, 0xFFFF, 0xFFFF, 0xFFFF); + bcm43xx_phy_init_pctl(bcm); +} + +static void bcm43xx_phy_initb4(struct bcm43xx_private *bcm) +{ + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + u16 offset, val; + + bcm43xx_write16(bcm, 0x03EC, 0x3F22); + bcm43xx_phy_write(bcm, 0x0020, 0x301C); + bcm43xx_phy_write(bcm, 0x0026, 0x0000); + bcm43xx_phy_write(bcm, 0x0030, 0x00C6); + bcm43xx_phy_write(bcm, 0x0088, 0x3E00); + val = 0x3C3D; + for (offset = 0x0089; offset < 0x00A7; offset++) { + bcm43xx_phy_write(bcm, offset, val); + val -= 0x0202; + } + bcm43xx_phy_write(bcm, 0x03E4, 0x3000); + if (radio->channel == 0xFF) + bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_BG, 0); + else + bcm43xx_radio_selectchannel(bcm, radio->channel, 0); + if (radio->version != 0x2050) { + bcm43xx_radio_write16(bcm, 0x0075, 0x0080); + bcm43xx_radio_write16(bcm, 0x0079, 0x0081); + } + bcm43xx_radio_write16(bcm, 0x0050, 0x0020); + bcm43xx_radio_write16(bcm, 0x0050, 0x0023); + if (radio->version == 0x2050) { + bcm43xx_radio_write16(bcm, 0x0050, 0x0020); + bcm43xx_radio_write16(bcm, 0x005A, 0x0070); + bcm43xx_radio_write16(bcm, 0x005B, 0x007B); + bcm43xx_radio_write16(bcm, 0x005C, 0x00B0); + bcm43xx_radio_write16(bcm, 0x007A, 0x000F); + bcm43xx_phy_write(bcm, 0x0038, 0x0677); + bcm43xx_radio_init2050(bcm); + } + bcm43xx_phy_write(bcm, 0x0014, 0x0080); + bcm43xx_phy_write(bcm, 0x0032, 0x00CA); + if (radio->version == 0x2050) + bcm43xx_phy_write(bcm, 0x0032, 0x00E0); + bcm43xx_phy_write(bcm, 0x0035, 0x07C2); + + bcm43xx_phy_lo_b_measure(bcm); + + bcm43xx_phy_write(bcm, 0x0026, 0xCC00); + if (radio->version == 0x2050) + bcm43xx_phy_write(bcm, 0x0026, 0xCE00); + bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, 0x1100); + bcm43xx_phy_write(bcm, 0x002A, 0x88A3); + if (radio->version == 0x2050) + bcm43xx_phy_write(bcm, 0x002A, 0x88C2); + bcm43xx_radio_set_txpower_bg(bcm, 0xFFFF, 0xFFFF, 0xFFFF); + if (bcm->sprom.boardflags & BCM43xx_BFL_RSSI) { + bcm43xx_calc_nrssi_slope(bcm); + bcm43xx_calc_nrssi_threshold(bcm); + } + bcm43xx_phy_init_pctl(bcm); +} + +static void bcm43xx_phy_initb5(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + u16 offset; + + if (phy->version == 1 && + radio->version == 0x2050) { + bcm43xx_radio_write16(bcm, 0x007A, + bcm43xx_radio_read16(bcm, 0x007A) + | 0x0050); + } + if ((bcm->board_vendor != PCI_VENDOR_ID_BROADCOM) && + (bcm->board_type != 0x0416)) { + for (offset = 0x00A8 ; offset < 0x00C7; offset++) { + bcm43xx_phy_write(bcm, offset, + (bcm43xx_phy_read(bcm, offset) + 0x2020) + & 0x3F3F); + } + } + bcm43xx_phy_write(bcm, 0x0035, + (bcm43xx_phy_read(bcm, 0x0035) & 0xF0FF) + | 0x0700); + if (radio->version == 0x2050) + bcm43xx_phy_write(bcm, 0x0038, 0x0667); + + if (phy->connected) { + if (radio->version == 0x2050) { + bcm43xx_radio_write16(bcm, 0x007A, + bcm43xx_radio_read16(bcm, 0x007A) + | 0x0020); + bcm43xx_radio_write16(bcm, 0x0051, + bcm43xx_radio_read16(bcm, 0x0051) + | 0x0004); + } + bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO, 0x0000); + + bcm43xx_phy_write(bcm, 0x0802, bcm43xx_phy_read(bcm, 0x0802) | 0x0100); + bcm43xx_phy_write(bcm, 0x042B, bcm43xx_phy_read(bcm, 0x042B) | 0x2000); + + bcm43xx_phy_write(bcm, 0x001C, 0x186A); + + bcm43xx_phy_write(bcm, 0x0013, (bcm43xx_phy_read(bcm, 0x0013) & 0x00FF) | 0x1900); + bcm43xx_phy_write(bcm, 0x0035, (bcm43xx_phy_read(bcm, 0x0035) & 0xFFC0) | 0x0064); + bcm43xx_phy_write(bcm, 0x005D, (bcm43xx_phy_read(bcm, 0x005D) & 0xFF80) | 0x000A); + } + + if (bcm->bad_frames_preempt) { + bcm43xx_phy_write(bcm, BCM43xx_PHY_RADIO_BITFIELD, + bcm43xx_phy_read(bcm, BCM43xx_PHY_RADIO_BITFIELD) | (1 << 11)); + } + + if (phy->version == 1 && radio->version == 0x2050) { + bcm43xx_phy_write(bcm, 0x0026, 0xCE00); + bcm43xx_phy_write(bcm, 0x0021, 0x3763); + bcm43xx_phy_write(bcm, 0x0022, 0x1BC3); + bcm43xx_phy_write(bcm, 0x0023, 0x06F9); + bcm43xx_phy_write(bcm, 0x0024, 0x037E); + } else + bcm43xx_phy_write(bcm, 0x0026, 0xCC00); + bcm43xx_phy_write(bcm, 0x0030, 0x00C6); + bcm43xx_write16(bcm, 0x03EC, 0x3F22); + + if (phy->version == 1 && radio->version == 0x2050) + bcm43xx_phy_write(bcm, 0x0020, 0x3E1C); + else + bcm43xx_phy_write(bcm, 0x0020, 0x301C); + + if (phy->version == 0) + bcm43xx_write16(bcm, 0x03E4, 0x3000); + + /* Force to channel 7, even if not supported. */ + bcm43xx_radio_selectchannel(bcm, 7, 0); + + if (radio->version != 0x2050) { + bcm43xx_radio_write16(bcm, 0x0075, 0x0080); + bcm43xx_radio_write16(bcm, 0x0079, 0x0081); + } + + bcm43xx_radio_write16(bcm, 0x0050, 0x0020); + bcm43xx_radio_write16(bcm, 0x0050, 0x0023); + + if (radio->version == 0x2050) { + bcm43xx_radio_write16(bcm, 0x0050, 0x0020); + bcm43xx_radio_write16(bcm, 0x005A, 0x0070); + } + + bcm43xx_radio_write16(bcm, 0x005B, 0x007B); + bcm43xx_radio_write16(bcm, 0x005C, 0x00B0); + + bcm43xx_radio_write16(bcm, 0x007A, bcm43xx_radio_read16(bcm, 0x007A) | 0x0007); + + bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_BG, 0); + + bcm43xx_phy_write(bcm, 0x0014, 0x0080); + bcm43xx_phy_write(bcm, 0x0032, 0x00CA); + bcm43xx_phy_write(bcm, 0x88A3, 0x002A); + + bcm43xx_radio_set_txpower_bg(bcm, 0xFFFF, 0xFFFF, 0xFFFF); + + if (radio->version == 0x2050) + bcm43xx_radio_write16(bcm, 0x005D, 0x000D); + + bcm43xx_write16(bcm, 0x03E4, (bcm43xx_read16(bcm, 0x03E4) & 0xFFC0) | 0x0004); +} + +static void bcm43xx_phy_initb6(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + u16 offset, val; + + bcm43xx_phy_write(bcm, 0x003E, 0x817A); + bcm43xx_radio_write16(bcm, 0x007A, + (bcm43xx_radio_read16(bcm, 0x007A) | 0x0058)); + if ((radio->manufact == 0x17F) && + (radio->version == 0x2050) && + (radio->revision == 3 || + radio->revision == 4 || + radio->revision == 5)) { + bcm43xx_radio_write16(bcm, 0x0051, 0x001F); + bcm43xx_radio_write16(bcm, 0x0052, 0x0040); + bcm43xx_radio_write16(bcm, 0x0053, 0x005B); + bcm43xx_radio_write16(bcm, 0x0054, 0x0098); + bcm43xx_radio_write16(bcm, 0x005A, 0x0088); + bcm43xx_radio_write16(bcm, 0x005B, 0x0088); + bcm43xx_radio_write16(bcm, 0x005D, 0x0088); + bcm43xx_radio_write16(bcm, 0x005E, 0x0088); + bcm43xx_radio_write16(bcm, 0x007D, 0x0088); + } + if ((radio->manufact == 0x17F) && + (radio->version == 0x2050) && + (radio->revision == 6)) { + bcm43xx_radio_write16(bcm, 0x0051, 0x0000); + bcm43xx_radio_write16(bcm, 0x0052, 0x0040); + bcm43xx_radio_write16(bcm, 0x0053, 0x00B7); + bcm43xx_radio_write16(bcm, 0x0054, 0x0098); + bcm43xx_radio_write16(bcm, 0x005A, 0x0088); + bcm43xx_radio_write16(bcm, 0x005B, 0x008B); + bcm43xx_radio_write16(bcm, 0x005C, 0x00B5); + bcm43xx_radio_write16(bcm, 0x005D, 0x0088); + bcm43xx_radio_write16(bcm, 0x005E, 0x0088); + bcm43xx_radio_write16(bcm, 0x007D, 0x0088); + bcm43xx_radio_write16(bcm, 0x007C, 0x0001); + bcm43xx_radio_write16(bcm, 0x007E, 0x0008); + } + if ((radio->manufact == 0x17F) && + (radio->version == 0x2050) && + (radio->revision == 7)) { + bcm43xx_radio_write16(bcm, 0x0051, 0x0000); + bcm43xx_radio_write16(bcm, 0x0052, 0x0040); + bcm43xx_radio_write16(bcm, 0x0053, 0x00B7); + bcm43xx_radio_write16(bcm, 0x0054, 0x0098); + bcm43xx_radio_write16(bcm, 0x005A, 0x0088); + bcm43xx_radio_write16(bcm, 0x005B, 0x00A8); + bcm43xx_radio_write16(bcm, 0x005C, 0x0075); + bcm43xx_radio_write16(bcm, 0x005D, 0x00F5); + bcm43xx_radio_write16(bcm, 0x005E, 0x00B8); + bcm43xx_radio_write16(bcm, 0x007D, 0x00E8); + bcm43xx_radio_write16(bcm, 0x007C, 0x0001); + bcm43xx_radio_write16(bcm, 0x007E, 0x0008); + bcm43xx_radio_write16(bcm, 0x007B, 0x0000); + } + if ((radio->manufact == 0x17F) && + (radio->version == 0x2050) && + (radio->revision == 8)) { + bcm43xx_radio_write16(bcm, 0x0051, 0x0000); + bcm43xx_radio_write16(bcm, 0x0052, 0x0040); + bcm43xx_radio_write16(bcm, 0x0053, 0x00B7); + bcm43xx_radio_write16(bcm, 0x0054, 0x0098); + bcm43xx_radio_write16(bcm, 0x005A, 0x0088); + bcm43xx_radio_write16(bcm, 0x005B, 0x006B); + bcm43xx_radio_write16(bcm, 0x005C, 0x000F); + if (bcm->sprom.boardflags & 0x8000) { + bcm43xx_radio_write16(bcm, 0x005D, 0x00FA); + bcm43xx_radio_write16(bcm, 0x005E, 0x00D8); + } else { + bcm43xx_radio_write16(bcm, 0x005D, 0x00F5); + bcm43xx_radio_write16(bcm, 0x005E, 0x00B8); + } + bcm43xx_radio_write16(bcm, 0x0073, 0x0003); + bcm43xx_radio_write16(bcm, 0x007D, 0x00A8); + bcm43xx_radio_write16(bcm, 0x007C, 0x0001); + bcm43xx_radio_write16(bcm, 0x007E, 0x0008); + } + val = 0x1E1F; + for (offset = 0x0088; offset < 0x0098; offset++) { + bcm43xx_phy_write(bcm, offset, val); + val -= 0x0202; + } + val = 0x3E3F; + for (offset = 0x0098; offset < 0x00A8; offset++) { + bcm43xx_phy_write(bcm, offset, val); + val -= 0x0202; + } + val = 0x2120; + for (offset = 0x00A8; offset < 0x00C8; offset++) { + bcm43xx_phy_write(bcm, offset, (val & 0x3F3F)); + val += 0x0202; + } + if (phy->type == BCM43xx_PHYTYPE_G) { + bcm43xx_radio_write16(bcm, 0x007A, + bcm43xx_radio_read16(bcm, 0x007A) | 0x0020); + bcm43xx_radio_write16(bcm, 0x0051, + bcm43xx_radio_read16(bcm, 0x0051) | 0x0004); + bcm43xx_phy_write(bcm, 0x0802, + bcm43xx_phy_read(bcm, 0x0802) | 0x0100); + bcm43xx_phy_write(bcm, 0x042B, + bcm43xx_phy_read(bcm, 0x042B) | 0x2000); + } + + /* Force to channel 7, even if not supported. */ + bcm43xx_radio_selectchannel(bcm, 7, 0); + + bcm43xx_radio_write16(bcm, 0x0050, 0x0020); + bcm43xx_radio_write16(bcm, 0x0050, 0x0023); + udelay(40); + bcm43xx_radio_write16(bcm, 0x007C, (bcm43xx_radio_read16(bcm, 0x007C) | 0x0002)); + bcm43xx_radio_write16(bcm, 0x0050, 0x0020); + if (radio->manufact == 0x17F && + radio->version == 0x2050 && + radio->revision <= 2) { + bcm43xx_radio_write16(bcm, 0x0050, 0x0020); + bcm43xx_radio_write16(bcm, 0x005A, 0x0070); + bcm43xx_radio_write16(bcm, 0x005B, 0x007B); + bcm43xx_radio_write16(bcm, 0x005C, 0x00B0); + } + bcm43xx_radio_write16(bcm, 0x007A, + (bcm43xx_radio_read16(bcm, 0x007A) & 0x00F8) | 0x0007); + + bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_BG, 0); + + bcm43xx_phy_write(bcm, 0x0014, 0x0200); + if (radio->version == 0x2050){ + if (radio->revision == 3 || + radio->revision == 4 || + radio->revision == 5) + bcm43xx_phy_write(bcm, 0x002A, 0x8AC0); + else + bcm43xx_phy_write(bcm, 0x002A, 0x88C2); + } + bcm43xx_phy_write(bcm, 0x0038, 0x0668); + bcm43xx_radio_set_txpower_bg(bcm, 0xFFFF, 0xFFFF, 0xFFFF); + if (radio->version == 0x2050) { + if (radio->revision == 3 || + radio->revision == 4 || + radio->revision == 5) + bcm43xx_phy_write(bcm, 0x005D, bcm43xx_phy_read(bcm, 0x005D) | 0x0003); + else if (radio->revision <= 2) + bcm43xx_radio_write16(bcm, 0x005D, 0x000D); + } + + if (phy->rev == 4) + bcm43xx_phy_write(bcm, 0x0002, (bcm43xx_phy_read(bcm, 0x0002) & 0xFFC0) | 0x0004); + else + bcm43xx_write16(bcm, 0x03E4, 0x0009); + if (phy->type == BCM43xx_PHYTYPE_B) { + bcm43xx_write16(bcm, 0x03E6, 0x8140); + bcm43xx_phy_write(bcm, 0x0016, 0x0410); + bcm43xx_phy_write(bcm, 0x0017, 0x0820); + bcm43xx_phy_write(bcm, 0x0062, 0x0007); + (void) bcm43xx_radio_calibrationvalue(bcm); + bcm43xx_phy_lo_b_measure(bcm); + if (bcm->sprom.boardflags & BCM43xx_BFL_RSSI) { + bcm43xx_calc_nrssi_slope(bcm); + bcm43xx_calc_nrssi_threshold(bcm); + } + bcm43xx_phy_init_pctl(bcm); + } else + bcm43xx_write16(bcm, 0x03E6, 0x0); +} + +static void bcm43xx_calc_loopback_gain(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + u16 backup_phy[15]; + u16 backup_radio[3]; + u16 backup_bband; + u16 i; + u16 loop1_cnt, loop1_done, loop1_omitted; + u16 loop2_done; + + backup_phy[0] = bcm43xx_phy_read(bcm, 0x0429); + backup_phy[1] = bcm43xx_phy_read(bcm, 0x0001); + backup_phy[2] = bcm43xx_phy_read(bcm, 0x0811); + backup_phy[3] = bcm43xx_phy_read(bcm, 0x0812); + backup_phy[4] = bcm43xx_phy_read(bcm, 0x0814); + backup_phy[5] = bcm43xx_phy_read(bcm, 0x0815); + backup_phy[6] = bcm43xx_phy_read(bcm, 0x005A); + backup_phy[7] = bcm43xx_phy_read(bcm, 0x0059); + backup_phy[8] = bcm43xx_phy_read(bcm, 0x0058); + backup_phy[9] = bcm43xx_phy_read(bcm, 0x000A); + backup_phy[10] = bcm43xx_phy_read(bcm, 0x0003); + backup_phy[11] = bcm43xx_phy_read(bcm, 0x080F); + backup_phy[12] = bcm43xx_phy_read(bcm, 0x0810); + backup_phy[13] = bcm43xx_phy_read(bcm, 0x002B); + backup_phy[14] = bcm43xx_phy_read(bcm, 0x0015); + bcm43xx_phy_read(bcm, 0x002D); /* dummy read */ + backup_bband = radio->baseband_atten; + backup_radio[0] = bcm43xx_radio_read16(bcm, 0x0052); + backup_radio[1] = bcm43xx_radio_read16(bcm, 0x0043); + backup_radio[2] = bcm43xx_radio_read16(bcm, 0x007A); + + bcm43xx_phy_write(bcm, 0x0429, + bcm43xx_phy_read(bcm, 0x0429) & 0x3FFF); + bcm43xx_phy_write(bcm, 0x0001, + bcm43xx_phy_read(bcm, 0x0001) & 0x8000); + bcm43xx_phy_write(bcm, 0x0811, + bcm43xx_phy_read(bcm, 0x0811) | 0x0002); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_phy_read(bcm, 0x0812) & 0xFFFD); + bcm43xx_phy_write(bcm, 0x0811, + bcm43xx_phy_read(bcm, 0x0811) | 0x0001); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_phy_read(bcm, 0x0812) & 0xFFFE); + bcm43xx_phy_write(bcm, 0x0814, + bcm43xx_phy_read(bcm, 0x0814) | 0x0001); + bcm43xx_phy_write(bcm, 0x0815, + bcm43xx_phy_read(bcm, 0x0815) & 0xFFFE); + bcm43xx_phy_write(bcm, 0x0814, + bcm43xx_phy_read(bcm, 0x0814) | 0x0002); + bcm43xx_phy_write(bcm, 0x0815, + bcm43xx_phy_read(bcm, 0x0815) & 0xFFFD); + bcm43xx_phy_write(bcm, 0x0811, + bcm43xx_phy_read(bcm, 0x0811) | 0x000C); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_phy_read(bcm, 0x0812) | 0x000C); + + bcm43xx_phy_write(bcm, 0x0811, + (bcm43xx_phy_read(bcm, 0x0811) + & 0xFFCF) | 0x0030); + bcm43xx_phy_write(bcm, 0x0812, + (bcm43xx_phy_read(bcm, 0x0812) + & 0xFFCF) | 0x0010); + + bcm43xx_phy_write(bcm, 0x005A, 0x0780); + bcm43xx_phy_write(bcm, 0x0059, 0xC810); + bcm43xx_phy_write(bcm, 0x0058, 0x000D); + if (phy->version == 0) { + bcm43xx_phy_write(bcm, 0x0003, 0x0122); + } else { + bcm43xx_phy_write(bcm, 0x000A, + bcm43xx_phy_read(bcm, 0x000A) + | 0x2000); + } + bcm43xx_phy_write(bcm, 0x0814, + bcm43xx_phy_read(bcm, 0x0814) | 0x0004); + bcm43xx_phy_write(bcm, 0x0815, + bcm43xx_phy_read(bcm, 0x0815) & 0xFFFB); + bcm43xx_phy_write(bcm, 0x0003, + (bcm43xx_phy_read(bcm, 0x0003) + & 0xFF9F) | 0x0040); + if (radio->version == 0x2050 && radio->revision == 2) { + bcm43xx_radio_write16(bcm, 0x0052, 0x0000); + bcm43xx_radio_write16(bcm, 0x0043, + (bcm43xx_radio_read16(bcm, 0x0043) + & 0xFFF0) | 0x0009); + loop1_cnt = 9; + } else if (radio->revision == 8) { + bcm43xx_radio_write16(bcm, 0x0043, 0x000F); + loop1_cnt = 15; + } else + loop1_cnt = 0; + + bcm43xx_phy_set_baseband_attenuation(bcm, 11); + + if (phy->rev >= 3) + bcm43xx_phy_write(bcm, 0x080F, 0xC020); + else + bcm43xx_phy_write(bcm, 0x080F, 0x8020); + bcm43xx_phy_write(bcm, 0x0810, 0x0000); + + bcm43xx_phy_write(bcm, 0x002B, + (bcm43xx_phy_read(bcm, 0x002B) + & 0xFFC0) | 0x0001); + bcm43xx_phy_write(bcm, 0x002B, + (bcm43xx_phy_read(bcm, 0x002B) + & 0xC0FF) | 0x0800); + bcm43xx_phy_write(bcm, 0x0811, + bcm43xx_phy_read(bcm, 0x0811) | 0x0100); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_phy_read(bcm, 0x0812) & 0xCFFF); + if (bcm->sprom.boardflags & BCM43xx_BFL_EXTLNA) { + if (phy->rev >= 7) { + bcm43xx_phy_write(bcm, 0x0811, + bcm43xx_phy_read(bcm, 0x0811) + | 0x0800); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_phy_read(bcm, 0x0812) + | 0x8000); + } + } + bcm43xx_radio_write16(bcm, 0x007A, + bcm43xx_radio_read16(bcm, 0x007A) + & 0x00F7); + + for (i = 0; i < loop1_cnt; i++) { + bcm43xx_radio_write16(bcm, 0x0043, loop1_cnt); + bcm43xx_phy_write(bcm, 0x0812, + (bcm43xx_phy_read(bcm, 0x0812) + & 0xF0FF) | (i << 8)); + bcm43xx_phy_write(bcm, 0x0015, + (bcm43xx_phy_read(bcm, 0x0015) + & 0x0FFF) | 0xA000); + bcm43xx_phy_write(bcm, 0x0015, + (bcm43xx_phy_read(bcm, 0x0015) + & 0x0FFF) | 0xF000); + udelay(20); + if (bcm43xx_phy_read(bcm, 0x002D) >= 0x0DFC) + break; + } + loop1_done = i; + loop1_omitted = loop1_cnt - loop1_done; + + loop2_done = 0; + if (loop1_done >= 8) { + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_phy_read(bcm, 0x0812) + | 0x0030); + for (i = loop1_done - 8; i < 16; i++) { + bcm43xx_phy_write(bcm, 0x0812, + (bcm43xx_phy_read(bcm, 0x0812) + & 0xF0FF) | (i << 8)); + bcm43xx_phy_write(bcm, 0x0015, + (bcm43xx_phy_read(bcm, 0x0015) + & 0x0FFF) | 0xA000); + bcm43xx_phy_write(bcm, 0x0015, + (bcm43xx_phy_read(bcm, 0x0015) + & 0x0FFF) | 0xF000); + udelay(20); + if (bcm43xx_phy_read(bcm, 0x002D) >= 0x0DFC) + break; + } + } + + bcm43xx_phy_write(bcm, 0x0814, backup_phy[4]); + bcm43xx_phy_write(bcm, 0x0815, backup_phy[5]); + bcm43xx_phy_write(bcm, 0x005A, backup_phy[6]); + bcm43xx_phy_write(bcm, 0x0059, backup_phy[7]); + bcm43xx_phy_write(bcm, 0x0058, backup_phy[8]); + bcm43xx_phy_write(bcm, 0x000A, backup_phy[9]); + bcm43xx_phy_write(bcm, 0x0003, backup_phy[10]); + bcm43xx_phy_write(bcm, 0x080F, backup_phy[11]); + bcm43xx_phy_write(bcm, 0x0810, backup_phy[12]); + bcm43xx_phy_write(bcm, 0x002B, backup_phy[13]); + bcm43xx_phy_write(bcm, 0x0015, backup_phy[14]); + + bcm43xx_phy_set_baseband_attenuation(bcm, backup_bband); + + bcm43xx_radio_write16(bcm, 0x0052, backup_radio[0]); + bcm43xx_radio_write16(bcm, 0x0043, backup_radio[1]); + bcm43xx_radio_write16(bcm, 0x007A, backup_radio[2]); + + bcm43xx_phy_write(bcm, 0x0811, backup_phy[2] | 0x0003); + udelay(10); + bcm43xx_phy_write(bcm, 0x0811, backup_phy[2]); + bcm43xx_phy_write(bcm, 0x0812, backup_phy[3]); + bcm43xx_phy_write(bcm, 0x0429, backup_phy[0]); + bcm43xx_phy_write(bcm, 0x0001, backup_phy[1]); + + phy->loopback_gain[0] = ((loop1_done * 6) - (loop1_omitted * 4)) - 11; + phy->loopback_gain[1] = (24 - (3 * loop2_done)) * 2; +} + +static void bcm43xx_phy_initg(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + u16 tmp; + + if (phy->rev == 1) + bcm43xx_phy_initb5(bcm); + else if (phy->rev >= 2 && phy->rev <= 7) + bcm43xx_phy_initb6(bcm); + if (phy->rev >= 2 || phy->connected) + bcm43xx_phy_inita(bcm); + + if (phy->rev >= 2) { + bcm43xx_phy_write(bcm, 0x0814, 0x0000); + bcm43xx_phy_write(bcm, 0x0815, 0x0000); + if (phy->rev == 2) + bcm43xx_phy_write(bcm, 0x0811, 0x0000); + else if (phy->rev >= 3) + bcm43xx_phy_write(bcm, 0x0811, 0x0400); + bcm43xx_phy_write(bcm, 0x0015, 0x00C0); + if (phy->connected) { + tmp = bcm43xx_phy_read(bcm, 0x0400) & 0xFF; + if (tmp < 6) { + bcm43xx_phy_write(bcm, 0x04C2, 0x1816); + bcm43xx_phy_write(bcm, 0x04C3, 0x8006); + if (tmp != 3) { + bcm43xx_phy_write(bcm, 0x04CC, + (bcm43xx_phy_read(bcm, 0x04CC) + & 0x00FF) | 0x1F00); + } + } + } + } + if (phy->rev < 3 && phy->connected) + bcm43xx_phy_write(bcm, 0x047E, 0x0078); + if (phy->rev >= 6 && phy->rev <= 8) { + bcm43xx_phy_write(bcm, 0x0801, bcm43xx_phy_read(bcm, 0x0801) | 0x0080); + bcm43xx_phy_write(bcm, 0x043E, bcm43xx_phy_read(bcm, 0x043E) | 0x0004); + } + if (phy->rev >= 2 && phy->connected) + bcm43xx_calc_loopback_gain(bcm); + if (radio->revision != 8) { + if (radio->initval == 0xFFFF) + radio->initval = bcm43xx_radio_init2050(bcm); + else + bcm43xx_radio_write16(bcm, 0x0078, radio->initval); + } + if (radio->txctl2 == 0xFFFF) { + bcm43xx_phy_lo_g_measure(bcm); + } else { + if (radio->version == 0x2050 && radio->revision == 8) { + //FIXME + } else { + bcm43xx_radio_write16(bcm, 0x0052, + (bcm43xx_radio_read16(bcm, 0x0052) + & 0xFFF0) | radio->txctl1); + } + if (phy->rev >= 6) { + /* + bcm43xx_phy_write(bcm, 0x0036, + (bcm43xx_phy_read(bcm, 0x0036) + & 0xF000) | (FIXME << 12)); + */ + } + if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL) + bcm43xx_phy_write(bcm, 0x002E, 0x8075); + else + bcm43xx_phy_write(bcm, 0x003E, 0x807F); + if (phy->rev < 2) + bcm43xx_phy_write(bcm, 0x002F, 0x0101); + else + bcm43xx_phy_write(bcm, 0x002F, 0x0202); + } + if (phy->connected) { + bcm43xx_phy_lo_adjust(bcm, 0); + bcm43xx_phy_write(bcm, 0x080F, 0x8078); + } + + if (!(bcm->sprom.boardflags & BCM43xx_BFL_RSSI)) { + /* The specs state to update the NRSSI LT with + * the value 0x7FFFFFFF here. I think that is some weird + * compiler optimization in the original driver. + * Essentially, what we do here is resetting all NRSSI LT + * entries to -32 (see the limit_value() in nrssi_hw_update()) + */ + bcm43xx_nrssi_hw_update(bcm, 0xFFFF); + bcm43xx_calc_nrssi_threshold(bcm); + } else if (phy->connected) { + if (radio->nrssi[0] == -1000) { + assert(radio->nrssi[1] == -1000); + bcm43xx_calc_nrssi_slope(bcm); + } else { + assert(radio->nrssi[1] != -1000); + bcm43xx_calc_nrssi_threshold(bcm); + } + } + if (radio->revision == 8) + bcm43xx_phy_write(bcm, 0x0805, 0x3230); + bcm43xx_phy_init_pctl(bcm); + if (bcm->chip_id == 0x4306 && bcm->chip_package != 2) { + bcm43xx_phy_write(bcm, 0x0429, + bcm43xx_phy_read(bcm, 0x0429) & 0xBFFF); + bcm43xx_phy_write(bcm, 0x04C3, + bcm43xx_phy_read(bcm, 0x04C3) & 0x7FFF); + } +} + +static u16 bcm43xx_phy_lo_b_r15_loop(struct bcm43xx_private *bcm) +{ + int i; + u16 ret = 0; + + for (i = 0; i < 10; i++){ + bcm43xx_phy_write(bcm, 0x0015, 0xAFA0); + udelay(1); + bcm43xx_phy_write(bcm, 0x0015, 0xEFA0); + udelay(10); + bcm43xx_phy_write(bcm, 0x0015, 0xFFA0); + udelay(40); + ret += bcm43xx_phy_read(bcm, 0x002C); + } + + return ret; +} + +void bcm43xx_phy_lo_b_measure(struct bcm43xx_private *bcm) +{ + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + u16 regstack[12] = { 0 }; + u16 mls; + u16 fval; + int i, j; + + regstack[0] = bcm43xx_phy_read(bcm, 0x0015); + regstack[1] = bcm43xx_radio_read16(bcm, 0x0052) & 0xFFF0; + + if (radio->version == 0x2053) { + regstack[2] = bcm43xx_phy_read(bcm, 0x000A); + regstack[3] = bcm43xx_phy_read(bcm, 0x002A); + regstack[4] = bcm43xx_phy_read(bcm, 0x0035); + regstack[5] = bcm43xx_phy_read(bcm, 0x0003); + regstack[6] = bcm43xx_phy_read(bcm, 0x0001); + regstack[7] = bcm43xx_phy_read(bcm, 0x0030); + + regstack[8] = bcm43xx_radio_read16(bcm, 0x0043); + regstack[9] = bcm43xx_radio_read16(bcm, 0x007A); + regstack[10] = bcm43xx_read16(bcm, 0x03EC); + regstack[11] = bcm43xx_radio_read16(bcm, 0x0052) & 0x00F0; + + bcm43xx_phy_write(bcm, 0x0030, 0x00FF); + bcm43xx_write16(bcm, 0x03EC, 0x3F3F); + bcm43xx_phy_write(bcm, 0x0035, regstack[4] & 0xFF7F); + bcm43xx_radio_write16(bcm, 0x007A, regstack[9] & 0xFFF0); + } + bcm43xx_phy_write(bcm, 0x0015, 0xB000); + bcm43xx_phy_write(bcm, 0x002B, 0x0004); + + if (radio->version == 0x2053) { + bcm43xx_phy_write(bcm, 0x002B, 0x0203); + bcm43xx_phy_write(bcm, 0x002A, 0x08A3); + } + + phy->minlowsig[0] = 0xFFFF; + + for (i = 0; i < 4; i++) { + bcm43xx_radio_write16(bcm, 0x0052, regstack[1] | i); + bcm43xx_phy_lo_b_r15_loop(bcm); + } + for (i = 0; i < 10; i++) { + bcm43xx_radio_write16(bcm, 0x0052, regstack[1] | i); + mls = bcm43xx_phy_lo_b_r15_loop(bcm) / 10; + if (mls < phy->minlowsig[0]) { + phy->minlowsig[0] = mls; + phy->minlowsigpos[0] = i; + } + } + bcm43xx_radio_write16(bcm, 0x0052, regstack[1] | phy->minlowsigpos[0]); + + phy->minlowsig[1] = 0xFFFF; + + for (i = -4; i < 5; i += 2) { + for (j = -4; j < 5; j += 2) { + if (j < 0) + fval = (0x0100 * i) + j + 0x0100; + else + fval = (0x0100 * i) + j; + bcm43xx_phy_write(bcm, 0x002F, fval); + mls = bcm43xx_phy_lo_b_r15_loop(bcm) / 10; + if (mls < phy->minlowsig[1]) { + phy->minlowsig[1] = mls; + phy->minlowsigpos[1] = fval; + } + } + } + phy->minlowsigpos[1] += 0x0101; + + bcm43xx_phy_write(bcm, 0x002F, phy->minlowsigpos[1]); + if (radio->version == 0x2053) { + bcm43xx_phy_write(bcm, 0x000A, regstack[2]); + bcm43xx_phy_write(bcm, 0x002A, regstack[3]); + bcm43xx_phy_write(bcm, 0x0035, regstack[4]); + bcm43xx_phy_write(bcm, 0x0003, regstack[5]); + bcm43xx_phy_write(bcm, 0x0001, regstack[6]); + bcm43xx_phy_write(bcm, 0x0030, regstack[7]); + + bcm43xx_radio_write16(bcm, 0x0043, regstack[8]); + bcm43xx_radio_write16(bcm, 0x007A, regstack[9]); + + bcm43xx_radio_write16(bcm, 0x0052, + (bcm43xx_radio_read16(bcm, 0x0052) & 0x000F) + | regstack[11]); + + bcm43xx_write16(bcm, 0x03EC, regstack[10]); + } + bcm43xx_phy_write(bcm, 0x0015, regstack[0]); +} + +static inline +u16 bcm43xx_phy_lo_g_deviation_subval(struct bcm43xx_private *bcm, u16 control) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + + if (phy->connected) { + bcm43xx_phy_write(bcm, 0x15, 0xE300); + control <<= 8; + bcm43xx_phy_write(bcm, 0x0812, control | 0x00B0); + udelay(5); + bcm43xx_phy_write(bcm, 0x0812, control | 0x00B2); + udelay(2); + bcm43xx_phy_write(bcm, 0x0812, control | 0x00B3); + udelay(4); + bcm43xx_phy_write(bcm, 0x0015, 0xF300); + udelay(8); + } else { + bcm43xx_phy_write(bcm, 0x0015, control | 0xEFA0); + udelay(2); + bcm43xx_phy_write(bcm, 0x0015, control | 0xEFE0); + udelay(4); + bcm43xx_phy_write(bcm, 0x0015, control | 0xFFE0); + udelay(8); + } + + return bcm43xx_phy_read(bcm, 0x002D); +} + +static u32 bcm43xx_phy_lo_g_singledeviation(struct bcm43xx_private *bcm, u16 control) +{ + int i; + u32 ret = 0; + + for (i = 0; i < 8; i++) + ret += bcm43xx_phy_lo_g_deviation_subval(bcm, control); + + return ret; +} + +/* Write the LocalOscillator CONTROL */ +static inline +void bcm43xx_lo_write(struct bcm43xx_private *bcm, + struct bcm43xx_lopair *pair) +{ + u16 value; + + value = (u8)(pair->low); + value |= ((u8)(pair->high)) << 8; + +#ifdef CONFIG_BCM43XX_DEBUG + /* Sanity check. */ + if (pair->low < -8 || pair->low > 8 || + pair->high < -8 || pair->high > 8) { + printk(KERN_WARNING PFX + "WARNING: Writing invalid LOpair " + "(low: %d, high: %d, index: %lu)\n", + pair->low, pair->high, + (unsigned long)(pair - bcm43xx_current_phy(bcm)->_lo_pairs)); + dump_stack(); + } +#endif + + bcm43xx_phy_write(bcm, BCM43xx_PHY_G_LO_CONTROL, value); +} + +static inline +struct bcm43xx_lopair * bcm43xx_find_lopair(struct bcm43xx_private *bcm, + u16 baseband_attenuation, + u16 radio_attenuation, + u16 tx) +{ + static const u8 dict[10] = { 11, 10, 11, 12, 13, 12, 13, 12, 13, 12 }; + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + + if (baseband_attenuation > 6) + baseband_attenuation = 6; + assert(radio_attenuation < 10); + + if (tx == 3) { + return bcm43xx_get_lopair(phy, + radio_attenuation, + baseband_attenuation); + } + return bcm43xx_get_lopair(phy, dict[radio_attenuation], baseband_attenuation); +} + +static inline +struct bcm43xx_lopair * bcm43xx_current_lopair(struct bcm43xx_private *bcm) +{ + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + + return bcm43xx_find_lopair(bcm, + radio->baseband_atten, + radio->radio_atten, + radio->txctl1); +} + +/* Adjust B/G LO */ +void bcm43xx_phy_lo_adjust(struct bcm43xx_private *bcm, int fixed) +{ + struct bcm43xx_lopair *pair; + + if (fixed) { + /* Use fixed values. Only for initialization. */ + pair = bcm43xx_find_lopair(bcm, 2, 3, 0); + } else + pair = bcm43xx_current_lopair(bcm); + bcm43xx_lo_write(bcm, pair); +} + +static void bcm43xx_phy_lo_g_measure_txctl2(struct bcm43xx_private *bcm) +{ + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + u16 txctl2 = 0, i; + u32 smallest, tmp; + + bcm43xx_radio_write16(bcm, 0x0052, 0x0000); + udelay(10); + smallest = bcm43xx_phy_lo_g_singledeviation(bcm, 0); + for (i = 0; i < 16; i++) { + bcm43xx_radio_write16(bcm, 0x0052, i); + udelay(10); + tmp = bcm43xx_phy_lo_g_singledeviation(bcm, 0); + if (tmp < smallest) { + smallest = tmp; + txctl2 = i; + } + } + radio->txctl2 = txctl2; +} + +static +void bcm43xx_phy_lo_g_state(struct bcm43xx_private *bcm, + const struct bcm43xx_lopair *in_pair, + struct bcm43xx_lopair *out_pair, + u16 r27) +{ + static const struct bcm43xx_lopair transitions[8] = { + { .high = 1, .low = 1, }, + { .high = 1, .low = 0, }, + { .high = 1, .low = -1, }, + { .high = 0, .low = -1, }, + { .high = -1, .low = -1, }, + { .high = -1, .low = 0, }, + { .high = -1, .low = 1, }, + { .high = 0, .low = 1, }, + }; + struct bcm43xx_lopair lowest_transition = { + .high = in_pair->high, + .low = in_pair->low, + }; + struct bcm43xx_lopair tmp_pair; + struct bcm43xx_lopair transition; + int i = 12; + int state = 0; + int found_lower; + int j, begin, end; + u32 lowest_deviation; + u32 tmp; + + /* Note that in_pair and out_pair can point to the same pair. Be careful. */ + + bcm43xx_lo_write(bcm, &lowest_transition); + lowest_deviation = bcm43xx_phy_lo_g_singledeviation(bcm, r27); + do { + found_lower = 0; + assert(state >= 0 && state <= 8); + if (state == 0) { + begin = 1; + end = 8; + } else if (state % 2 == 0) { + begin = state - 1; + end = state + 1; + } else { + begin = state - 2; + end = state + 2; + } + if (begin < 1) + begin += 8; + if (end > 8) + end -= 8; + + j = begin; + tmp_pair.high = lowest_transition.high; + tmp_pair.low = lowest_transition.low; + while (1) { + assert(j >= 1 && j <= 8); + transition.high = tmp_pair.high + transitions[j - 1].high; + transition.low = tmp_pair.low + transitions[j - 1].low; + if ((abs(transition.low) < 9) && (abs(transition.high) < 9)) { + bcm43xx_lo_write(bcm, &transition); + tmp = bcm43xx_phy_lo_g_singledeviation(bcm, r27); + if (tmp < lowest_deviation) { + lowest_deviation = tmp; + state = j; + found_lower = 1; + + lowest_transition.high = transition.high; + lowest_transition.low = transition.low; + } + } + if (j == end) + break; + if (j == 8) + j = 1; + else + j++; + } + } while (i-- && found_lower); + + out_pair->high = lowest_transition.high; + out_pair->low = lowest_transition.low; +} + +/* Set the baseband attenuation value on chip. */ +void bcm43xx_phy_set_baseband_attenuation(struct bcm43xx_private *bcm, + u16 baseband_attenuation) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + u16 value; + + if (phy->version == 0) { + value = (bcm43xx_read16(bcm, 0x03E6) & 0xFFF0); + value |= (baseband_attenuation & 0x000F); + bcm43xx_write16(bcm, 0x03E6, value); + return; + } + + if (phy->version > 1) { + value = bcm43xx_phy_read(bcm, 0x0060) & ~0x003C; + value |= (baseband_attenuation << 2) & 0x003C; + } else { + value = bcm43xx_phy_read(bcm, 0x0060) & ~0x0078; + value |= (baseband_attenuation << 3) & 0x0078; + } + bcm43xx_phy_write(bcm, 0x0060, value); +} + +/* http://bcm-specs.sipsolutions.net/LocalOscillator/Measure */ +void bcm43xx_phy_lo_g_measure(struct bcm43xx_private *bcm) +{ + static const u8 pairorder[10] = { 3, 1, 5, 7, 9, 2, 0, 4, 6, 8 }; + const int is_initializing = bcm43xx_is_initializing(bcm); + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + u16 h, i, oldi = 0, j; + struct bcm43xx_lopair control; + struct bcm43xx_lopair *tmp_control; + u16 tmp; + u16 regstack[16] = { 0 }; + u8 oldchannel; + + //XXX: What are these? + u8 r27 = 0, r31; + + oldchannel = radio->channel; + /* Setup */ + if (phy->connected) { + regstack[0] = bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS); + regstack[1] = bcm43xx_phy_read(bcm, 0x0802); + bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, regstack[0] & 0x7FFF); + bcm43xx_phy_write(bcm, 0x0802, regstack[1] & 0xFFFC); + } + regstack[3] = bcm43xx_read16(bcm, 0x03E2); + bcm43xx_write16(bcm, 0x03E2, regstack[3] | 0x8000); + regstack[4] = bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT); + regstack[5] = bcm43xx_phy_read(bcm, 0x15); + regstack[6] = bcm43xx_phy_read(bcm, 0x2A); + regstack[7] = bcm43xx_phy_read(bcm, 0x35); + regstack[8] = bcm43xx_phy_read(bcm, 0x60); + regstack[9] = bcm43xx_radio_read16(bcm, 0x43); + regstack[10] = bcm43xx_radio_read16(bcm, 0x7A); + regstack[11] = bcm43xx_radio_read16(bcm, 0x52); + if (phy->connected) { + regstack[12] = bcm43xx_phy_read(bcm, 0x0811); + regstack[13] = bcm43xx_phy_read(bcm, 0x0812); + regstack[14] = bcm43xx_phy_read(bcm, 0x0814); + regstack[15] = bcm43xx_phy_read(bcm, 0x0815); + } + bcm43xx_radio_selectchannel(bcm, 6, 0); + if (phy->connected) { + bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, regstack[0] & 0x7FFF); + bcm43xx_phy_write(bcm, 0x0802, regstack[1] & 0xFFFC); + bcm43xx_dummy_transmission(bcm); + } + bcm43xx_radio_write16(bcm, 0x0043, 0x0006); + + bcm43xx_phy_set_baseband_attenuation(bcm, 2); + + bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, 0x0000); + bcm43xx_phy_write(bcm, 0x002E, 0x007F); + bcm43xx_phy_write(bcm, 0x080F, 0x0078); + bcm43xx_phy_write(bcm, 0x0035, regstack[7] & ~(1 << 7)); + bcm43xx_radio_write16(bcm, 0x007A, regstack[10] & 0xFFF0); + bcm43xx_phy_write(bcm, 0x002B, 0x0203); + bcm43xx_phy_write(bcm, 0x002A, 0x08A3); + if (phy->connected) { + bcm43xx_phy_write(bcm, 0x0814, regstack[14] | 0x0003); + bcm43xx_phy_write(bcm, 0x0815, regstack[15] & 0xFFFC); + bcm43xx_phy_write(bcm, 0x0811, 0x01B3); + bcm43xx_phy_write(bcm, 0x0812, 0x00B2); + } + if (is_initializing) + bcm43xx_phy_lo_g_measure_txctl2(bcm); + bcm43xx_phy_write(bcm, 0x080F, 0x8078); + + /* Measure */ + control.low = 0; + control.high = 0; + for (h = 0; h < 10; h++) { + /* Loop over each possible RadioAttenuation (0-9) */ + i = pairorder[h]; + if (is_initializing) { + if (i == 3) { + control.low = 0; + control.high = 0; + } else if (((i % 2 == 1) && (oldi % 2 == 1)) || + ((i % 2 == 0) && (oldi % 2 == 0))) { + tmp_control = bcm43xx_get_lopair(phy, oldi, 0); + memcpy(&control, tmp_control, sizeof(control)); + } else { + tmp_control = bcm43xx_get_lopair(phy, 3, 0); + memcpy(&control, tmp_control, sizeof(control)); + } + } + /* Loop over each possible BasebandAttenuation/2 */ + for (j = 0; j < 4; j++) { + if (is_initializing) { + tmp = i * 2 + j; + r27 = 0; + r31 = 0; + if (tmp > 14) { + r31 = 1; + if (tmp > 17) + r27 = 1; + if (tmp > 19) + r27 = 2; + } + } else { + tmp_control = bcm43xx_get_lopair(phy, i, j * 2); + if (!tmp_control->used) + continue; + memcpy(&control, tmp_control, sizeof(control)); + r27 = 3; + r31 = 0; + } + bcm43xx_radio_write16(bcm, 0x43, i); + bcm43xx_radio_write16(bcm, 0x52, radio->txctl2); + udelay(10); + + bcm43xx_phy_set_baseband_attenuation(bcm, j * 2); + + tmp = (regstack[10] & 0xFFF0); + if (r31) + tmp |= 0x0008; + bcm43xx_radio_write16(bcm, 0x007A, tmp); + + tmp_control = bcm43xx_get_lopair(phy, i, j * 2); + bcm43xx_phy_lo_g_state(bcm, &control, tmp_control, r27); + } + oldi = i; + } + /* Loop over each possible RadioAttenuation (10-13) */ + for (i = 10; i < 14; i++) { + /* Loop over each possible BasebandAttenuation/2 */ + for (j = 0; j < 4; j++) { + if (is_initializing) { + tmp_control = bcm43xx_get_lopair(phy, i - 9, j * 2); + memcpy(&control, tmp_control, sizeof(control)); + tmp = (i - 9) * 2 + j - 5;//FIXME: This is wrong, as the following if statement can never trigger. + r27 = 0; + r31 = 0; + if (tmp > 14) { + r31 = 1; + if (tmp > 17) + r27 = 1; + if (tmp > 19) + r27 = 2; + } + } else { + tmp_control = bcm43xx_get_lopair(phy, i - 9, j * 2); + if (!tmp_control->used) + continue; + memcpy(&control, tmp_control, sizeof(control)); + r27 = 3; + r31 = 0; + } + bcm43xx_radio_write16(bcm, 0x43, i - 9); + bcm43xx_radio_write16(bcm, 0x52, + radio->txctl2 + | (3/*txctl1*/ << 4));//FIXME: shouldn't txctl1 be zero here and 3 in the loop above? + udelay(10); + + bcm43xx_phy_set_baseband_attenuation(bcm, j * 2); + + tmp = (regstack[10] & 0xFFF0); + if (r31) + tmp |= 0x0008; + bcm43xx_radio_write16(bcm, 0x7A, tmp); + + tmp_control = bcm43xx_get_lopair(phy, i, j * 2); + bcm43xx_phy_lo_g_state(bcm, &control, tmp_control, r27); + } + } + + /* Restoration */ + if (phy->connected) { + bcm43xx_phy_write(bcm, 0x0015, 0xE300); + bcm43xx_phy_write(bcm, 0x0812, (r27 << 8) | 0xA0); + udelay(5); + bcm43xx_phy_write(bcm, 0x0812, (r27 << 8) | 0xA2); + udelay(2); + bcm43xx_phy_write(bcm, 0x0812, (r27 << 8) | 0xA3); + } else + bcm43xx_phy_write(bcm, 0x0015, r27 | 0xEFA0); + bcm43xx_phy_lo_adjust(bcm, is_initializing); + bcm43xx_phy_write(bcm, 0x002E, 0x807F); + if (phy->connected) + bcm43xx_phy_write(bcm, 0x002F, 0x0202); + else + bcm43xx_phy_write(bcm, 0x002F, 0x0101); + bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, regstack[4]); + bcm43xx_phy_write(bcm, 0x0015, regstack[5]); + bcm43xx_phy_write(bcm, 0x002A, regstack[6]); + bcm43xx_phy_write(bcm, 0x0035, regstack[7]); + bcm43xx_phy_write(bcm, 0x0060, regstack[8]); + bcm43xx_radio_write16(bcm, 0x0043, regstack[9]); + bcm43xx_radio_write16(bcm, 0x007A, regstack[10]); + regstack[11] &= 0x00F0; + regstack[11] |= (bcm43xx_radio_read16(bcm, 0x52) & 0x000F); + bcm43xx_radio_write16(bcm, 0x52, regstack[11]); + bcm43xx_write16(bcm, 0x03E2, regstack[3]); + if (phy->connected) { + bcm43xx_phy_write(bcm, 0x0811, regstack[12]); + bcm43xx_phy_write(bcm, 0x0812, regstack[13]); + bcm43xx_phy_write(bcm, 0x0814, regstack[14]); + bcm43xx_phy_write(bcm, 0x0815, regstack[15]); + bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, regstack[0]); + bcm43xx_phy_write(bcm, 0x0802, regstack[1]); + } + bcm43xx_radio_selectchannel(bcm, oldchannel, 1); + +#ifdef CONFIG_BCM43XX_DEBUG + { + /* Sanity check for all lopairs. */ + for (i = 0; i < BCM43xx_LO_COUNT; i++) { + tmp_control = phy->_lo_pairs + i; + if (tmp_control->low < -8 || tmp_control->low > 8 || + tmp_control->high < -8 || tmp_control->high > 8) { + printk(KERN_WARNING PFX + "WARNING: Invalid LOpair (low: %d, high: %d, index: %d)\n", + tmp_control->low, tmp_control->high, i); + } + } + } +#endif /* CONFIG_BCM43XX_DEBUG */ +} + +static +void bcm43xx_phy_lo_mark_current_used(struct bcm43xx_private *bcm) +{ + struct bcm43xx_lopair *pair; + + pair = bcm43xx_current_lopair(bcm); + pair->used = 1; +} + +void bcm43xx_phy_lo_mark_all_unused(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_lopair *pair; + int i; + + for (i = 0; i < BCM43xx_LO_COUNT; i++) { + pair = phy->_lo_pairs + i; + pair->used = 0; + } +} + +/* http://bcm-specs.sipsolutions.net/EstimatePowerOut + * This function converts a TSSI value to dBm in Q5.2 + */ +static s8 bcm43xx_phy_estimate_power_out(struct bcm43xx_private *bcm, s8 tssi) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + s8 dbm = 0; + s32 tmp; + + tmp = phy->idle_tssi; + tmp += tssi; + tmp -= phy->savedpctlreg; + + switch (phy->type) { + case BCM43xx_PHYTYPE_A: + tmp += 0x80; + tmp = limit_value(tmp, 0x00, 0xFF); + dbm = phy->tssi2dbm[tmp]; + TODO(); //TODO: There's a FIXME on the specs + break; + case BCM43xx_PHYTYPE_B: + case BCM43xx_PHYTYPE_G: + tmp = limit_value(tmp, 0x00, 0x3F); + dbm = phy->tssi2dbm[tmp]; + break; + default: + assert(0); + } + + return dbm; +} + +/* http://bcm-specs.sipsolutions.net/RecalculateTransmissionPower */ +void bcm43xx_phy_xmitpower(struct bcm43xx_private *bcm) +{ + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + + if (phy->savedpctlreg == 0xFFFF) + return; + if ((bcm->board_type == 0x0416) && + (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM)) + return; + + switch (phy->type) { + case BCM43xx_PHYTYPE_A: { + + TODO(); //TODO: Nothing for A PHYs yet :-/ + + break; + } + case BCM43xx_PHYTYPE_B: + case BCM43xx_PHYTYPE_G: { + u16 tmp; + u16 txpower; + s8 v0, v1, v2, v3; + s8 average; + u8 max_pwr; + s16 desired_pwr, estimated_pwr, pwr_adjust; + s16 radio_att_delta, baseband_att_delta; + s16 radio_attenuation, baseband_attenuation; + unsigned long phylock_flags; + + tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0058); + v0 = (s8)(tmp & 0x00FF); + v1 = (s8)((tmp & 0xFF00) >> 8); + tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x005A); + v2 = (s8)(tmp & 0x00FF); + v3 = (s8)((tmp & 0xFF00) >> 8); + tmp = 0; + + if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F || v3 == 0x7F) { + tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0070); + v0 = (s8)(tmp & 0x00FF); + v1 = (s8)((tmp & 0xFF00) >> 8); + tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0072); + v2 = (s8)(tmp & 0x00FF); + v3 = (s8)((tmp & 0xFF00) >> 8); + if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F || v3 == 0x7F) + return; + v0 = (v0 + 0x20) & 0x3F; + v1 = (v1 + 0x20) & 0x3F; + v2 = (v2 + 0x20) & 0x3F; + v3 = (v3 + 0x20) & 0x3F; + tmp = 1; + } + bcm43xx_radio_clear_tssi(bcm); + + average = (v0 + v1 + v2 + v3 + 2) / 4; + + if (tmp && (bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x005E) & 0x8)) + average -= 13; + + estimated_pwr = bcm43xx_phy_estimate_power_out(bcm, average); + + max_pwr = bcm->sprom.maxpower_bgphy; + + if ((bcm->sprom.boardflags & BCM43xx_BFL_PACTRL) && + (phy->type == BCM43xx_PHYTYPE_G)) + max_pwr -= 0x3; + + /*TODO: + max_pwr = min(REG - bcm->sprom.antennagain_bgphy - 0x6, max_pwr) + where REG is the max power as per the regulatory domain + */ + + desired_pwr = limit_value(radio->txpower_desired, 0, max_pwr); + /* Check if we need to adjust the current power. */ + pwr_adjust = desired_pwr - estimated_pwr; + radio_att_delta = -(pwr_adjust + 7) >> 3; + baseband_att_delta = -(pwr_adjust >> 1) - (4 * radio_att_delta); + if ((radio_att_delta == 0) && (baseband_att_delta == 0)) { + bcm43xx_phy_lo_mark_current_used(bcm); + return; + } + + /* Calculate the new attenuation values. */ + baseband_attenuation = radio->baseband_atten; + baseband_attenuation += baseband_att_delta; + radio_attenuation = radio->radio_atten; + radio_attenuation += radio_att_delta; + + /* Get baseband and radio attenuation values into their permitted ranges. + * baseband 0-11, radio 0-9. + * Radio attenuation affects power level 4 times as much as baseband. + */ + if (radio_attenuation < 0) { + baseband_attenuation -= (4 * -radio_attenuation); + radio_attenuation = 0; + } else if (radio_attenuation > 9) { + baseband_attenuation += (4 * (radio_attenuation - 9)); + radio_attenuation = 9; + } else { + while (baseband_attenuation < 0 && radio_attenuation > 0) { + baseband_attenuation += 4; + radio_attenuation--; + } + while (baseband_attenuation > 11 && radio_attenuation < 9) { + baseband_attenuation -= 4; + radio_attenuation++; + } + } + baseband_attenuation = limit_value(baseband_attenuation, 0, 11); + + txpower = radio->txctl1; + if ((radio->version == 0x2050) && (radio->revision == 2)) { + if (radio_attenuation <= 1) { + if (txpower == 0) { + txpower = 3; + radio_attenuation += 2; + baseband_attenuation += 2; + } else if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL) { + baseband_attenuation += 4 * (radio_attenuation - 2); + radio_attenuation = 2; + } + } else if (radio_attenuation > 4 && txpower != 0) { + txpower = 0; + if (baseband_attenuation < 3) { + radio_attenuation -= 3; + baseband_attenuation += 2; + } else { + radio_attenuation -= 2; + baseband_attenuation -= 2; + } + } + } + radio->txctl1 = txpower; + baseband_attenuation = limit_value(baseband_attenuation, 0, 11); + radio_attenuation = limit_value(radio_attenuation, 0, 9); + + bcm43xx_phy_lock(bcm, phylock_flags); + bcm43xx_radio_lock(bcm); + bcm43xx_radio_set_txpower_bg(bcm, baseband_attenuation, + radio_attenuation, txpower); + bcm43xx_phy_lo_mark_current_used(bcm); + bcm43xx_radio_unlock(bcm); + bcm43xx_phy_unlock(bcm, phylock_flags); + break; + } + default: + assert(0); + } +} + +static inline +s32 bcm43xx_tssi2dbm_ad(s32 num, s32 den) +{ + if (num < 0) + return num/den; + else + return (num+den/2)/den; +} + +static inline +s8 bcm43xx_tssi2dbm_entry(s8 entry [], u8 index, s16 pab0, s16 pab1, s16 pab2) +{ + s32 m1, m2, f = 256, q, delta; + s8 i = 0; + + m1 = bcm43xx_tssi2dbm_ad(16 * pab0 + index * pab1, 32); + m2 = max(bcm43xx_tssi2dbm_ad(32768 + index * pab2, 256), 1); + do { + if (i > 15) + return -EINVAL; + q = bcm43xx_tssi2dbm_ad(f * 4096 - + bcm43xx_tssi2dbm_ad(m2 * f, 16) * f, 2048); + delta = abs(q - f); + f = q; + i++; + } while (delta >= 2); + entry[index] = limit_value(bcm43xx_tssi2dbm_ad(m1 * f, 8192), -127, 128); + return 0; +} + +/* http://bcm-specs.sipsolutions.net/TSSI_to_DBM_Table */ +int bcm43xx_phy_init_tssi2dbm_table(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + s16 pab0, pab1, pab2; + u8 idx; + s8 *dyn_tssi2dbm; + + if (phy->type == BCM43xx_PHYTYPE_A) { + pab0 = (s16)(bcm->sprom.pa1b0); + pab1 = (s16)(bcm->sprom.pa1b1); + pab2 = (s16)(bcm->sprom.pa1b2); + } else { + pab0 = (s16)(bcm->sprom.pa0b0); + pab1 = (s16)(bcm->sprom.pa0b1); + pab2 = (s16)(bcm->sprom.pa0b2); + } + + if ((bcm->chip_id == 0x4301) && (radio->version != 0x2050)) { + phy->idle_tssi = 0x34; + phy->tssi2dbm = bcm43xx_tssi2dbm_b_table; + return 0; + } + + if (pab0 != 0 && pab1 != 0 && pab2 != 0 && + pab0 != -1 && pab1 != -1 && pab2 != -1) { + /* The pabX values are set in SPROM. Use them. */ + if (phy->type == BCM43xx_PHYTYPE_A) { + if ((s8)bcm->sprom.idle_tssi_tgt_aphy != 0 && + (s8)bcm->sprom.idle_tssi_tgt_aphy != -1) + phy->idle_tssi = (s8)(bcm->sprom.idle_tssi_tgt_aphy); + else + phy->idle_tssi = 62; + } else { + if ((s8)bcm->sprom.idle_tssi_tgt_bgphy != 0 && + (s8)bcm->sprom.idle_tssi_tgt_bgphy != -1) + phy->idle_tssi = (s8)(bcm->sprom.idle_tssi_tgt_bgphy); + else + phy->idle_tssi = 62; + } + dyn_tssi2dbm = kmalloc(64, GFP_KERNEL); + if (dyn_tssi2dbm == NULL) { + printk(KERN_ERR PFX "Could not allocate memory" + "for tssi2dbm table\n"); + return -ENOMEM; + } + for (idx = 0; idx < 64; idx++) + if (bcm43xx_tssi2dbm_entry(dyn_tssi2dbm, idx, pab0, pab1, pab2)) { + phy->tssi2dbm = NULL; + printk(KERN_ERR PFX "Could not generate " + "tssi2dBm table\n"); + return -ENODEV; + } + phy->tssi2dbm = dyn_tssi2dbm; + phy->dyn_tssi_tbl = 1; + } else { + /* pabX values not set in SPROM. */ + switch (phy->type) { + case BCM43xx_PHYTYPE_A: + /* APHY needs a generated table. */ + phy->tssi2dbm = NULL; + printk(KERN_ERR PFX "Could not generate tssi2dBm " + "table (wrong SPROM info)!\n"); + return -ENODEV; + case BCM43xx_PHYTYPE_B: + phy->idle_tssi = 0x34; + phy->tssi2dbm = bcm43xx_tssi2dbm_b_table; + break; + case BCM43xx_PHYTYPE_G: + phy->idle_tssi = 0x34; + phy->tssi2dbm = bcm43xx_tssi2dbm_g_table; + break; + } + } + + return 0; +} + +int bcm43xx_phy_init(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + int err = -ENODEV; + unsigned long flags; + + /* We do not want to be preempted while calibrating + * the hardware. + */ + local_irq_save(flags); + + switch (phy->type) { + case BCM43xx_PHYTYPE_A: + if (phy->rev == 2 || phy->rev == 3) { + bcm43xx_phy_inita(bcm); + err = 0; + } + break; + case BCM43xx_PHYTYPE_B: + switch (phy->rev) { + case 2: + bcm43xx_phy_initb2(bcm); + err = 0; + break; + case 4: + bcm43xx_phy_initb4(bcm); + err = 0; + break; + case 5: + bcm43xx_phy_initb5(bcm); + err = 0; + break; + case 6: + bcm43xx_phy_initb6(bcm); + err = 0; + break; + } + break; + case BCM43xx_PHYTYPE_G: + bcm43xx_phy_initg(bcm); + err = 0; + break; + } + local_irq_restore(flags); + if (err) + printk(KERN_WARNING PFX "Unknown PHYTYPE found!\n"); + + return err; +} + +void bcm43xx_phy_set_antenna_diversity(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + u16 antennadiv; + u16 offset; + u16 value; + u32 ucodeflags; + + antennadiv = phy->antenna_diversity; + + if (antennadiv == 0xFFFF) + antennadiv = 3; + assert(antennadiv <= 3); + + ucodeflags = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, + BCM43xx_UCODEFLAGS_OFFSET); + bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, + BCM43xx_UCODEFLAGS_OFFSET, + ucodeflags & ~BCM43xx_UCODEFLAG_AUTODIV); + + switch (phy->type) { + case BCM43xx_PHYTYPE_A: + case BCM43xx_PHYTYPE_G: + if (phy->type == BCM43xx_PHYTYPE_A) + offset = 0x0000; + else + offset = 0x0400; + + if (antennadiv == 2) + value = (3/*automatic*/ << 7); + else + value = (antennadiv << 7); + bcm43xx_phy_write(bcm, offset + 1, + (bcm43xx_phy_read(bcm, offset + 1) + & 0x7E7F) | value); + + if (antennadiv >= 2) { + if (antennadiv == 2) + value = (antennadiv << 7); + else + value = (0/*force0*/ << 7); + bcm43xx_phy_write(bcm, offset + 0x2B, + (bcm43xx_phy_read(bcm, offset + 0x2B) + & 0xFEFF) | value); + } + + if (phy->type == BCM43xx_PHYTYPE_G) { + if (antennadiv >= 2) + bcm43xx_phy_write(bcm, 0x048C, + bcm43xx_phy_read(bcm, 0x048C) + | 0x2000); + else + bcm43xx_phy_write(bcm, 0x048C, + bcm43xx_phy_read(bcm, 0x048C) + & ~0x2000); + if (phy->rev >= 2) { + bcm43xx_phy_write(bcm, 0x0461, + bcm43xx_phy_read(bcm, 0x0461) + | 0x0010); + bcm43xx_phy_write(bcm, 0x04AD, + (bcm43xx_phy_read(bcm, 0x04AD) + & 0x00FF) | 0x0015); + if (phy->rev == 2) + bcm43xx_phy_write(bcm, 0x0427, 0x0008); + else + bcm43xx_phy_write(bcm, 0x0427, + (bcm43xx_phy_read(bcm, 0x0427) + & 0x00FF) | 0x0008); + } + else if (phy->rev >= 6) + bcm43xx_phy_write(bcm, 0x049B, 0x00DC); + } else { + if (phy->rev < 3) + bcm43xx_phy_write(bcm, 0x002B, + (bcm43xx_phy_read(bcm, 0x002B) + & 0x00FF) | 0x0024); + else { + bcm43xx_phy_write(bcm, 0x0061, + bcm43xx_phy_read(bcm, 0x0061) + | 0x0010); + if (phy->rev == 3) { + bcm43xx_phy_write(bcm, 0x0093, 0x001D); + bcm43xx_phy_write(bcm, 0x0027, 0x0008); + } else { + bcm43xx_phy_write(bcm, 0x0093, 0x003A); + bcm43xx_phy_write(bcm, 0x0027, + (bcm43xx_phy_read(bcm, 0x0027) + & 0x00FF) | 0x0008); + } + } + } + break; + case BCM43xx_PHYTYPE_B: + if (bcm->current_core->rev == 2) + value = (3/*automatic*/ << 7); + else + value = (antennadiv << 7); + bcm43xx_phy_write(bcm, 0x03E2, + (bcm43xx_phy_read(bcm, 0x03E2) + & 0xFE7F) | value); + break; + default: + assert(0); + } + + if (antennadiv >= 2) { + ucodeflags = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, + BCM43xx_UCODEFLAGS_OFFSET); + bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, + BCM43xx_UCODEFLAGS_OFFSET, + ucodeflags | BCM43xx_UCODEFLAG_AUTODIV); + } + + phy->antenna_diversity = antennadiv; +} diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_phy.h b/drivers/net/wireless/bcm43xx/bcm43xx_phy.h new file mode 100644 index 000000000000..1f321ef42be8 --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_phy.h @@ -0,0 +1,74 @@ +/* + + Broadcom BCM43xx wireless driver + + Copyright (c) 2005 Martin Langer , + Stefano Brivio + Michael Buesch + Danny van Dyk + Andreas Jaggi + + Some parts of the code in this file are derived from the ipw2200 + driver Copyright(c) 2003 - 2004 Intel Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#ifndef BCM43xx_PHY_H_ +#define BCM43xx_PHY_H_ + +#include + +struct bcm43xx_private; + +void bcm43xx_raw_phy_lock(struct bcm43xx_private *bcm); +#define bcm43xx_phy_lock(bcm, flags) \ + do { \ + local_irq_save(flags); \ + bcm43xx_raw_phy_lock(bcm); \ + } while (0) +void bcm43xx_raw_phy_unlock(struct bcm43xx_private *bcm); +#define bcm43xx_phy_unlock(bcm, flags) \ + do { \ + bcm43xx_raw_phy_unlock(bcm); \ + local_irq_restore(flags); \ + } while (0) + +u16 bcm43xx_phy_read(struct bcm43xx_private *bcm, u16 offset); +void bcm43xx_phy_write(struct bcm43xx_private *bcm, u16 offset, u16 val); + +int bcm43xx_phy_init_tssi2dbm_table(struct bcm43xx_private *bcm); +int bcm43xx_phy_init(struct bcm43xx_private *bcm); + +void bcm43xx_phy_set_antenna_diversity(struct bcm43xx_private *bcm); +void bcm43xx_phy_calibrate(struct bcm43xx_private *bcm); +int bcm43xx_phy_connect(struct bcm43xx_private *bcm, int connect); + +void bcm43xx_phy_lo_b_measure(struct bcm43xx_private *bcm); +void bcm43xx_phy_lo_g_measure(struct bcm43xx_private *bcm); +void bcm43xx_phy_xmitpower(struct bcm43xx_private *bcm); + +/* Adjust the LocalOscillator to the saved values. + * "fixed" is only set to 1 once in initialization. Set to 0 otherwise. + */ +void bcm43xx_phy_lo_adjust(struct bcm43xx_private *bcm, int fixed); +void bcm43xx_phy_lo_mark_all_unused(struct bcm43xx_private *bcm); + +void bcm43xx_phy_set_baseband_attenuation(struct bcm43xx_private *bcm, + u16 baseband_attenuation); + +#endif /* BCM43xx_PHY_H_ */ diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_pio.c b/drivers/net/wireless/bcm43xx/bcm43xx_pio.c new file mode 100644 index 000000000000..c59ddd40680d --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_pio.c @@ -0,0 +1,606 @@ +/* + + Broadcom BCM43xx wireless driver + + PIO Transmission + + Copyright (c) 2005 Michael Buesch + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "bcm43xx.h" +#include "bcm43xx_pio.h" +#include "bcm43xx_main.h" +#include "bcm43xx_xmit.h" + +#include + + +static void tx_start(struct bcm43xx_pioqueue *queue) +{ + bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, + BCM43xx_PIO_TXCTL_INIT); +} + +static void tx_octet(struct bcm43xx_pioqueue *queue, + u8 octet) +{ + if (queue->need_workarounds) { + bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA, + octet); + bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, + BCM43xx_PIO_TXCTL_WRITEHI); + } else { + bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, + BCM43xx_PIO_TXCTL_WRITEHI); + bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA, + octet); + } +} + +static u16 tx_get_next_word(struct bcm43xx_txhdr *txhdr, + const u8 *packet, + unsigned int *pos) +{ + const u8 *source; + unsigned int i = *pos; + u16 ret; + + if (i < sizeof(*txhdr)) { + source = (const u8 *)txhdr; + } else { + source = packet; + i -= sizeof(*txhdr); + } + ret = le16_to_cpu( *((u16 *)(source + i)) ); + *pos += 2; + + return ret; +} + +static void tx_data(struct bcm43xx_pioqueue *queue, + struct bcm43xx_txhdr *txhdr, + const u8 *packet, + unsigned int octets) +{ + u16 data; + unsigned int i = 0; + + if (queue->need_workarounds) { + data = tx_get_next_word(txhdr, packet, &i); + bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA, data); + } + bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, + BCM43xx_PIO_TXCTL_WRITELO | + BCM43xx_PIO_TXCTL_WRITEHI); + while (i < octets - 1) { + data = tx_get_next_word(txhdr, packet, &i); + bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA, data); + } + if (octets % 2) + tx_octet(queue, packet[octets - sizeof(*txhdr) - 1]); +} + +static void tx_complete(struct bcm43xx_pioqueue *queue, + struct sk_buff *skb) +{ + if (queue->need_workarounds) { + bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA, + skb->data[skb->len - 1]); + bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, + BCM43xx_PIO_TXCTL_WRITEHI | + BCM43xx_PIO_TXCTL_COMPLETE); + } else { + bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL, + BCM43xx_PIO_TXCTL_COMPLETE); + } +} + +static u16 generate_cookie(struct bcm43xx_pioqueue *queue, + int packetindex) +{ + u16 cookie = 0x0000; + + /* We use the upper 4 bits for the PIO + * controller ID and the lower 12 bits + * for the packet index (in the cache). + */ + switch (queue->mmio_base) { + case BCM43xx_MMIO_PIO1_BASE: + break; + case BCM43xx_MMIO_PIO2_BASE: + cookie = 0x1000; + break; + case BCM43xx_MMIO_PIO3_BASE: + cookie = 0x2000; + break; + case BCM43xx_MMIO_PIO4_BASE: + cookie = 0x3000; + break; + default: + assert(0); + } + assert(((u16)packetindex & 0xF000) == 0x0000); + cookie |= (u16)packetindex; + + return cookie; +} + +static +struct bcm43xx_pioqueue * parse_cookie(struct bcm43xx_private *bcm, + u16 cookie, + struct bcm43xx_pio_txpacket **packet) +{ + struct bcm43xx_pio *pio = bcm43xx_current_pio(bcm); + struct bcm43xx_pioqueue *queue = NULL; + int packetindex; + + switch (cookie & 0xF000) { + case 0x0000: + queue = pio->queue0; + break; + case 0x1000: + queue = pio->queue1; + break; + case 0x2000: + queue = pio->queue2; + break; + case 0x3000: + queue = pio->queue3; + break; + default: + assert(0); + } + packetindex = (cookie & 0x0FFF); + assert(packetindex >= 0 && packetindex < BCM43xx_PIO_MAXTXPACKETS); + *packet = &(queue->tx_packets_cache[packetindex]); + + return queue; +} + +static void pio_tx_write_fragment(struct bcm43xx_pioqueue *queue, + struct sk_buff *skb, + struct bcm43xx_pio_txpacket *packet) +{ + struct bcm43xx_txhdr txhdr; + unsigned int octets; + + assert(skb_shinfo(skb)->nr_frags == 0); + bcm43xx_generate_txhdr(queue->bcm, + &txhdr, skb->data, skb->len, + (packet->xmitted_frags == 0), + generate_cookie(queue, pio_txpacket_getindex(packet))); + + tx_start(queue); + octets = skb->len + sizeof(txhdr); + if (queue->need_workarounds) + octets--; + tx_data(queue, &txhdr, (u8 *)skb->data, octets); + tx_complete(queue, skb); +} + +static void free_txpacket(struct bcm43xx_pio_txpacket *packet, + int irq_context) +{ + struct bcm43xx_pioqueue *queue = packet->queue; + + ieee80211_txb_free(packet->txb); + list_move(&packet->list, &queue->txfree); + queue->nr_txfree++; + + assert(queue->tx_devq_used >= packet->xmitted_octets); + assert(queue->tx_devq_packets >= packet->xmitted_frags); + queue->tx_devq_used -= packet->xmitted_octets; + queue->tx_devq_packets -= packet->xmitted_frags; +} + +static int pio_tx_packet(struct bcm43xx_pio_txpacket *packet) +{ + struct bcm43xx_pioqueue *queue = packet->queue; + struct ieee80211_txb *txb = packet->txb; + struct sk_buff *skb; + u16 octets; + int i; + + for (i = packet->xmitted_frags; i < txb->nr_frags; i++) { + skb = txb->fragments[i]; + + octets = (u16)skb->len + sizeof(struct bcm43xx_txhdr); + assert(queue->tx_devq_size >= octets); + assert(queue->tx_devq_packets <= BCM43xx_PIO_MAXTXDEVQPACKETS); + assert(queue->tx_devq_used <= queue->tx_devq_size); + /* Check if there is sufficient free space on the device + * TX queue. If not, return and let the TX tasklet + * retry later. + */ + if (queue->tx_devq_packets == BCM43xx_PIO_MAXTXDEVQPACKETS) + return -EBUSY; + if (queue->tx_devq_used + octets > queue->tx_devq_size) + return -EBUSY; + /* Now poke the device. */ + pio_tx_write_fragment(queue, skb, packet); + + /* Account for the packet size. + * (We must not overflow the device TX queue) + */ + queue->tx_devq_packets++; + queue->tx_devq_used += octets; + + assert(packet->xmitted_frags <= packet->txb->nr_frags); + packet->xmitted_frags++; + packet->xmitted_octets += octets; + } + list_move_tail(&packet->list, &queue->txrunning); + + return 0; +} + +static void tx_tasklet(unsigned long d) +{ + struct bcm43xx_pioqueue *queue = (struct bcm43xx_pioqueue *)d; + struct bcm43xx_private *bcm = queue->bcm; + unsigned long flags; + struct bcm43xx_pio_txpacket *packet, *tmp_packet; + int err; + + bcm43xx_lock_mmio(bcm, flags); + list_for_each_entry_safe(packet, tmp_packet, &queue->txqueue, list) { + assert(packet->xmitted_frags < packet->txb->nr_frags); + if (packet->xmitted_frags == 0) { + int i; + struct sk_buff *skb; + + /* Check if the device queue is big + * enough for every fragment. If not, drop the + * whole packet. + */ + for (i = 0; i < packet->txb->nr_frags; i++) { + skb = packet->txb->fragments[i]; + if (unlikely(skb->len > queue->tx_devq_size)) { + dprintkl(KERN_ERR PFX "PIO TX device queue too small. " + "Dropping packet.\n"); + free_txpacket(packet, 1); + goto next_packet; + } + } + } + /* Try to transmit the packet. + * This may not completely succeed. + */ + err = pio_tx_packet(packet); + if (err) + break; + next_packet: + continue; + } + bcm43xx_unlock_mmio(bcm, flags); +} + +static void setup_txqueues(struct bcm43xx_pioqueue *queue) +{ + struct bcm43xx_pio_txpacket *packet; + int i; + + queue->nr_txfree = BCM43xx_PIO_MAXTXPACKETS; + for (i = 0; i < BCM43xx_PIO_MAXTXPACKETS; i++) { + packet = &(queue->tx_packets_cache[i]); + + packet->queue = queue; + INIT_LIST_HEAD(&packet->list); + + list_add(&packet->list, &queue->txfree); + } +} + +static +struct bcm43xx_pioqueue * bcm43xx_setup_pioqueue(struct bcm43xx_private *bcm, + u16 pio_mmio_base) +{ + struct bcm43xx_pioqueue *queue; + u32 value; + u16 qsize; + + queue = kzalloc(sizeof(*queue), GFP_KERNEL); + if (!queue) + goto out; + + queue->bcm = bcm; + queue->mmio_base = pio_mmio_base; + queue->need_workarounds = (bcm->current_core->rev < 3); + + INIT_LIST_HEAD(&queue->txfree); + INIT_LIST_HEAD(&queue->txqueue); + INIT_LIST_HEAD(&queue->txrunning); + tasklet_init(&queue->txtask, tx_tasklet, + (unsigned long)queue); + + value = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); + value |= BCM43xx_SBF_XFER_REG_BYTESWAP; + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, value); + + qsize = bcm43xx_read16(bcm, queue->mmio_base + BCM43xx_PIO_TXQBUFSIZE); + if (qsize <= BCM43xx_PIO_TXQADJUST) { + printk(KERN_ERR PFX "PIO tx device-queue too small (%u)\n", qsize); + goto err_freequeue; + } + qsize -= BCM43xx_PIO_TXQADJUST; + queue->tx_devq_size = qsize; + + setup_txqueues(queue); + +out: + return queue; + +err_freequeue: + kfree(queue); + queue = NULL; + goto out; +} + +static void cancel_transfers(struct bcm43xx_pioqueue *queue) +{ + struct bcm43xx_pio_txpacket *packet, *tmp_packet; + + netif_tx_disable(queue->bcm->net_dev); + assert(queue->bcm->shutting_down); + tasklet_disable(&queue->txtask); + + list_for_each_entry_safe(packet, tmp_packet, &queue->txrunning, list) + free_txpacket(packet, 0); + list_for_each_entry_safe(packet, tmp_packet, &queue->txqueue, list) + free_txpacket(packet, 0); +} + +static void bcm43xx_destroy_pioqueue(struct bcm43xx_pioqueue *queue) +{ + if (!queue) + return; + + cancel_transfers(queue); + kfree(queue); +} + +void bcm43xx_pio_free(struct bcm43xx_private *bcm) +{ + struct bcm43xx_pio *pio; + + if (!bcm43xx_using_pio(bcm)) + return; + pio = bcm43xx_current_pio(bcm); + + bcm43xx_destroy_pioqueue(pio->queue3); + pio->queue3 = NULL; + bcm43xx_destroy_pioqueue(pio->queue2); + pio->queue2 = NULL; + bcm43xx_destroy_pioqueue(pio->queue1); + pio->queue1 = NULL; + bcm43xx_destroy_pioqueue(pio->queue0); + pio->queue0 = NULL; +} + +int bcm43xx_pio_init(struct bcm43xx_private *bcm) +{ + struct bcm43xx_pio *pio = bcm43xx_current_pio(bcm); + struct bcm43xx_pioqueue *queue; + int err = -ENOMEM; + + queue = bcm43xx_setup_pioqueue(bcm, BCM43xx_MMIO_PIO1_BASE); + if (!queue) + goto out; + pio->queue0 = queue; + + queue = bcm43xx_setup_pioqueue(bcm, BCM43xx_MMIO_PIO2_BASE); + if (!queue) + goto err_destroy0; + pio->queue1 = queue; + + queue = bcm43xx_setup_pioqueue(bcm, BCM43xx_MMIO_PIO3_BASE); + if (!queue) + goto err_destroy1; + pio->queue2 = queue; + + queue = bcm43xx_setup_pioqueue(bcm, BCM43xx_MMIO_PIO4_BASE); + if (!queue) + goto err_destroy2; + pio->queue3 = queue; + + if (bcm->current_core->rev < 3) + bcm->irq_savedstate |= BCM43xx_IRQ_PIO_WORKAROUND; + + dprintk(KERN_INFO PFX "PIO initialized\n"); + err = 0; +out: + return err; + +err_destroy2: + bcm43xx_destroy_pioqueue(pio->queue2); + pio->queue2 = NULL; +err_destroy1: + bcm43xx_destroy_pioqueue(pio->queue1); + pio->queue1 = NULL; +err_destroy0: + bcm43xx_destroy_pioqueue(pio->queue0); + pio->queue0 = NULL; + goto out; +} + +int bcm43xx_pio_tx(struct bcm43xx_private *bcm, + struct ieee80211_txb *txb) +{ + struct bcm43xx_pioqueue *queue = bcm43xx_current_pio(bcm)->queue1; + struct bcm43xx_pio_txpacket *packet; + u16 tmp; + + assert(!queue->tx_suspended); + assert(!list_empty(&queue->txfree)); + + tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_TXCTL); + if (tmp & BCM43xx_PIO_TXCTL_SUSPEND) + return -EBUSY; + + packet = list_entry(queue->txfree.next, struct bcm43xx_pio_txpacket, list); + packet->txb = txb; + packet->xmitted_frags = 0; + packet->xmitted_octets = 0; + list_move_tail(&packet->list, &queue->txqueue); + queue->nr_txfree--; + assert(queue->nr_txfree < BCM43xx_PIO_MAXTXPACKETS); + + /* Suspend TX, if we are out of packets in the "free" queue. */ + if (unlikely(list_empty(&queue->txfree))) { + netif_stop_queue(queue->bcm->net_dev); + queue->tx_suspended = 1; + } + + tasklet_schedule(&queue->txtask); + + return 0; +} + +void bcm43xx_pio_handle_xmitstatus(struct bcm43xx_private *bcm, + struct bcm43xx_xmitstatus *status) +{ + struct bcm43xx_pioqueue *queue; + struct bcm43xx_pio_txpacket *packet; + + queue = parse_cookie(bcm, status->cookie, &packet); + assert(queue); +//TODO +if (!queue) +return; + free_txpacket(packet, 1); + if (unlikely(queue->tx_suspended)) { + queue->tx_suspended = 0; + netif_wake_queue(queue->bcm->net_dev); + } + /* If there are packets on the txqueue, poke the tasklet. */ + if (!list_empty(&queue->txqueue)) + tasklet_schedule(&queue->txtask); +} + +static void pio_rx_error(struct bcm43xx_pioqueue *queue, + int clear_buffers, + const char *error) +{ + int i; + + printkl("PIO RX error: %s\n", error); + bcm43xx_pio_write(queue, BCM43xx_PIO_RXCTL, + BCM43xx_PIO_RXCTL_READY); + if (clear_buffers) { + assert(queue->mmio_base == BCM43xx_MMIO_PIO1_BASE); + for (i = 0; i < 15; i++) { + /* Dummy read. */ + bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA); + } + } +} + +void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue) +{ + u16 preamble[21] = { 0 }; + struct bcm43xx_rxhdr *rxhdr; + u16 tmp, len, rxflags2; + int i, preamble_readwords; + struct sk_buff *skb; + +return; + tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXCTL); + if (!(tmp & BCM43xx_PIO_RXCTL_DATAAVAILABLE)) { + dprintkl(KERN_ERR PFX "PIO RX: No data available\n");//TODO: remove this printk. + return; + } + bcm43xx_pio_write(queue, BCM43xx_PIO_RXCTL, + BCM43xx_PIO_RXCTL_DATAAVAILABLE); + + for (i = 0; i < 10; i++) { + tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXCTL); + if (tmp & BCM43xx_PIO_RXCTL_READY) + goto data_ready; + udelay(10); + } + dprintkl(KERN_ERR PFX "PIO RX timed out\n"); + return; +data_ready: + +//FIXME: endianess in this function. + len = le16_to_cpu(bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA)); + if (unlikely(len > 0x700)) { + pio_rx_error(queue, 0, "len > 0x700"); + return; + } + if (unlikely(len == 0 && queue->mmio_base != BCM43xx_MMIO_PIO4_BASE)) { + pio_rx_error(queue, 0, "len == 0"); + return; + } + preamble[0] = cpu_to_le16(len); + if (queue->mmio_base == BCM43xx_MMIO_PIO4_BASE) + preamble_readwords = 14 / sizeof(u16); + else + preamble_readwords = 18 / sizeof(u16); + for (i = 0; i < preamble_readwords; i++) { + tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA); + preamble[i + 1] = cpu_to_be16(tmp);//FIXME? + } + rxhdr = (struct bcm43xx_rxhdr *)preamble; + rxflags2 = le16_to_cpu(rxhdr->flags2); + if (unlikely(rxflags2 & BCM43xx_RXHDR_FLAGS2_INVALIDFRAME)) { + pio_rx_error(queue, + (queue->mmio_base == BCM43xx_MMIO_PIO1_BASE), + "invalid frame"); + return; + } + if (queue->mmio_base == BCM43xx_MMIO_PIO4_BASE) { + /* We received an xmit status. */ + struct bcm43xx_hwxmitstatus *hw; + struct bcm43xx_xmitstatus stat; + + hw = (struct bcm43xx_hwxmitstatus *)(preamble + 1); + stat.cookie = le16_to_cpu(hw->cookie); + stat.flags = hw->flags; + stat.cnt1 = hw->cnt1; + stat.cnt2 = hw->cnt2; + stat.seq = le16_to_cpu(hw->seq); + stat.unknown = le16_to_cpu(hw->unknown); + + bcm43xx_debugfs_log_txstat(queue->bcm, &stat); + bcm43xx_pio_handle_xmitstatus(queue->bcm, &stat); + + return; + } + + skb = dev_alloc_skb(len); + if (unlikely(!skb)) { + pio_rx_error(queue, 1, "OOM"); + return; + } + skb_put(skb, len); + for (i = 0; i < len - 1; i += 2) { + tmp = cpu_to_be16(bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA)); + *((u16 *)(skb->data + i)) = tmp; + } + if (len % 2) { + tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA); + skb->data[len - 1] = (tmp & 0x00FF); + if (rxflags2 & BCM43xx_RXHDR_FLAGS2_TYPE2FRAME) + skb->data[0x20] = (tmp & 0xFF00) >> 8; + else + skb->data[0x1E] = (tmp & 0xFF00) >> 8; + } + bcm43xx_rx(queue->bcm, skb, rxhdr); +} diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_pio.h b/drivers/net/wireless/bcm43xx/bcm43xx_pio.h new file mode 100644 index 000000000000..970627bc1769 --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_pio.h @@ -0,0 +1,138 @@ +#ifndef BCM43xx_PIO_H_ +#define BCM43xx_PIO_H_ + +#include "bcm43xx.h" + +#include +#include +#include + + +#define BCM43xx_PIO_TXCTL 0x00 +#define BCM43xx_PIO_TXDATA 0x02 +#define BCM43xx_PIO_TXQBUFSIZE 0x04 +#define BCM43xx_PIO_RXCTL 0x08 +#define BCM43xx_PIO_RXDATA 0x0A + +#define BCM43xx_PIO_TXCTL_WRITEHI (1 << 0) +#define BCM43xx_PIO_TXCTL_WRITELO (1 << 1) +#define BCM43xx_PIO_TXCTL_COMPLETE (1 << 2) +#define BCM43xx_PIO_TXCTL_INIT (1 << 3) +#define BCM43xx_PIO_TXCTL_SUSPEND (1 << 7) + +#define BCM43xx_PIO_RXCTL_DATAAVAILABLE (1 << 0) +#define BCM43xx_PIO_RXCTL_READY (1 << 1) + +/* PIO constants */ +#define BCM43xx_PIO_MAXTXDEVQPACKETS 31 +#define BCM43xx_PIO_TXQADJUST 80 + +/* PIO tuning knobs */ +#define BCM43xx_PIO_MAXTXPACKETS 256 + + + +#ifdef CONFIG_BCM43XX_PIO + + +struct bcm43xx_pioqueue; +struct bcm43xx_xmitstatus; + +struct bcm43xx_pio_txpacket { + struct bcm43xx_pioqueue *queue; + struct ieee80211_txb *txb; + struct list_head list; + + u8 xmitted_frags; + u16 xmitted_octets; +}; + +#define pio_txpacket_getindex(packet) ((int)((packet) - (packet)->queue->tx_packets_cache)) + +struct bcm43xx_pioqueue { + struct bcm43xx_private *bcm; + u16 mmio_base; + + u8 tx_suspended:1, + need_workarounds:1; /* Workarounds needed for core.rev < 3 */ + + /* Adjusted size of the device internal TX buffer. */ + u16 tx_devq_size; + /* Used octets of the device internal TX buffer. */ + u16 tx_devq_used; + /* Used packet slots in the device internal TX buffer. */ + u8 tx_devq_packets; + /* Packets from the txfree list can + * be taken on incoming TX requests. + */ + struct list_head txfree; + unsigned int nr_txfree; + /* Packets on the txqueue are queued, + * but not completely written to the chip, yet. + */ + struct list_head txqueue; + /* Packets on the txrunning queue are completely + * posted to the device. We are waiting for the txstatus. + */ + struct list_head txrunning; + /* Total number or packets sent. + * (This counter can obviously wrap). + */ + unsigned int nr_tx_packets; + struct tasklet_struct txtask; + struct bcm43xx_pio_txpacket tx_packets_cache[BCM43xx_PIO_MAXTXPACKETS]; +}; + +static inline +u16 bcm43xx_pio_read(struct bcm43xx_pioqueue *queue, + u16 offset) +{ + return bcm43xx_read16(queue->bcm, queue->mmio_base + offset); +} + +static inline +void bcm43xx_pio_write(struct bcm43xx_pioqueue *queue, + u16 offset, u16 value) +{ + bcm43xx_write16(queue->bcm, queue->mmio_base + offset, value); +} + + +int bcm43xx_pio_init(struct bcm43xx_private *bcm); +void bcm43xx_pio_free(struct bcm43xx_private *bcm); + +int bcm43xx_pio_tx(struct bcm43xx_private *bcm, + struct ieee80211_txb *txb); +void bcm43xx_pio_handle_xmitstatus(struct bcm43xx_private *bcm, + struct bcm43xx_xmitstatus *status); +void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue); + +#else /* CONFIG_BCM43XX_PIO */ + +static inline +int bcm43xx_pio_init(struct bcm43xx_private *bcm) +{ + return 0; +} +static inline +void bcm43xx_pio_free(struct bcm43xx_private *bcm) +{ +} +static inline +int bcm43xx_pio_tx(struct bcm43xx_private *bcm, + struct ieee80211_txb *txb) +{ + return 0; +} +static inline +void bcm43xx_pio_handle_xmitstatus(struct bcm43xx_private *bcm, + struct bcm43xx_xmitstatus *status) +{ +} +static inline +void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue) +{ +} + +#endif /* CONFIG_BCM43XX_PIO */ +#endif /* BCM43xx_PIO_H_ */ diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_power.c b/drivers/net/wireless/bcm43xx/bcm43xx_power.c new file mode 100644 index 000000000000..3c92b62807c5 --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_power.c @@ -0,0 +1,358 @@ +/* + + Broadcom BCM43xx wireless driver + + Copyright (c) 2005 Martin Langer , + Stefano Brivio + Michael Buesch + Danny van Dyk + Andreas Jaggi + + Some parts of the code in this file are derived from the ipw2200 + driver Copyright(c) 2003 - 2004 Intel Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include + +#include "bcm43xx.h" +#include "bcm43xx_power.h" +#include "bcm43xx_main.h" + + +/* Get max/min slowclock frequency + * as described in http://bcm-specs.sipsolutions.net/PowerControl + */ +static int bcm43xx_pctl_clockfreqlimit(struct bcm43xx_private *bcm, + int get_max) +{ + int limit = 0; + int divisor; + int selection; + int err; + u32 tmp; + struct bcm43xx_coreinfo *old_core; + + if (!(bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL)) + goto out; + old_core = bcm->current_core; + err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon); + if (err) + goto out; + + if (bcm->current_core->rev < 6) { + if ((bcm->bustype == BCM43xx_BUSTYPE_PCMCIA) || + (bcm->bustype == BCM43xx_BUSTYPE_SB)) { + selection = 1; + divisor = 32; + } else { + err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUT, &tmp); + if (err) { + printk(KERN_ERR PFX "clockfreqlimit pcicfg read failure\n"); + goto out_switchback; + } + if (tmp & 0x10) { + /* PCI */ + selection = 2; + divisor = 64; + } else { + /* XTAL */ + selection = 1; + divisor = 32; + } + } + } else if (bcm->current_core->rev < 10) { + selection = (tmp & 0x07); + if (selection) { + tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL); + divisor = 4 * (1 + ((tmp & 0xFFFF0000) >> 16)); + } else + divisor = 1; + } else { + tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SYSCLKCTL); + divisor = 4 * (1 + ((tmp & 0xFFFF0000) >> 16)); + selection = 1; + } + + switch (selection) { + case 0: + /* LPO */ + if (get_max) + limit = 43000; + else + limit = 25000; + break; + case 1: + /* XTAL */ + if (get_max) + limit = 20200000; + else + limit = 19800000; + break; + case 2: + /* PCI */ + if (get_max) + limit = 34000000; + else + limit = 25000000; + break; + default: + assert(0); + } + limit /= divisor; + +out_switchback: + err = bcm43xx_switch_core(bcm, old_core); + assert(err == 0); + +out: + return limit; +} + +/* init power control + * as described in http://bcm-specs.sipsolutions.net/PowerControl + */ +int bcm43xx_pctl_init(struct bcm43xx_private *bcm) +{ + int err, maxfreq; + struct bcm43xx_coreinfo *old_core; + + if (!(bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL)) + return 0; + old_core = bcm->current_core; + err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon); + if (err == -ENODEV) + return 0; + if (err) + goto out; + + maxfreq = bcm43xx_pctl_clockfreqlimit(bcm, 1); + bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_PLLONDELAY, + (maxfreq * 150 + 999999) / 1000000); + bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_FREFSELDELAY, + (maxfreq * 15 + 999999) / 1000000); + + err = bcm43xx_switch_core(bcm, old_core); + assert(err == 0); + +out: + return err; +} + +u16 bcm43xx_pctl_powerup_delay(struct bcm43xx_private *bcm) +{ + u16 delay = 0; + int err; + u32 pll_on_delay; + struct bcm43xx_coreinfo *old_core; + int minfreq; + + if (bcm->bustype != BCM43xx_BUSTYPE_PCI) + goto out; + if (!(bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL)) + goto out; + old_core = bcm->current_core; + err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon); + if (err == -ENODEV) + goto out; + + minfreq = bcm43xx_pctl_clockfreqlimit(bcm, 0); + pll_on_delay = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_PLLONDELAY); + delay = (((pll_on_delay + 2) * 1000000) + (minfreq - 1)) / minfreq; + + err = bcm43xx_switch_core(bcm, old_core); + assert(err == 0); + +out: + return delay; +} + +/* set the powercontrol clock + * as described in http://bcm-specs.sipsolutions.net/PowerControl + */ +int bcm43xx_pctl_set_clock(struct bcm43xx_private *bcm, u16 mode) +{ + int err; + struct bcm43xx_coreinfo *old_core; + u32 tmp; + + old_core = bcm->current_core; + err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon); + if (err == -ENODEV) + return 0; + if (err) + goto out; + + if (bcm->core_chipcommon.rev < 6) { + if (mode == BCM43xx_PCTL_CLK_FAST) { + err = bcm43xx_pctl_set_crystal(bcm, 1); + if (err) + goto out; + } + } else { + if ((bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL) && + (bcm->core_chipcommon.rev < 10)) { + switch (mode) { + case BCM43xx_PCTL_CLK_FAST: + tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL); + tmp = (tmp & ~BCM43xx_PCTL_FORCE_SLOW) | BCM43xx_PCTL_FORCE_PLL; + bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp); + break; + case BCM43xx_PCTL_CLK_SLOW: + tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL); + tmp |= BCM43xx_PCTL_FORCE_SLOW; + bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp); + break; + case BCM43xx_PCTL_CLK_DYNAMIC: + tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL); + tmp &= ~BCM43xx_PCTL_FORCE_SLOW; + tmp |= BCM43xx_PCTL_FORCE_PLL; + tmp &= ~BCM43xx_PCTL_DYN_XTAL; + bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp); + } + } + } + + err = bcm43xx_switch_core(bcm, old_core); + assert(err == 0); + +out: + return err; +} + +int bcm43xx_pctl_set_crystal(struct bcm43xx_private *bcm, int on) +{ + int err; + u32 in, out, outenable; + + err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_IN, &in); + if (err) + goto err_pci; + err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUT, &out); + if (err) + goto err_pci; + err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUTENABLE, &outenable); + if (err) + goto err_pci; + + outenable |= (BCM43xx_PCTL_XTAL_POWERUP | BCM43xx_PCTL_PLL_POWERDOWN); + + if (on) { + if (in & 0x40) + return 0; + + out |= (BCM43xx_PCTL_XTAL_POWERUP | BCM43xx_PCTL_PLL_POWERDOWN); + + err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out); + if (err) + goto err_pci; + err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUTENABLE, outenable); + if (err) + goto err_pci; + udelay(1000); + + out &= ~BCM43xx_PCTL_PLL_POWERDOWN; + err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out); + if (err) + goto err_pci; + udelay(5000); + } else { + if (bcm->current_core->rev < 5) + return 0; + if (bcm->sprom.boardflags & BCM43xx_BFL_XTAL_NOSLOW) + return 0; + +/* XXX: Why BCM43xx_MMIO_RADIO_HWENABLED_xx can't be read at this time? + * err = bcm43xx_switch_core(bcm, bcm->active_80211_core); + * if (err) + * return err; + * if (((bcm->current_core->rev >= 3) && + * (bcm43xx_read32(bcm, BCM43xx_MMIO_RADIO_HWENABLED_HI) & (1 << 16))) || + * ((bcm->current_core->rev < 3) && + * !(bcm43xx_read16(bcm, BCM43xx_MMIO_RADIO_HWENABLED_LO) & (1 << 4)))) + * return 0; + * err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon); + * if (err) + * return err; + */ + + err = bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_SLOW); + if (err) + goto out; + out &= ~BCM43xx_PCTL_XTAL_POWERUP; + out |= BCM43xx_PCTL_PLL_POWERDOWN; + err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out); + if (err) + goto err_pci; + err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUTENABLE, outenable); + if (err) + goto err_pci; + } + +out: + return err; + +err_pci: + printk(KERN_ERR PFX "Error: pctl_set_clock() could not access PCI config space!\n"); + err = -EBUSY; + goto out; +} + +/* Set the PowerSavingControlBits. + * Bitvalues: + * 0 => unset the bit + * 1 => set the bit + * -1 => calculate the bit + */ +void bcm43xx_power_saving_ctl_bits(struct bcm43xx_private *bcm, + int bit25, int bit26) +{ + int i; + u32 status; + +//FIXME: Force 25 to off and 26 to on for now: +bit25 = 0; +bit26 = 1; + + if (bit25 == -1) { + //TODO: If powersave is not off and FIXME is not set and we are not in adhoc + // and thus is not an AP and we are associated, set bit 25 + } + if (bit26 == -1) { + //TODO: If the device is awake or this is an AP, or we are scanning, or FIXME, + // or we are associated, or FIXME, or the latest PS-Poll packet sent was + // successful, set bit26 + } + status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); + if (bit25) + status |= BCM43xx_SBF_PS1; + else + status &= ~BCM43xx_SBF_PS1; + if (bit26) + status |= BCM43xx_SBF_PS2; + else + status &= ~BCM43xx_SBF_PS2; + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status); + if (bit26 && bcm->current_core->rev >= 5) { + for (i = 0; i < 100; i++) { + if (bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0040) != 4) + break; + udelay(10); + } + } +} diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_power.h b/drivers/net/wireless/bcm43xx/bcm43xx_power.h new file mode 100644 index 000000000000..5f63640810bd --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_power.h @@ -0,0 +1,47 @@ +/* + + Broadcom BCM43xx wireless driver + + Copyright (c) 2005 Martin Langer , + Stefano Brivio + Michael Buesch + Danny van Dyk + Andreas Jaggi + + Some parts of the code in this file are derived from the ipw2200 + driver Copyright(c) 2003 - 2004 Intel Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#ifndef BCM43xx_POWER_H_ +#define BCM43xx_POWER_H_ + +#include + + +struct bcm43xx_private; + +int bcm43xx_pctl_init(struct bcm43xx_private *bcm); +int bcm43xx_pctl_set_clock(struct bcm43xx_private *bcm, u16 mode); +int bcm43xx_pctl_set_crystal(struct bcm43xx_private *bcm, int on); +u16 bcm43xx_pctl_powerup_delay(struct bcm43xx_private *bcm); + +void bcm43xx_power_saving_ctl_bits(struct bcm43xx_private *bcm, + int bit25, int bit26); + +#endif /* BCM43xx_POWER_H_ */ diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_radio.c b/drivers/net/wireless/bcm43xx/bcm43xx_radio.c new file mode 100644 index 000000000000..af5c0bff1696 --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_radio.c @@ -0,0 +1,2026 @@ +/* + + Broadcom BCM43xx wireless driver + + Copyright (c) 2005 Martin Langer , + Stefano Brivio + Michael Buesch + Danny van Dyk + Andreas Jaggi + + Some parts of the code in this file are derived from the ipw2200 + driver Copyright(c) 2003 - 2004 Intel Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include + +#include "bcm43xx.h" +#include "bcm43xx_main.h" +#include "bcm43xx_phy.h" +#include "bcm43xx_radio.h" +#include "bcm43xx_ilt.h" + + +/* Table for bcm43xx_radio_calibrationvalue() */ +static const u16 rcc_table[16] = { + 0x0002, 0x0003, 0x0001, 0x000F, + 0x0006, 0x0007, 0x0005, 0x000F, + 0x000A, 0x000B, 0x0009, 0x000F, + 0x000E, 0x000F, 0x000D, 0x000F, +}; + +/* Reverse the bits of a 4bit value. + * Example: 1101 is flipped 1011 + */ +static u16 flip_4bit(u16 value) +{ + u16 flipped = 0x0000; + + assert((value & ~0x000F) == 0x0000); + + flipped |= (value & 0x0001) << 3; + flipped |= (value & 0x0002) << 1; + flipped |= (value & 0x0004) >> 1; + flipped |= (value & 0x0008) >> 3; + + return flipped; +} + +/* Get the freq, as it has to be written to the device. */ +static inline +u16 channel2freq_bg(u8 channel) +{ + /* Frequencies are given as frequencies_bg[index] + 2.4GHz + * Starting with channel 1 + */ + static const u16 frequencies_bg[14] = { + 12, 17, 22, 27, + 32, 37, 42, 47, + 52, 57, 62, 67, + 72, 84, + }; + + assert(channel >= 1 && channel <= 14); + + return frequencies_bg[channel - 1]; +} + +/* Get the freq, as it has to be written to the device. */ +static inline +u16 channel2freq_a(u8 channel) +{ + assert(channel <= 200); + + return (5000 + 5 * channel); +} + +void bcm43xx_radio_lock(struct bcm43xx_private *bcm) +{ + u32 status; + + status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); + status |= BCM43xx_SBF_RADIOREG_LOCK; + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status); + mmiowb(); + udelay(10); +} + +void bcm43xx_radio_unlock(struct bcm43xx_private *bcm) +{ + u32 status; + + bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_VER); /* dummy read */ + status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); + status &= ~BCM43xx_SBF_RADIOREG_LOCK; + bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status); + mmiowb(); +} + +u16 bcm43xx_radio_read16(struct bcm43xx_private *bcm, u16 offset) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + + switch (phy->type) { + case BCM43xx_PHYTYPE_A: + offset |= 0x0040; + break; + case BCM43xx_PHYTYPE_B: + if (radio->version == 0x2053) { + if (offset < 0x70) + offset += 0x80; + else if (offset < 0x80) + offset += 0x70; + } else if (radio->version == 0x2050) { + offset |= 0x80; + } else + assert(0); + break; + case BCM43xx_PHYTYPE_G: + offset |= 0x80; + break; + } + + bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_CONTROL, offset); + return bcm43xx_read16(bcm, BCM43xx_MMIO_RADIO_DATA_LOW); +} + +void bcm43xx_radio_write16(struct bcm43xx_private *bcm, u16 offset, u16 val) +{ + bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_CONTROL, offset); + mmiowb(); + bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_DATA_LOW, val); +} + +static void bcm43xx_set_all_gains(struct bcm43xx_private *bcm, + s16 first, s16 second, s16 third) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + u16 i; + u16 start = 0x08, end = 0x18; + u16 offset = 0x0400; + u16 tmp; + + if (phy->rev <= 1) { + offset = 0x5000; + start = 0x10; + end = 0x20; + } + + for (i = 0; i < 4; i++) + bcm43xx_ilt_write(bcm, offset + i, first); + + for (i = start; i < end; i++) + bcm43xx_ilt_write(bcm, offset + i, second); + + if (third != -1) { + tmp = ((u16)third << 14) | ((u16)third << 6); + bcm43xx_phy_write(bcm, 0x04A0, + (bcm43xx_phy_read(bcm, 0x04A0) & 0xBFBF) | tmp); + bcm43xx_phy_write(bcm, 0x04A1, + (bcm43xx_phy_read(bcm, 0x04A1) & 0xBFBF) | tmp); + bcm43xx_phy_write(bcm, 0x04A2, + (bcm43xx_phy_read(bcm, 0x04A2) & 0xBFBF) | tmp); + } + bcm43xx_dummy_transmission(bcm); +} + +static void bcm43xx_set_original_gains(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + u16 i, tmp; + u16 offset = 0x0400; + u16 start = 0x0008, end = 0x0018; + + if (phy->rev <= 1) { + offset = 0x5000; + start = 0x0010; + end = 0x0020; + } + + for (i = 0; i < 4; i++) { + tmp = (i & 0xFFFC); + tmp |= (i & 0x0001) << 1; + tmp |= (i & 0x0002) >> 1; + + bcm43xx_ilt_write(bcm, offset + i, tmp); + } + + for (i = start; i < end; i++) + bcm43xx_ilt_write(bcm, offset + i, i - start); + + bcm43xx_phy_write(bcm, 0x04A0, + (bcm43xx_phy_read(bcm, 0x04A0) & 0xBFBF) | 0x4040); + bcm43xx_phy_write(bcm, 0x04A1, + (bcm43xx_phy_read(bcm, 0x04A1) & 0xBFBF) | 0x4040); + bcm43xx_phy_write(bcm, 0x04A2, + (bcm43xx_phy_read(bcm, 0x04A2) & 0xBFBF) | 0x4000); + bcm43xx_dummy_transmission(bcm); +} + +/* Synthetic PU workaround */ +static void bcm43xx_synth_pu_workaround(struct bcm43xx_private *bcm, u8 channel) +{ + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + + if (radio->version != 0x2050 || radio->revision >= 6) { + /* We do not need the workaround. */ + return; + } + + if (channel <= 10) { + bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL, + channel2freq_bg(channel + 4)); + } else { + bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL, + channel2freq_bg(1)); + } + udelay(100); + bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL, + channel2freq_bg(channel)); +} + +u8 bcm43xx_radio_aci_detect(struct bcm43xx_private *bcm, u8 channel) +{ + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + u8 ret = 0; + u16 saved, rssi, temp; + int i, j = 0; + + saved = bcm43xx_phy_read(bcm, 0x0403); + bcm43xx_radio_selectchannel(bcm, channel, 0); + bcm43xx_phy_write(bcm, 0x0403, (saved & 0xFFF8) | 5); + if (radio->aci_hw_rssi) + rssi = bcm43xx_phy_read(bcm, 0x048A) & 0x3F; + else + rssi = saved & 0x3F; + /* clamp temp to signed 5bit */ + if (rssi > 32) + rssi -= 64; + for (i = 0;i < 100; i++) { + temp = (bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x3F; + if (temp > 32) + temp -= 64; + if (temp < rssi) + j++; + if (j >= 20) + ret = 1; + } + bcm43xx_phy_write(bcm, 0x0403, saved); + + return ret; +} + +u8 bcm43xx_radio_aci_scan(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + u8 ret[13]; + unsigned int channel = radio->channel; + unsigned int i, j, start, end; + unsigned long phylock_flags; + + if (!((phy->type == BCM43xx_PHYTYPE_G) && (phy->rev > 0))) + return 0; + + bcm43xx_phy_lock(bcm, phylock_flags); + bcm43xx_radio_lock(bcm); + bcm43xx_phy_write(bcm, 0x0802, + bcm43xx_phy_read(bcm, 0x0802) & 0xFFFC); + bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, + bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & 0x7FFF); + bcm43xx_set_all_gains(bcm, 3, 8, 1); + + start = (channel - 5 > 0) ? channel - 5 : 1; + end = (channel + 5 < 14) ? channel + 5 : 13; + + for (i = start; i <= end; i++) { + if (abs(channel - i) > 2) + ret[i-1] = bcm43xx_radio_aci_detect(bcm, i); + } + bcm43xx_radio_selectchannel(bcm, channel, 0); + bcm43xx_phy_write(bcm, 0x0802, + (bcm43xx_phy_read(bcm, 0x0802) & 0xFFFC) | 0x0003); + bcm43xx_phy_write(bcm, 0x0403, + bcm43xx_phy_read(bcm, 0x0403) & 0xFFF8); + bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, + bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) | 0x8000); + bcm43xx_set_original_gains(bcm); + for (i = 0; i < 13; i++) { + if (!ret[i]) + continue; + end = (i + 5 < 13) ? i + 5 : 13; + for (j = i; j < end; j++) + ret[j] = 1; + } + bcm43xx_radio_unlock(bcm); + bcm43xx_phy_unlock(bcm, phylock_flags); + + return ret[channel - 1]; +} + +/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ +void bcm43xx_nrssi_hw_write(struct bcm43xx_private *bcm, u16 offset, s16 val) +{ + bcm43xx_phy_write(bcm, BCM43xx_PHY_NRSSILT_CTRL, offset); + mmiowb(); + bcm43xx_phy_write(bcm, BCM43xx_PHY_NRSSILT_DATA, (u16)val); +} + +/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ +s16 bcm43xx_nrssi_hw_read(struct bcm43xx_private *bcm, u16 offset) +{ + u16 val; + + bcm43xx_phy_write(bcm, BCM43xx_PHY_NRSSILT_CTRL, offset); + val = bcm43xx_phy_read(bcm, BCM43xx_PHY_NRSSILT_DATA); + + return (s16)val; +} + +/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ +void bcm43xx_nrssi_hw_update(struct bcm43xx_private *bcm, u16 val) +{ + u16 i; + s16 tmp; + + for (i = 0; i < 64; i++) { + tmp = bcm43xx_nrssi_hw_read(bcm, i); + tmp -= val; + tmp = limit_value(tmp, -32, 31); + bcm43xx_nrssi_hw_write(bcm, i, tmp); + } +} + +/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */ +void bcm43xx_nrssi_mem_update(struct bcm43xx_private *bcm) +{ + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + s16 i, delta; + s32 tmp; + + delta = 0x1F - radio->nrssi[0]; + for (i = 0; i < 64; i++) { + tmp = (i - delta) * radio->nrssislope; + tmp /= 0x10000; + tmp += 0x3A; + tmp = limit_value(tmp, 0, 0x3F); + radio->nrssi_lt[i] = tmp; + } +} + +static void bcm43xx_calc_nrssi_offset(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + u16 backup[20] = { 0 }; + s16 v47F; + u16 i; + u16 saved = 0xFFFF; + + backup[0] = bcm43xx_phy_read(bcm, 0x0001); + backup[1] = bcm43xx_phy_read(bcm, 0x0811); + backup[2] = bcm43xx_phy_read(bcm, 0x0812); + backup[3] = bcm43xx_phy_read(bcm, 0x0814); + backup[4] = bcm43xx_phy_read(bcm, 0x0815); + backup[5] = bcm43xx_phy_read(bcm, 0x005A); + backup[6] = bcm43xx_phy_read(bcm, 0x0059); + backup[7] = bcm43xx_phy_read(bcm, 0x0058); + backup[8] = bcm43xx_phy_read(bcm, 0x000A); + backup[9] = bcm43xx_phy_read(bcm, 0x0003); + backup[10] = bcm43xx_radio_read16(bcm, 0x007A); + backup[11] = bcm43xx_radio_read16(bcm, 0x0043); + + bcm43xx_phy_write(bcm, 0x0429, + bcm43xx_phy_read(bcm, 0x0429) & 0x7FFF); + bcm43xx_phy_write(bcm, 0x0001, + (bcm43xx_phy_read(bcm, 0x0001) & 0x3FFF) | 0x4000); + bcm43xx_phy_write(bcm, 0x0811, + bcm43xx_phy_read(bcm, 0x0811) | 0x000C); + bcm43xx_phy_write(bcm, 0x0812, + (bcm43xx_phy_read(bcm, 0x0812) & 0xFFF3) | 0x0004); + bcm43xx_phy_write(bcm, 0x0802, + bcm43xx_phy_read(bcm, 0x0802) & ~(0x1 | 0x2)); + if (phy->rev >= 6) { + backup[12] = bcm43xx_phy_read(bcm, 0x002E); + backup[13] = bcm43xx_phy_read(bcm, 0x002F); + backup[14] = bcm43xx_phy_read(bcm, 0x080F); + backup[15] = bcm43xx_phy_read(bcm, 0x0810); + backup[16] = bcm43xx_phy_read(bcm, 0x0801); + backup[17] = bcm43xx_phy_read(bcm, 0x0060); + backup[18] = bcm43xx_phy_read(bcm, 0x0014); + backup[19] = bcm43xx_phy_read(bcm, 0x0478); + + bcm43xx_phy_write(bcm, 0x002E, 0); + bcm43xx_phy_write(bcm, 0x002F, 0); + bcm43xx_phy_write(bcm, 0x080F, 0); + bcm43xx_phy_write(bcm, 0x0810, 0); + bcm43xx_phy_write(bcm, 0x0478, + bcm43xx_phy_read(bcm, 0x0478) | 0x0100); + bcm43xx_phy_write(bcm, 0x0801, + bcm43xx_phy_read(bcm, 0x0801) | 0x0040); + bcm43xx_phy_write(bcm, 0x0060, + bcm43xx_phy_read(bcm, 0x0060) | 0x0040); + bcm43xx_phy_write(bcm, 0x0014, + bcm43xx_phy_read(bcm, 0x0014) | 0x0200); + } + bcm43xx_radio_write16(bcm, 0x007A, + bcm43xx_radio_read16(bcm, 0x007A) | 0x0070); + bcm43xx_radio_write16(bcm, 0x007A, + bcm43xx_radio_read16(bcm, 0x007A) | 0x0080); + udelay(30); + + v47F = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F); + if (v47F >= 0x20) + v47F -= 0x40; + if (v47F == 31) { + for (i = 7; i >= 4; i--) { + bcm43xx_radio_write16(bcm, 0x007B, i); + udelay(20); + v47F = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F); + if (v47F >= 0x20) + v47F -= 0x40; + if (v47F < 31 && saved == 0xFFFF) + saved = i; + } + if (saved == 0xFFFF) + saved = 4; + } else { + bcm43xx_radio_write16(bcm, 0x007A, + bcm43xx_radio_read16(bcm, 0x007A) & 0x007F); + bcm43xx_phy_write(bcm, 0x0814, + bcm43xx_phy_read(bcm, 0x0814) | 0x0001); + bcm43xx_phy_write(bcm, 0x0815, + bcm43xx_phy_read(bcm, 0x0815) & 0xFFFE); + bcm43xx_phy_write(bcm, 0x0811, + bcm43xx_phy_read(bcm, 0x0811) | 0x000C); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_phy_read(bcm, 0x0812) | 0x000C); + bcm43xx_phy_write(bcm, 0x0811, + bcm43xx_phy_read(bcm, 0x0811) | 0x0030); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_phy_read(bcm, 0x0812) | 0x0030); + bcm43xx_phy_write(bcm, 0x005A, 0x0480); + bcm43xx_phy_write(bcm, 0x0059, 0x0810); + bcm43xx_phy_write(bcm, 0x0058, 0x000D); + if (phy->rev == 0) { + bcm43xx_phy_write(bcm, 0x0003, 0x0122); + } else { + bcm43xx_phy_write(bcm, 0x000A, + bcm43xx_phy_read(bcm, 0x000A) + | 0x2000); + } + bcm43xx_phy_write(bcm, 0x0814, + bcm43xx_phy_read(bcm, 0x0814) | 0x0004); + bcm43xx_phy_write(bcm, 0x0815, + bcm43xx_phy_read(bcm, 0x0815) & 0xFFFB); + bcm43xx_phy_write(bcm, 0x0003, + (bcm43xx_phy_read(bcm, 0x0003) & 0xFF9F) + | 0x0040); + bcm43xx_radio_write16(bcm, 0x007A, + bcm43xx_radio_read16(bcm, 0x007A) | 0x000F); + bcm43xx_set_all_gains(bcm, 3, 0, 1); + bcm43xx_radio_write16(bcm, 0x0043, + (bcm43xx_radio_read16(bcm, 0x0043) + & 0x00F0) | 0x000F); + udelay(30); + v47F = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F); + if (v47F >= 0x20) + v47F -= 0x40; + if (v47F == -32) { + for (i = 0; i < 4; i++) { + bcm43xx_radio_write16(bcm, 0x007B, i); + udelay(20); + v47F = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F); + if (v47F >= 0x20) + v47F -= 0x40; + if (v47F > -31 && saved == 0xFFFF) + saved = i; + } + if (saved == 0xFFFF) + saved = 3; + } else + saved = 0; + } + bcm43xx_radio_write16(bcm, 0x007B, saved); + + if (phy->rev >= 6) { + bcm43xx_phy_write(bcm, 0x002E, backup[12]); + bcm43xx_phy_write(bcm, 0x002F, backup[13]); + bcm43xx_phy_write(bcm, 0x080F, backup[14]); + bcm43xx_phy_write(bcm, 0x0810, backup[15]); + } + bcm43xx_phy_write(bcm, 0x0814, backup[3]); + bcm43xx_phy_write(bcm, 0x0815, backup[4]); + bcm43xx_phy_write(bcm, 0x005A, backup[5]); + bcm43xx_phy_write(bcm, 0x0059, backup[6]); + bcm43xx_phy_write(bcm, 0x0058, backup[7]); + bcm43xx_phy_write(bcm, 0x000A, backup[8]); + bcm43xx_phy_write(bcm, 0x0003, backup[9]); + bcm43xx_radio_write16(bcm, 0x0043, backup[11]); + bcm43xx_radio_write16(bcm, 0x007A, backup[10]); + bcm43xx_phy_write(bcm, 0x0802, + bcm43xx_phy_read(bcm, 0x0802) | 0x1 | 0x2); + bcm43xx_phy_write(bcm, 0x0429, + bcm43xx_phy_read(bcm, 0x0429) | 0x8000); + bcm43xx_set_original_gains(bcm); + if (phy->rev >= 6) { + bcm43xx_phy_write(bcm, 0x0801, backup[16]); + bcm43xx_phy_write(bcm, 0x0060, backup[17]); + bcm43xx_phy_write(bcm, 0x0014, backup[18]); + bcm43xx_phy_write(bcm, 0x0478, backup[19]); + } + bcm43xx_phy_write(bcm, 0x0001, backup[0]); + bcm43xx_phy_write(bcm, 0x0812, backup[2]); + bcm43xx_phy_write(bcm, 0x0811, backup[1]); +} + +void bcm43xx_calc_nrssi_slope(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + u16 backup[18] = { 0 }; + u16 tmp; + s16 nrssi0, nrssi1; + + switch (phy->type) { + case BCM43xx_PHYTYPE_B: + backup[0] = bcm43xx_radio_read16(bcm, 0x007A); + backup[1] = bcm43xx_radio_read16(bcm, 0x0052); + backup[2] = bcm43xx_radio_read16(bcm, 0x0043); + backup[3] = bcm43xx_phy_read(bcm, 0x0030); + backup[4] = bcm43xx_phy_read(bcm, 0x0026); + backup[5] = bcm43xx_phy_read(bcm, 0x0015); + backup[6] = bcm43xx_phy_read(bcm, 0x002A); + backup[7] = bcm43xx_phy_read(bcm, 0x0020); + backup[8] = bcm43xx_phy_read(bcm, 0x005A); + backup[9] = bcm43xx_phy_read(bcm, 0x0059); + backup[10] = bcm43xx_phy_read(bcm, 0x0058); + backup[11] = bcm43xx_read16(bcm, 0x03E2); + backup[12] = bcm43xx_read16(bcm, 0x03E6); + backup[13] = bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT); + + tmp = bcm43xx_radio_read16(bcm, 0x007A); + tmp &= (phy->rev >= 5) ? 0x007F : 0x000F; + bcm43xx_radio_write16(bcm, 0x007A, tmp); + bcm43xx_phy_write(bcm, 0x0030, 0x00FF); + bcm43xx_write16(bcm, 0x03EC, 0x7F7F); + bcm43xx_phy_write(bcm, 0x0026, 0x0000); + bcm43xx_phy_write(bcm, 0x0015, + bcm43xx_phy_read(bcm, 0x0015) | 0x0020); + bcm43xx_phy_write(bcm, 0x002A, 0x08A3); + bcm43xx_radio_write16(bcm, 0x007A, + bcm43xx_radio_read16(bcm, 0x007A) | 0x0080); + + nrssi0 = (s16)bcm43xx_phy_read(bcm, 0x0027); + bcm43xx_radio_write16(bcm, 0x007A, + bcm43xx_radio_read16(bcm, 0x007A) & 0x007F); + if (phy->rev >= 2) { + bcm43xx_write16(bcm, 0x03E6, 0x0040); + } else if (phy->rev == 0) { + bcm43xx_write16(bcm, 0x03E6, 0x0122); + } else { + bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, + bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT) & 0x2000); + } + bcm43xx_phy_write(bcm, 0x0020, 0x3F3F); + bcm43xx_phy_write(bcm, 0x0015, 0xF330); + bcm43xx_radio_write16(bcm, 0x005A, 0x0060); + bcm43xx_radio_write16(bcm, 0x0043, + bcm43xx_radio_read16(bcm, 0x0043) & 0x00F0); + bcm43xx_phy_write(bcm, 0x005A, 0x0480); + bcm43xx_phy_write(bcm, 0x0059, 0x0810); + bcm43xx_phy_write(bcm, 0x0058, 0x000D); + udelay(20); + + nrssi1 = (s16)bcm43xx_phy_read(bcm, 0x0027); + bcm43xx_phy_write(bcm, 0x0030, backup[3]); + bcm43xx_radio_write16(bcm, 0x007A, backup[0]); + bcm43xx_write16(bcm, 0x03E2, backup[11]); + bcm43xx_phy_write(bcm, 0x0026, backup[4]); + bcm43xx_phy_write(bcm, 0x0015, backup[5]); + bcm43xx_phy_write(bcm, 0x002A, backup[6]); + bcm43xx_synth_pu_workaround(bcm, radio->channel); + if (phy->rev != 0) + bcm43xx_write16(bcm, 0x03F4, backup[13]); + + bcm43xx_phy_write(bcm, 0x0020, backup[7]); + bcm43xx_phy_write(bcm, 0x005A, backup[8]); + bcm43xx_phy_write(bcm, 0x0059, backup[9]); + bcm43xx_phy_write(bcm, 0x0058, backup[10]); + bcm43xx_radio_write16(bcm, 0x0052, backup[1]); + bcm43xx_radio_write16(bcm, 0x0043, backup[2]); + + if (nrssi0 == nrssi1) + radio->nrssislope = 0x00010000; + else + radio->nrssislope = 0x00400000 / (nrssi0 - nrssi1); + + if (nrssi0 <= -4) { + radio->nrssi[0] = nrssi0; + radio->nrssi[1] = nrssi1; + } + break; + case BCM43xx_PHYTYPE_G: + if (radio->revision >= 9) + return; + if (radio->revision == 8) + bcm43xx_calc_nrssi_offset(bcm); + + bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, + bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & 0x7FFF); + bcm43xx_phy_write(bcm, 0x0802, + bcm43xx_phy_read(bcm, 0x0802) & 0xFFFC); + backup[7] = bcm43xx_read16(bcm, 0x03E2); + bcm43xx_write16(bcm, 0x03E2, + bcm43xx_read16(bcm, 0x03E2) | 0x8000); + backup[0] = bcm43xx_radio_read16(bcm, 0x007A); + backup[1] = bcm43xx_radio_read16(bcm, 0x0052); + backup[2] = bcm43xx_radio_read16(bcm, 0x0043); + backup[3] = bcm43xx_phy_read(bcm, 0x0015); + backup[4] = bcm43xx_phy_read(bcm, 0x005A); + backup[5] = bcm43xx_phy_read(bcm, 0x0059); + backup[6] = bcm43xx_phy_read(bcm, 0x0058); + backup[8] = bcm43xx_read16(bcm, 0x03E6); + backup[9] = bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT); + if (phy->rev >= 3) { + backup[10] = bcm43xx_phy_read(bcm, 0x002E); + backup[11] = bcm43xx_phy_read(bcm, 0x002F); + backup[12] = bcm43xx_phy_read(bcm, 0x080F); + backup[13] = bcm43xx_phy_read(bcm, BCM43xx_PHY_G_LO_CONTROL); + backup[14] = bcm43xx_phy_read(bcm, 0x0801); + backup[15] = bcm43xx_phy_read(bcm, 0x0060); + backup[16] = bcm43xx_phy_read(bcm, 0x0014); + backup[17] = bcm43xx_phy_read(bcm, 0x0478); + bcm43xx_phy_write(bcm, 0x002E, 0); + bcm43xx_phy_write(bcm, BCM43xx_PHY_G_LO_CONTROL, 0); + switch (phy->rev) { + case 4: case 6: case 7: + bcm43xx_phy_write(bcm, 0x0478, + bcm43xx_phy_read(bcm, 0x0478) + | 0x0100); + bcm43xx_phy_write(bcm, 0x0801, + bcm43xx_phy_read(bcm, 0x0801) + | 0x0040); + break; + case 3: case 5: + bcm43xx_phy_write(bcm, 0x0801, + bcm43xx_phy_read(bcm, 0x0801) + & 0xFFBF); + break; + } + bcm43xx_phy_write(bcm, 0x0060, + bcm43xx_phy_read(bcm, 0x0060) + | 0x0040); + bcm43xx_phy_write(bcm, 0x0014, + bcm43xx_phy_read(bcm, 0x0014) + | 0x0200); + } + bcm43xx_radio_write16(bcm, 0x007A, + bcm43xx_radio_read16(bcm, 0x007A) | 0x0070); + bcm43xx_set_all_gains(bcm, 0, 8, 0); + bcm43xx_radio_write16(bcm, 0x007A, + bcm43xx_radio_read16(bcm, 0x007A) & 0x00F7); + if (phy->rev >= 2) { + bcm43xx_phy_write(bcm, 0x0811, + (bcm43xx_phy_read(bcm, 0x0811) & 0xFFCF) | 0x0030); + bcm43xx_phy_write(bcm, 0x0812, + (bcm43xx_phy_read(bcm, 0x0812) & 0xFFCF) | 0x0010); + } + bcm43xx_radio_write16(bcm, 0x007A, + bcm43xx_radio_read16(bcm, 0x007A) | 0x0080); + udelay(20); + + nrssi0 = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F); + if (nrssi0 >= 0x0020) + nrssi0 -= 0x0040; + + bcm43xx_radio_write16(bcm, 0x007A, + bcm43xx_radio_read16(bcm, 0x007A) & 0x007F); + if (phy->rev >= 2) { + bcm43xx_phy_write(bcm, 0x0003, + (bcm43xx_phy_read(bcm, 0x0003) + & 0xFF9F) | 0x0040); + } + + bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, + bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT) + | 0x2000); + bcm43xx_radio_write16(bcm, 0x007A, + bcm43xx_radio_read16(bcm, 0x007A) | 0x000F); + bcm43xx_phy_write(bcm, 0x0015, 0xF330); + if (phy->rev >= 2) { + bcm43xx_phy_write(bcm, 0x0812, + (bcm43xx_phy_read(bcm, 0x0812) & 0xFFCF) | 0x0020); + bcm43xx_phy_write(bcm, 0x0811, + (bcm43xx_phy_read(bcm, 0x0811) & 0xFFCF) | 0x0020); + } + + bcm43xx_set_all_gains(bcm, 3, 0, 1); + if (radio->revision == 8) { + bcm43xx_radio_write16(bcm, 0x0043, 0x001F); + } else { + tmp = bcm43xx_radio_read16(bcm, 0x0052) & 0xFF0F; + bcm43xx_radio_write16(bcm, 0x0052, tmp | 0x0060); + tmp = bcm43xx_radio_read16(bcm, 0x0043) & 0xFFF0; + bcm43xx_radio_write16(bcm, 0x0043, tmp | 0x0009); + } + bcm43xx_phy_write(bcm, 0x005A, 0x0480); + bcm43xx_phy_write(bcm, 0x0059, 0x0810); + bcm43xx_phy_write(bcm, 0x0058, 0x000D); + udelay(20); + nrssi1 = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F); + if (nrssi1 >= 0x0020) + nrssi1 -= 0x0040; + if (nrssi0 == nrssi1) + radio->nrssislope = 0x00010000; + else + radio->nrssislope = 0x00400000 / (nrssi0 - nrssi1); + if (nrssi0 >= -4) { + radio->nrssi[0] = nrssi1; + radio->nrssi[1] = nrssi0; + } + if (phy->rev >= 3) { + bcm43xx_phy_write(bcm, 0x002E, backup[10]); + bcm43xx_phy_write(bcm, 0x002F, backup[11]); + bcm43xx_phy_write(bcm, 0x080F, backup[12]); + bcm43xx_phy_write(bcm, BCM43xx_PHY_G_LO_CONTROL, backup[13]); + } + if (phy->rev >= 2) { + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_phy_read(bcm, 0x0812) & 0xFFCF); + bcm43xx_phy_write(bcm, 0x0811, + bcm43xx_phy_read(bcm, 0x0811) & 0xFFCF); + } + + bcm43xx_radio_write16(bcm, 0x007A, backup[0]); + bcm43xx_radio_write16(bcm, 0x0052, backup[1]); + bcm43xx_radio_write16(bcm, 0x0043, backup[2]); + bcm43xx_write16(bcm, 0x03E2, backup[7]); + bcm43xx_write16(bcm, 0x03E6, backup[8]); + bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, backup[9]); + bcm43xx_phy_write(bcm, 0x0015, backup[3]); + bcm43xx_phy_write(bcm, 0x005A, backup[4]); + bcm43xx_phy_write(bcm, 0x0059, backup[5]); + bcm43xx_phy_write(bcm, 0x0058, backup[6]); + bcm43xx_synth_pu_workaround(bcm, radio->channel); + bcm43xx_phy_write(bcm, 0x0802, + bcm43xx_phy_read(bcm, 0x0802) | (0x0001 | 0x0002)); + bcm43xx_set_original_gains(bcm); + bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, + bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) | 0x8000); + if (phy->rev >= 3) { + bcm43xx_phy_write(bcm, 0x0801, backup[14]); + bcm43xx_phy_write(bcm, 0x0060, backup[15]); + bcm43xx_phy_write(bcm, 0x0014, backup[16]); + bcm43xx_phy_write(bcm, 0x0478, backup[17]); + } + bcm43xx_nrssi_mem_update(bcm); + bcm43xx_calc_nrssi_threshold(bcm); + break; + default: + assert(0); + } +} + +void bcm43xx_calc_nrssi_threshold(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + s32 threshold; + s32 a, b; + s16 tmp16; + u16 tmp_u16; + + switch (phy->type) { + case BCM43xx_PHYTYPE_B: { + if (radio->version != 0x2050) + return; + if (!(bcm->sprom.boardflags & BCM43xx_BFL_RSSI)) + return; + + if (radio->revision >= 6) { + threshold = (radio->nrssi[1] - radio->nrssi[0]) * 32; + threshold += 20 * (radio->nrssi[0] + 1); + threshold /= 40; + } else + threshold = radio->nrssi[1] - 5; + + threshold = limit_value(threshold, 0, 0x3E); + bcm43xx_phy_read(bcm, 0x0020); /* dummy read */ + bcm43xx_phy_write(bcm, 0x0020, (((u16)threshold) << 8) | 0x001C); + + if (radio->revision >= 6) { + bcm43xx_phy_write(bcm, 0x0087, 0x0E0D); + bcm43xx_phy_write(bcm, 0x0086, 0x0C0B); + bcm43xx_phy_write(bcm, 0x0085, 0x0A09); + bcm43xx_phy_write(bcm, 0x0084, 0x0808); + bcm43xx_phy_write(bcm, 0x0083, 0x0808); + bcm43xx_phy_write(bcm, 0x0082, 0x0604); + bcm43xx_phy_write(bcm, 0x0081, 0x0302); + bcm43xx_phy_write(bcm, 0x0080, 0x0100); + } + break; + } + case BCM43xx_PHYTYPE_G: + if (!phy->connected || + !(bcm->sprom.boardflags & BCM43xx_BFL_RSSI)) { + tmp16 = bcm43xx_nrssi_hw_read(bcm, 0x20); + if (tmp16 >= 0x20) + tmp16 -= 0x40; + if (tmp16 < 3) { + bcm43xx_phy_write(bcm, 0x048A, + (bcm43xx_phy_read(bcm, 0x048A) + & 0xF000) | 0x09EB); + } else { + bcm43xx_phy_write(bcm, 0x048A, + (bcm43xx_phy_read(bcm, 0x048A) + & 0xF000) | 0x0AED); + } + } else { + if (radio->interfmode == BCM43xx_RADIO_INTERFMODE_NONWLAN) { + a = 0xE; + b = 0xA; + } else if (!radio->aci_wlan_automatic && radio->aci_enable) { + a = 0x13; + b = 0x12; + } else { + a = 0xE; + b = 0x11; + } + + a = a * (radio->nrssi[1] - radio->nrssi[0]); + a += (radio->nrssi[0] << 6); + if (a < 32) + a += 31; + else + a += 32; + a = a >> 6; + a = limit_value(a, -31, 31); + + b = b * (radio->nrssi[1] - radio->nrssi[0]); + b += (radio->nrssi[0] << 6); + if (b < 32) + b += 31; + else + b += 32; + b = b >> 6; + b = limit_value(b, -31, 31); + + tmp_u16 = bcm43xx_phy_read(bcm, 0x048A) & 0xF000; + tmp_u16 |= ((u32)b & 0x0000003F); + tmp_u16 |= (((u32)a & 0x0000003F) << 6); + bcm43xx_phy_write(bcm, 0x048A, tmp_u16); + } + break; + default: + assert(0); + } +} + +/* Stack implementation to save/restore values from the + * interference mitigation code. + * It is save to restore values in random order. + */ +static void _stack_save(u32 *_stackptr, size_t *stackidx, + u8 id, u16 offset, u16 value) +{ + u32 *stackptr = &(_stackptr[*stackidx]); + + assert((offset & 0xF000) == 0x0000); + assert((id & 0xF0) == 0x00); + *stackptr = offset; + *stackptr |= ((u32)id) << 12; + *stackptr |= ((u32)value) << 16; + (*stackidx)++; + assert(*stackidx < BCM43xx_INTERFSTACK_SIZE); +} + +static u16 _stack_restore(u32 *stackptr, + u8 id, u16 offset) +{ + size_t i; + + assert((offset & 0xF000) == 0x0000); + assert((id & 0xF0) == 0x00); + for (i = 0; i < BCM43xx_INTERFSTACK_SIZE; i++, stackptr++) { + if ((*stackptr & 0x00000FFF) != offset) + continue; + if (((*stackptr & 0x0000F000) >> 12) != id) + continue; + return ((*stackptr & 0xFFFF0000) >> 16); + } + assert(0); + + return 0; +} + +#define phy_stacksave(offset) \ + do { \ + _stack_save(stack, &stackidx, 0x1, (offset), \ + bcm43xx_phy_read(bcm, (offset))); \ + } while (0) +#define phy_stackrestore(offset) \ + do { \ + bcm43xx_phy_write(bcm, (offset), \ + _stack_restore(stack, 0x1, \ + (offset))); \ + } while (0) +#define radio_stacksave(offset) \ + do { \ + _stack_save(stack, &stackidx, 0x2, (offset), \ + bcm43xx_radio_read16(bcm, (offset))); \ + } while (0) +#define radio_stackrestore(offset) \ + do { \ + bcm43xx_radio_write16(bcm, (offset), \ + _stack_restore(stack, 0x2, \ + (offset))); \ + } while (0) +#define ilt_stacksave(offset) \ + do { \ + _stack_save(stack, &stackidx, 0x3, (offset), \ + bcm43xx_ilt_read(bcm, (offset))); \ + } while (0) +#define ilt_stackrestore(offset) \ + do { \ + bcm43xx_ilt_write(bcm, (offset), \ + _stack_restore(stack, 0x3, \ + (offset))); \ + } while (0) + +static void +bcm43xx_radio_interference_mitigation_enable(struct bcm43xx_private *bcm, + int mode) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + u16 tmp, flipped; + u32 tmp32; + size_t stackidx = 0; + u32 *stack = radio->interfstack; + + switch (mode) { + case BCM43xx_RADIO_INTERFMODE_NONWLAN: + if (phy->rev != 1) { + bcm43xx_phy_write(bcm, 0x042B, + bcm43xx_phy_read(bcm, 0x042B) | 0x0800); + bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, + bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & ~0x4000); + break; + } + radio_stacksave(0x0078); + tmp = (bcm43xx_radio_read16(bcm, 0x0078) & 0x001E); + flipped = flip_4bit(tmp); + if (flipped < 10 && flipped >= 8) + flipped = 7; + else if (flipped >= 10) + flipped -= 3; + flipped = flip_4bit(flipped); + flipped = (flipped << 1) | 0x0020; + bcm43xx_radio_write16(bcm, 0x0078, flipped); + + bcm43xx_calc_nrssi_threshold(bcm); + + phy_stacksave(0x0406); + bcm43xx_phy_write(bcm, 0x0406, 0x7E28); + + bcm43xx_phy_write(bcm, 0x042B, + bcm43xx_phy_read(bcm, 0x042B) | 0x0800); + bcm43xx_phy_write(bcm, BCM43xx_PHY_RADIO_BITFIELD, + bcm43xx_phy_read(bcm, BCM43xx_PHY_RADIO_BITFIELD) | 0x1000); + + phy_stacksave(0x04A0); + bcm43xx_phy_write(bcm, 0x04A0, + (bcm43xx_phy_read(bcm, 0x04A0) & 0xC0C0) | 0x0008); + phy_stacksave(0x04A1); + bcm43xx_phy_write(bcm, 0x04A1, + (bcm43xx_phy_read(bcm, 0x04A1) & 0xC0C0) | 0x0605); + phy_stacksave(0x04A2); + bcm43xx_phy_write(bcm, 0x04A2, + (bcm43xx_phy_read(bcm, 0x04A2) & 0xC0C0) | 0x0204); + phy_stacksave(0x04A8); + bcm43xx_phy_write(bcm, 0x04A8, + (bcm43xx_phy_read(bcm, 0x04A8) & 0xC0C0) | 0x0803); + phy_stacksave(0x04AB); + bcm43xx_phy_write(bcm, 0x04AB, + (bcm43xx_phy_read(bcm, 0x04AB) & 0xC0C0) | 0x0605); + + phy_stacksave(0x04A7); + bcm43xx_phy_write(bcm, 0x04A7, 0x0002); + phy_stacksave(0x04A3); + bcm43xx_phy_write(bcm, 0x04A3, 0x287A); + phy_stacksave(0x04A9); + bcm43xx_phy_write(bcm, 0x04A9, 0x2027); + phy_stacksave(0x0493); + bcm43xx_phy_write(bcm, 0x0493, 0x32F5); + phy_stacksave(0x04AA); + bcm43xx_phy_write(bcm, 0x04AA, 0x2027); + phy_stacksave(0x04AC); + bcm43xx_phy_write(bcm, 0x04AC, 0x32F5); + break; + case BCM43xx_RADIO_INTERFMODE_MANUALWLAN: + if (bcm43xx_phy_read(bcm, 0x0033) & 0x0800) + break; + + radio->aci_enable = 1; + + phy_stacksave(BCM43xx_PHY_RADIO_BITFIELD); + phy_stacksave(BCM43xx_PHY_G_CRS); + if (phy->rev < 2) { + phy_stacksave(0x0406); + } else { + phy_stacksave(0x04C0); + phy_stacksave(0x04C1); + } + phy_stacksave(0x0033); + phy_stacksave(0x04A7); + phy_stacksave(0x04A3); + phy_stacksave(0x04A9); + phy_stacksave(0x04AA); + phy_stacksave(0x04AC); + phy_stacksave(0x0493); + phy_stacksave(0x04A1); + phy_stacksave(0x04A0); + phy_stacksave(0x04A2); + phy_stacksave(0x048A); + phy_stacksave(0x04A8); + phy_stacksave(0x04AB); + if (phy->rev == 2) { + phy_stacksave(0x04AD); + phy_stacksave(0x04AE); + } else if (phy->rev >= 3) { + phy_stacksave(0x04AD); + phy_stacksave(0x0415); + phy_stacksave(0x0416); + phy_stacksave(0x0417); + ilt_stacksave(0x1A00 + 0x2); + ilt_stacksave(0x1A00 + 0x3); + } + phy_stacksave(0x042B); + phy_stacksave(0x048C); + + bcm43xx_phy_write(bcm, BCM43xx_PHY_RADIO_BITFIELD, + bcm43xx_phy_read(bcm, BCM43xx_PHY_RADIO_BITFIELD) + & ~0x1000); + bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, + (bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) + & 0xFFFC) | 0x0002); + + bcm43xx_phy_write(bcm, 0x0033, 0x0800); + bcm43xx_phy_write(bcm, 0x04A3, 0x2027); + bcm43xx_phy_write(bcm, 0x04A9, 0x1CA8); + bcm43xx_phy_write(bcm, 0x0493, 0x287A); + bcm43xx_phy_write(bcm, 0x04AA, 0x1CA8); + bcm43xx_phy_write(bcm, 0x04AC, 0x287A); + + bcm43xx_phy_write(bcm, 0x04A0, + (bcm43xx_phy_read(bcm, 0x04A0) + & 0xFFC0) | 0x001A); + bcm43xx_phy_write(bcm, 0x04A7, 0x000D); + + if (phy->rev < 2) { + bcm43xx_phy_write(bcm, 0x0406, 0xFF0D); + } else if (phy->rev == 2) { + bcm43xx_phy_write(bcm, 0x04C0, 0xFFFF); + bcm43xx_phy_write(bcm, 0x04C1, 0x00A9); + } else { + bcm43xx_phy_write(bcm, 0x04C0, 0x00C1); + bcm43xx_phy_write(bcm, 0x04C1, 0x0059); + } + + bcm43xx_phy_write(bcm, 0x04A1, + (bcm43xx_phy_read(bcm, 0x04A1) + & 0xC0FF) | 0x1800); + bcm43xx_phy_write(bcm, 0x04A1, + (bcm43xx_phy_read(bcm, 0x04A1) + & 0xFFC0) | 0x0015); + bcm43xx_phy_write(bcm, 0x04A8, + (bcm43xx_phy_read(bcm, 0x04A8) + & 0xCFFF) | 0x1000); + bcm43xx_phy_write(bcm, 0x04A8, + (bcm43xx_phy_read(bcm, 0x04A8) + & 0xF0FF) | 0x0A00); + bcm43xx_phy_write(bcm, 0x04AB, + (bcm43xx_phy_read(bcm, 0x04AB) + & 0xCFFF) | 0x1000); + bcm43xx_phy_write(bcm, 0x04AB, + (bcm43xx_phy_read(bcm, 0x04AB) + & 0xF0FF) | 0x0800); + bcm43xx_phy_write(bcm, 0x04AB, + (bcm43xx_phy_read(bcm, 0x04AB) + & 0xFFCF) | 0x0010); + bcm43xx_phy_write(bcm, 0x04AB, + (bcm43xx_phy_read(bcm, 0x04AB) + & 0xFFF0) | 0x0005); + bcm43xx_phy_write(bcm, 0x04A8, + (bcm43xx_phy_read(bcm, 0x04A8) + & 0xFFCF) | 0x0010); + bcm43xx_phy_write(bcm, 0x04A8, + (bcm43xx_phy_read(bcm, 0x04A8) + & 0xFFF0) | 0x0006); + bcm43xx_phy_write(bcm, 0x04A2, + (bcm43xx_phy_read(bcm, 0x04A2) + & 0xF0FF) | 0x0800); + bcm43xx_phy_write(bcm, 0x04A0, + (bcm43xx_phy_read(bcm, 0x04A0) + & 0xF0FF) | 0x0500); + bcm43xx_phy_write(bcm, 0x04A2, + (bcm43xx_phy_read(bcm, 0x04A2) + & 0xFFF0) | 0x000B); + + if (phy->rev >= 3) { + bcm43xx_phy_write(bcm, 0x048A, + bcm43xx_phy_read(bcm, 0x048A) + & ~0x8000); + bcm43xx_phy_write(bcm, 0x0415, + (bcm43xx_phy_read(bcm, 0x0415) + & 0x8000) | 0x36D8); + bcm43xx_phy_write(bcm, 0x0416, + (bcm43xx_phy_read(bcm, 0x0416) + & 0x8000) | 0x36D8); + bcm43xx_phy_write(bcm, 0x0417, + (bcm43xx_phy_read(bcm, 0x0417) + & 0xFE00) | 0x016D); + } else { + bcm43xx_phy_write(bcm, 0x048A, + bcm43xx_phy_read(bcm, 0x048A) + | 0x1000); + bcm43xx_phy_write(bcm, 0x048A, + (bcm43xx_phy_read(bcm, 0x048A) + & 0x9FFF) | 0x2000); + tmp32 = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, + BCM43xx_UCODEFLAGS_OFFSET); + if (!(tmp32 & 0x800)) { + tmp32 |= 0x800; + bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, + BCM43xx_UCODEFLAGS_OFFSET, + tmp32); + } + } + if (phy->rev >= 2) { + bcm43xx_phy_write(bcm, 0x042B, + bcm43xx_phy_read(bcm, 0x042B) + | 0x0800); + } + bcm43xx_phy_write(bcm, 0x048C, + (bcm43xx_phy_read(bcm, 0x048C) + & 0xF0FF) | 0x0200); + if (phy->rev == 2) { + bcm43xx_phy_write(bcm, 0x04AE, + (bcm43xx_phy_read(bcm, 0x04AE) + & 0xFF00) | 0x007F); + bcm43xx_phy_write(bcm, 0x04AD, + (bcm43xx_phy_read(bcm, 0x04AD) + & 0x00FF) | 0x1300); + } else if (phy->rev >= 6) { + bcm43xx_ilt_write(bcm, 0x1A00 + 0x3, 0x007F); + bcm43xx_ilt_write(bcm, 0x1A00 + 0x2, 0x007F); + bcm43xx_phy_write(bcm, 0x04AD, + bcm43xx_phy_read(bcm, 0x04AD) + & 0x00FF); + } + bcm43xx_calc_nrssi_slope(bcm); + break; + default: + assert(0); + } +} + +static void +bcm43xx_radio_interference_mitigation_disable(struct bcm43xx_private *bcm, + int mode) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + u32 tmp32; + u32 *stack = radio->interfstack; + + switch (mode) { + case BCM43xx_RADIO_INTERFMODE_NONWLAN: + if (phy->rev != 1) { + bcm43xx_phy_write(bcm, 0x042B, + bcm43xx_phy_read(bcm, 0x042B) & ~0x0800); + bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, + bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) | 0x4000); + break; + } + phy_stackrestore(0x0078); + bcm43xx_calc_nrssi_threshold(bcm); + phy_stackrestore(0x0406); + bcm43xx_phy_write(bcm, 0x042B, + bcm43xx_phy_read(bcm, 0x042B) & ~0x0800); + if (!bcm->bad_frames_preempt) { + bcm43xx_phy_write(bcm, BCM43xx_PHY_RADIO_BITFIELD, + bcm43xx_phy_read(bcm, BCM43xx_PHY_RADIO_BITFIELD) + & ~(1 << 11)); + } + bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, + bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) | 0x4000); + phy_stackrestore(0x04A0); + phy_stackrestore(0x04A1); + phy_stackrestore(0x04A2); + phy_stackrestore(0x04A8); + phy_stackrestore(0x04AB); + phy_stackrestore(0x04A7); + phy_stackrestore(0x04A3); + phy_stackrestore(0x04A9); + phy_stackrestore(0x0493); + phy_stackrestore(0x04AA); + phy_stackrestore(0x04AC); + break; + case BCM43xx_RADIO_INTERFMODE_MANUALWLAN: + if (!(bcm43xx_phy_read(bcm, 0x0033) & 0x0800)) + break; + + radio->aci_enable = 0; + + phy_stackrestore(BCM43xx_PHY_RADIO_BITFIELD); + phy_stackrestore(BCM43xx_PHY_G_CRS); + phy_stackrestore(0x0033); + phy_stackrestore(0x04A3); + phy_stackrestore(0x04A9); + phy_stackrestore(0x0493); + phy_stackrestore(0x04AA); + phy_stackrestore(0x04AC); + phy_stackrestore(0x04A0); + phy_stackrestore(0x04A7); + if (phy->rev >= 2) { + phy_stackrestore(0x04C0); + phy_stackrestore(0x04C1); + } else + phy_stackrestore(0x0406); + phy_stackrestore(0x04A1); + phy_stackrestore(0x04AB); + phy_stackrestore(0x04A8); + if (phy->rev == 2) { + phy_stackrestore(0x04AD); + phy_stackrestore(0x04AE); + } else if (phy->rev >= 3) { + phy_stackrestore(0x04AD); + phy_stackrestore(0x0415); + phy_stackrestore(0x0416); + phy_stackrestore(0x0417); + ilt_stackrestore(0x1A00 + 0x2); + ilt_stackrestore(0x1A00 + 0x3); + } + phy_stackrestore(0x04A2); + phy_stackrestore(0x04A8); + phy_stackrestore(0x042B); + phy_stackrestore(0x048C); + tmp32 = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, + BCM43xx_UCODEFLAGS_OFFSET); + if (tmp32 & 0x800) { + tmp32 &= ~0x800; + bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, + BCM43xx_UCODEFLAGS_OFFSET, + tmp32); + } + bcm43xx_calc_nrssi_slope(bcm); + break; + default: + assert(0); + } +} + +#undef phy_stacksave +#undef phy_stackrestore +#undef radio_stacksave +#undef radio_stackrestore +#undef ilt_stacksave +#undef ilt_stackrestore + +int bcm43xx_radio_set_interference_mitigation(struct bcm43xx_private *bcm, + int mode) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + int currentmode; + + if ((phy->type != BCM43xx_PHYTYPE_G) || + (phy->rev == 0) || + (!phy->connected)) + return -ENODEV; + + radio->aci_wlan_automatic = 0; + switch (mode) { + case BCM43xx_RADIO_INTERFMODE_AUTOWLAN: + radio->aci_wlan_automatic = 1; + if (radio->aci_enable) + mode = BCM43xx_RADIO_INTERFMODE_MANUALWLAN; + else + mode = BCM43xx_RADIO_INTERFMODE_NONE; + break; + case BCM43xx_RADIO_INTERFMODE_NONE: + case BCM43xx_RADIO_INTERFMODE_NONWLAN: + case BCM43xx_RADIO_INTERFMODE_MANUALWLAN: + break; + default: + return -EINVAL; + } + + currentmode = radio->interfmode; + if (currentmode == mode) + return 0; + if (currentmode != BCM43xx_RADIO_INTERFMODE_NONE) + bcm43xx_radio_interference_mitigation_disable(bcm, currentmode); + + if (mode == BCM43xx_RADIO_INTERFMODE_NONE) { + radio->aci_enable = 0; + radio->aci_hw_rssi = 0; + } else + bcm43xx_radio_interference_mitigation_enable(bcm, mode); + radio->interfmode = mode; + + return 0; +} + +u16 bcm43xx_radio_calibrationvalue(struct bcm43xx_private *bcm) +{ + u16 reg, index, ret; + + reg = bcm43xx_radio_read16(bcm, 0x0060); + index = (reg & 0x001E) >> 1; + ret = rcc_table[index] << 1; + ret |= (reg & 0x0001); + ret |= 0x0020; + + return ret; +} + +u16 bcm43xx_radio_init2050(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + u16 backup[19] = { 0 }; + u16 ret; + u16 i, j; + u32 tmp1 = 0, tmp2 = 0; + + backup[0] = bcm43xx_radio_read16(bcm, 0x0043); + backup[14] = bcm43xx_radio_read16(bcm, 0x0051); + backup[15] = bcm43xx_radio_read16(bcm, 0x0052); + backup[1] = bcm43xx_phy_read(bcm, 0x0015); + backup[16] = bcm43xx_phy_read(bcm, 0x005A); + backup[17] = bcm43xx_phy_read(bcm, 0x0059); + backup[18] = bcm43xx_phy_read(bcm, 0x0058); + if (phy->type == BCM43xx_PHYTYPE_B) { + backup[2] = bcm43xx_phy_read(bcm, 0x0030); + backup[3] = bcm43xx_read16(bcm, 0x03EC); + bcm43xx_phy_write(bcm, 0x0030, 0x00FF); + bcm43xx_write16(bcm, 0x03EC, 0x3F3F); + } else { + if (phy->connected) { + backup[4] = bcm43xx_phy_read(bcm, 0x0811); + backup[5] = bcm43xx_phy_read(bcm, 0x0812); + backup[6] = bcm43xx_phy_read(bcm, 0x0814); + backup[7] = bcm43xx_phy_read(bcm, 0x0815); + backup[8] = bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS); + backup[9] = bcm43xx_phy_read(bcm, 0x0802); + bcm43xx_phy_write(bcm, 0x0814, + (bcm43xx_phy_read(bcm, 0x0814) | 0x0003)); + bcm43xx_phy_write(bcm, 0x0815, + (bcm43xx_phy_read(bcm, 0x0815) & 0xFFFC)); + bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, + (bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & 0x7FFF)); + bcm43xx_phy_write(bcm, 0x0802, + (bcm43xx_phy_read(bcm, 0x0802) & 0xFFFC)); + bcm43xx_phy_write(bcm, 0x0811, 0x01B3); + bcm43xx_phy_write(bcm, 0x0812, 0x0FB2); + } + bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO, + (bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_RADIO) | 0x8000)); + } + backup[10] = bcm43xx_phy_read(bcm, 0x0035); + bcm43xx_phy_write(bcm, 0x0035, + (bcm43xx_phy_read(bcm, 0x0035) & 0xFF7F)); + backup[11] = bcm43xx_read16(bcm, 0x03E6); + backup[12] = bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT); + + // Initialization + if (phy->version == 0) { + bcm43xx_write16(bcm, 0x03E6, 0x0122); + } else { + if (phy->version >= 2) + bcm43xx_write16(bcm, 0x03E6, 0x0040); + bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, + (bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT) | 0x2000)); + } + + ret = bcm43xx_radio_calibrationvalue(bcm); + + if (phy->type == BCM43xx_PHYTYPE_B) + bcm43xx_radio_write16(bcm, 0x0078, 0x0003); + + bcm43xx_phy_write(bcm, 0x0015, 0xBFAF); + bcm43xx_phy_write(bcm, 0x002B, 0x1403); + if (phy->connected) + bcm43xx_phy_write(bcm, 0x0812, 0x00B2); + bcm43xx_phy_write(bcm, 0x0015, 0xBFA0); + bcm43xx_radio_write16(bcm, 0x0051, + (bcm43xx_radio_read16(bcm, 0x0051) | 0x0004)); + bcm43xx_radio_write16(bcm, 0x0052, 0x0000); + bcm43xx_radio_write16(bcm, 0x0043, + bcm43xx_radio_read16(bcm, 0x0043) | 0x0009); + bcm43xx_phy_write(bcm, 0x0058, 0x0000); + + for (i = 0; i < 16; i++) { + bcm43xx_phy_write(bcm, 0x005A, 0x0480); + bcm43xx_phy_write(bcm, 0x0059, 0xC810); + bcm43xx_phy_write(bcm, 0x0058, 0x000D); + if (phy->connected) + bcm43xx_phy_write(bcm, 0x0812, 0x30B2); + bcm43xx_phy_write(bcm, 0x0015, 0xAFB0); + udelay(10); + if (phy->connected) + bcm43xx_phy_write(bcm, 0x0812, 0x30B2); + bcm43xx_phy_write(bcm, 0x0015, 0xEFB0); + udelay(10); + if (phy->connected) + bcm43xx_phy_write(bcm, 0x0812, 0x30B2); + bcm43xx_phy_write(bcm, 0x0015, 0xFFF0); + udelay(10); + tmp1 += bcm43xx_phy_read(bcm, 0x002D); + bcm43xx_phy_write(bcm, 0x0058, 0x0000); + if (phy->connected) + bcm43xx_phy_write(bcm, 0x0812, 0x30B2); + bcm43xx_phy_write(bcm, 0x0015, 0xAFB0); + } + + tmp1++; + tmp1 >>= 9; + udelay(10); + bcm43xx_phy_write(bcm, 0x0058, 0x0000); + + for (i = 0; i < 16; i++) { + bcm43xx_radio_write16(bcm, 0x0078, (flip_4bit(i) << 1) | 0x0020); + backup[13] = bcm43xx_radio_read16(bcm, 0x0078); + udelay(10); + for (j = 0; j < 16; j++) { + bcm43xx_phy_write(bcm, 0x005A, 0x0D80); + bcm43xx_phy_write(bcm, 0x0059, 0xC810); + bcm43xx_phy_write(bcm, 0x0058, 0x000D); + if (phy->connected) + bcm43xx_phy_write(bcm, 0x0812, 0x30B2); + bcm43xx_phy_write(bcm, 0x0015, 0xAFB0); + udelay(10); + if (phy->connected) + bcm43xx_phy_write(bcm, 0x0812, 0x30B2); + bcm43xx_phy_write(bcm, 0x0015, 0xEFB0); + udelay(10); + if (phy->connected) + bcm43xx_phy_write(bcm, 0x0812, 0x30B3); /* 0x30B3 is not a typo */ + bcm43xx_phy_write(bcm, 0x0015, 0xFFF0); + udelay(10); + tmp2 += bcm43xx_phy_read(bcm, 0x002D); + bcm43xx_phy_write(bcm, 0x0058, 0x0000); + if (phy->connected) + bcm43xx_phy_write(bcm, 0x0812, 0x30B2); + bcm43xx_phy_write(bcm, 0x0015, 0xAFB0); + } + tmp2++; + tmp2 >>= 8; + if (tmp1 < tmp2) + break; + } + + /* Restore the registers */ + bcm43xx_phy_write(bcm, 0x0015, backup[1]); + bcm43xx_radio_write16(bcm, 0x0051, backup[14]); + bcm43xx_radio_write16(bcm, 0x0052, backup[15]); + bcm43xx_radio_write16(bcm, 0x0043, backup[0]); + bcm43xx_phy_write(bcm, 0x005A, backup[16]); + bcm43xx_phy_write(bcm, 0x0059, backup[17]); + bcm43xx_phy_write(bcm, 0x0058, backup[18]); + bcm43xx_write16(bcm, 0x03E6, backup[11]); + if (phy->version != 0) + bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, backup[12]); + bcm43xx_phy_write(bcm, 0x0035, backup[10]); + bcm43xx_radio_selectchannel(bcm, radio->channel, 1); + if (phy->type == BCM43xx_PHYTYPE_B) { + bcm43xx_phy_write(bcm, 0x0030, backup[2]); + bcm43xx_write16(bcm, 0x03EC, backup[3]); + } else { + bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO, + (bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_RADIO) & 0x7FFF)); + if (phy->connected) { + bcm43xx_phy_write(bcm, 0x0811, backup[4]); + bcm43xx_phy_write(bcm, 0x0812, backup[5]); + bcm43xx_phy_write(bcm, 0x0814, backup[6]); + bcm43xx_phy_write(bcm, 0x0815, backup[7]); + bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, backup[8]); + bcm43xx_phy_write(bcm, 0x0802, backup[9]); + } + } + if (i >= 15) + ret = backup[13]; + + return ret; +} + +void bcm43xx_radio_init2060(struct bcm43xx_private *bcm) +{ + int err; + + bcm43xx_radio_write16(bcm, 0x0004, 0x00C0); + bcm43xx_radio_write16(bcm, 0x0005, 0x0008); + bcm43xx_radio_write16(bcm, 0x0009, 0x0040); + bcm43xx_radio_write16(bcm, 0x0005, 0x00AA); + bcm43xx_radio_write16(bcm, 0x0032, 0x008F); + bcm43xx_radio_write16(bcm, 0x0006, 0x008F); + bcm43xx_radio_write16(bcm, 0x0034, 0x008F); + bcm43xx_radio_write16(bcm, 0x002C, 0x0007); + bcm43xx_radio_write16(bcm, 0x0082, 0x0080); + bcm43xx_radio_write16(bcm, 0x0080, 0x0000); + bcm43xx_radio_write16(bcm, 0x003F, 0x00DA); + bcm43xx_radio_write16(bcm, 0x0005, bcm43xx_radio_read16(bcm, 0x0005) & ~0x0008); + bcm43xx_radio_write16(bcm, 0x0081, bcm43xx_radio_read16(bcm, 0x0081) & ~0x0010); + bcm43xx_radio_write16(bcm, 0x0081, bcm43xx_radio_read16(bcm, 0x0081) & ~0x0020); + bcm43xx_radio_write16(bcm, 0x0081, bcm43xx_radio_read16(bcm, 0x0081) & ~0x0020); + udelay(400); + + bcm43xx_radio_write16(bcm, 0x0081, (bcm43xx_radio_read16(bcm, 0x0081) & ~0x0020) | 0x0010); + udelay(400); + + bcm43xx_radio_write16(bcm, 0x0005, (bcm43xx_radio_read16(bcm, 0x0005) & ~0x0008) | 0x0008); + bcm43xx_radio_write16(bcm, 0x0085, bcm43xx_radio_read16(bcm, 0x0085) & ~0x0010); + bcm43xx_radio_write16(bcm, 0x0005, bcm43xx_radio_read16(bcm, 0x0005) & ~0x0008); + bcm43xx_radio_write16(bcm, 0x0081, bcm43xx_radio_read16(bcm, 0x0081) & ~0x0040); + bcm43xx_radio_write16(bcm, 0x0081, (bcm43xx_radio_read16(bcm, 0x0081) & ~0x0040) | 0x0040); + bcm43xx_radio_write16(bcm, 0x0005, (bcm43xx_radio_read16(bcm, 0x0081) & ~0x0008) | 0x0008); + bcm43xx_phy_write(bcm, 0x0063, 0xDDC6); + bcm43xx_phy_write(bcm, 0x0069, 0x07BE); + bcm43xx_phy_write(bcm, 0x006A, 0x0000); + + err = bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_A, 0); + assert(err == 0); + udelay(1000); +} + +static inline +u16 freq_r3A_value(u16 frequency) +{ + u16 value; + + if (frequency < 5091) + value = 0x0040; + else if (frequency < 5321) + value = 0x0000; + else if (frequency < 5806) + value = 0x0080; + else + value = 0x0040; + + return value; +} + +void bcm43xx_radio_set_tx_iq(struct bcm43xx_private *bcm) +{ + static const u8 data_high[5] = { 0x00, 0x40, 0x80, 0x90, 0xD0 }; + static const u8 data_low[5] = { 0x00, 0x01, 0x05, 0x06, 0x0A }; + u16 tmp = bcm43xx_radio_read16(bcm, 0x001E); + int i, j; + + for (i = 0; i < 5; i++) { + for (j = 0; j < 5; j++) { + if (tmp == (data_high[i] << 4 | data_low[j])) { + bcm43xx_phy_write(bcm, 0x0069, (i - j) << 8 | 0x00C0); + return; + } + } + } +} + +int bcm43xx_radio_selectchannel(struct bcm43xx_private *bcm, + u8 channel, + int synthetic_pu_workaround) +{ + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + u16 r8, tmp; + u16 freq; + + if ((radio->manufact == 0x17F) && + (radio->version == 0x2060) && + (radio->revision == 1)) { + if (channel > 200) + return -EINVAL; + freq = channel2freq_a(channel); + + r8 = bcm43xx_radio_read16(bcm, 0x0008); + bcm43xx_write16(bcm, 0x03F0, freq); + bcm43xx_radio_write16(bcm, 0x0008, r8); + + TODO();//TODO: write max channel TX power? to Radio 0x2D + tmp = bcm43xx_radio_read16(bcm, 0x002E); + tmp &= 0x0080; + TODO();//TODO: OR tmp with the Power out estimation for this channel? + bcm43xx_radio_write16(bcm, 0x002E, tmp); + + if (freq >= 4920 && freq <= 5500) { + /* + * r8 = (((freq * 15 * 0xE1FC780F) >> 32) / 29) & 0x0F; + * = (freq * 0.025862069 + */ + r8 = 3 * freq / 116; /* is equal to r8 = freq * 0.025862 */ + } + bcm43xx_radio_write16(bcm, 0x0007, (r8 << 4) | r8); + bcm43xx_radio_write16(bcm, 0x0020, (r8 << 4) | r8); + bcm43xx_radio_write16(bcm, 0x0021, (r8 << 4) | r8); + bcm43xx_radio_write16(bcm, 0x0022, + (bcm43xx_radio_read16(bcm, 0x0022) + & 0x000F) | (r8 << 4)); + bcm43xx_radio_write16(bcm, 0x002A, (r8 << 4)); + bcm43xx_radio_write16(bcm, 0x002B, (r8 << 4)); + bcm43xx_radio_write16(bcm, 0x0008, + (bcm43xx_radio_read16(bcm, 0x0008) + & 0x00F0) | (r8 << 4)); + bcm43xx_radio_write16(bcm, 0x0029, + (bcm43xx_radio_read16(bcm, 0x0029) + & 0xFF0F) | 0x00B0); + bcm43xx_radio_write16(bcm, 0x0035, 0x00AA); + bcm43xx_radio_write16(bcm, 0x0036, 0x0085); + bcm43xx_radio_write16(bcm, 0x003A, + (bcm43xx_radio_read16(bcm, 0x003A) + & 0xFF20) | freq_r3A_value(freq)); + bcm43xx_radio_write16(bcm, 0x003D, + bcm43xx_radio_read16(bcm, 0x003D) & 0x00FF); + bcm43xx_radio_write16(bcm, 0x0081, + (bcm43xx_radio_read16(bcm, 0x0081) + & 0xFF7F) | 0x0080); + bcm43xx_radio_write16(bcm, 0x0035, + bcm43xx_radio_read16(bcm, 0x0035) & 0xFFEF); + bcm43xx_radio_write16(bcm, 0x0035, + (bcm43xx_radio_read16(bcm, 0x0035) + & 0xFFEF) | 0x0010); + bcm43xx_radio_set_tx_iq(bcm); + TODO(); //TODO: TSSI2dbm workaround + bcm43xx_phy_xmitpower(bcm);//FIXME correct? + } else { + if ((channel < 1) || (channel > 14)) + return -EINVAL; + + if (synthetic_pu_workaround) + bcm43xx_synth_pu_workaround(bcm, channel); + + bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL, + channel2freq_bg(channel)); + + if (channel == 14) { + if (bcm->sprom.locale == BCM43xx_LOCALE_JAPAN) { + bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, + BCM43xx_UCODEFLAGS_OFFSET, + bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, + BCM43xx_UCODEFLAGS_OFFSET) + & ~(1 << 7)); + } else { + bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, + BCM43xx_UCODEFLAGS_OFFSET, + bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, + BCM43xx_UCODEFLAGS_OFFSET) + | (1 << 7)); + } + bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, + bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT) + | (1 << 11)); + } else { + bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, + bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT) + & 0xF7BF); + } + } + + radio->channel = channel; + //XXX: Using the longer of 2 timeouts (8000 vs 2000 usecs). Specs states + // that 2000 usecs might suffice. + udelay(8000); + + return 0; +} + +void bcm43xx_radio_set_txantenna(struct bcm43xx_private *bcm, u32 val) +{ + u16 tmp; + + val <<= 8; + tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0022) & 0xFCFF; + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0022, tmp | val); + tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x03A8) & 0xFCFF; + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x03A8, tmp | val); + tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0054) & 0xFCFF; + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0054, tmp | val); +} + +/* http://bcm-specs.sipsolutions.net/TX_Gain_Base_Band */ +static u16 bcm43xx_get_txgain_base_band(u16 txpower) +{ + u16 ret; + + assert(txpower <= 63); + + if (txpower >= 54) + ret = 2; + else if (txpower >= 49) + ret = 4; + else if (txpower >= 44) + ret = 5; + else + ret = 6; + + return ret; +} + +/* http://bcm-specs.sipsolutions.net/TX_Gain_Radio_Frequency_Power_Amplifier */ +static u16 bcm43xx_get_txgain_freq_power_amp(u16 txpower) +{ + u16 ret; + + assert(txpower <= 63); + + if (txpower >= 32) + ret = 0; + else if (txpower >= 25) + ret = 1; + else if (txpower >= 20) + ret = 2; + else if (txpower >= 12) + ret = 3; + else + ret = 4; + + return ret; +} + +/* http://bcm-specs.sipsolutions.net/TX_Gain_Digital_Analog_Converter */ +static u16 bcm43xx_get_txgain_dac(u16 txpower) +{ + u16 ret; + + assert(txpower <= 63); + + if (txpower >= 54) + ret = txpower - 53; + else if (txpower >= 49) + ret = txpower - 42; + else if (txpower >= 44) + ret = txpower - 37; + else if (txpower >= 32) + ret = txpower - 32; + else if (txpower >= 25) + ret = txpower - 20; + else if (txpower >= 20) + ret = txpower - 13; + else if (txpower >= 12) + ret = txpower - 8; + else + ret = txpower; + + return ret; +} + +void bcm43xx_radio_set_txpower_a(struct bcm43xx_private *bcm, u16 txpower) +{ + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + u16 pamp, base, dac, ilt; + + txpower = limit_value(txpower, 0, 63); + + pamp = bcm43xx_get_txgain_freq_power_amp(txpower); + pamp <<= 5; + pamp &= 0x00E0; + bcm43xx_phy_write(bcm, 0x0019, pamp); + + base = bcm43xx_get_txgain_base_band(txpower); + base &= 0x000F; + bcm43xx_phy_write(bcm, 0x0017, base | 0x0020); + + ilt = bcm43xx_ilt_read(bcm, 0x3001); + ilt &= 0x0007; + + dac = bcm43xx_get_txgain_dac(txpower); + dac <<= 3; + dac |= ilt; + + bcm43xx_ilt_write(bcm, 0x3001, dac); + + radio->txpwr_offset = txpower; + + TODO(); + //TODO: FuncPlaceholder (Adjust BB loft cancel) +} + +void bcm43xx_radio_set_txpower_bg(struct bcm43xx_private *bcm, + u16 baseband_attenuation, u16 radio_attenuation, + u16 txpower) +{ + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + + if (baseband_attenuation == 0xFFFF) + baseband_attenuation = radio->baseband_atten; + if (radio_attenuation == 0xFFFF) + radio_attenuation = radio->radio_atten; + if (txpower == 0xFFFF) + txpower = radio->txctl1; + radio->baseband_atten = baseband_attenuation; + radio->radio_atten = radio_attenuation; + radio->txctl1 = txpower; + + assert(/*baseband_attenuation >= 0 &&*/ baseband_attenuation <= 11); + if (radio->revision < 6) + assert(/*radio_attenuation >= 0 &&*/ radio_attenuation <= 9); + else + assert(/* radio_attenuation >= 0 &&*/ radio_attenuation <= 31); + assert(/*txpower >= 0 &&*/ txpower <= 7); + + bcm43xx_phy_set_baseband_attenuation(bcm, baseband_attenuation); + bcm43xx_radio_write16(bcm, 0x0043, radio_attenuation); + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0064, radio_attenuation); + if (radio->version == 0x2050) { + bcm43xx_radio_write16(bcm, 0x0052, + (bcm43xx_radio_read16(bcm, 0x0052) & ~0x0070) + | ((txpower << 4) & 0x0070)); + } + //FIXME: The spec is very weird and unclear here. + if (phy->type == BCM43xx_PHYTYPE_G) + bcm43xx_phy_lo_adjust(bcm, 0); +} + +u16 bcm43xx_default_baseband_attenuation(struct bcm43xx_private *bcm) +{ + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + + if (radio->version == 0x2050 && radio->revision < 6) + return 0; + return 2; +} + +u16 bcm43xx_default_radio_attenuation(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + u16 att = 0xFFFF; + + if (phy->type == BCM43xx_PHYTYPE_A) + return 0x60; + + switch (radio->version) { + case 0x2053: + switch (radio->revision) { + case 1: + att = 6; + break; + } + break; + case 0x2050: + switch (radio->revision) { + case 0: + att = 5; + break; + case 1: + if (phy->type == BCM43xx_PHYTYPE_G) { + if (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM && + bcm->board_type == 0x421 && + bcm->board_revision >= 30) + att = 3; + else if (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM && + bcm->board_type == 0x416) + att = 3; + else + att = 1; + } else { + if (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM && + bcm->board_type == 0x421 && + bcm->board_revision >= 30) + att = 7; + else + att = 6; + } + break; + case 2: + if (phy->type == BCM43xx_PHYTYPE_G) { + if (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM && + bcm->board_type == 0x421 && + bcm->board_revision >= 30) + att = 3; + else if (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM && + bcm->board_type == 0x416) + att = 5; + else if (bcm->chip_id == 0x4320) + att = 4; + else + att = 3; + } else + att = 6; + break; + case 3: + att = 5; + break; + case 4: + case 5: + att = 1; + break; + case 6: + case 7: + att = 5; + break; + case 8: + att = 0x1A; + break; + case 9: + default: + att = 5; + } + } + if (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM && + bcm->board_type == 0x421) { + if (bcm->board_revision < 0x43) + att = 2; + else if (bcm->board_revision < 0x51) + att = 3; + } + if (att == 0xFFFF) + att = 5; + + return att; +} + +u16 bcm43xx_default_txctl1(struct bcm43xx_private *bcm) +{ + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + + if (radio->version != 0x2050) + return 0; + if (radio->revision == 1) + return 3; + if (radio->revision < 6) + return 2; + if (radio->revision == 8) + return 1; + return 0; +} + +void bcm43xx_radio_turn_on(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + int err; + + if (radio->enabled) + return; + + switch (phy->type) { + case BCM43xx_PHYTYPE_A: + bcm43xx_radio_write16(bcm, 0x0004, 0x00C0); + bcm43xx_radio_write16(bcm, 0x0005, 0x0008); + bcm43xx_phy_write(bcm, 0x0010, bcm43xx_phy_read(bcm, 0x0010) & 0xFFF7); + bcm43xx_phy_write(bcm, 0x0011, bcm43xx_phy_read(bcm, 0x0011) & 0xFFF7); + bcm43xx_radio_init2060(bcm); + break; + case BCM43xx_PHYTYPE_B: + case BCM43xx_PHYTYPE_G: + bcm43xx_phy_write(bcm, 0x0015, 0x8000); + bcm43xx_phy_write(bcm, 0x0015, 0xCC00); + bcm43xx_phy_write(bcm, 0x0015, (phy->connected ? 0x00C0 : 0x0000)); + err = bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_BG, 1); + assert(err == 0); + break; + default: + assert(0); + } + radio->enabled = 1; + dprintk(KERN_INFO PFX "Radio turned on\n"); +} + +void bcm43xx_radio_turn_off(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + + if (phy->type == BCM43xx_PHYTYPE_A) { + bcm43xx_radio_write16(bcm, 0x0004, 0x00FF); + bcm43xx_radio_write16(bcm, 0x0005, 0x00FB); + bcm43xx_phy_write(bcm, 0x0010, bcm43xx_phy_read(bcm, 0x0010) | 0x0008); + bcm43xx_phy_write(bcm, 0x0011, bcm43xx_phy_read(bcm, 0x0011) | 0x0008); + } + if (phy->type == BCM43xx_PHYTYPE_G && bcm->current_core->rev >= 5) { + bcm43xx_phy_write(bcm, 0x0811, bcm43xx_phy_read(bcm, 0x0811) | 0x008C); + bcm43xx_phy_write(bcm, 0x0812, bcm43xx_phy_read(bcm, 0x0812) & 0xFF73); + } else + bcm43xx_phy_write(bcm, 0x0015, 0xAA00); + radio->enabled = 0; + dprintk(KERN_INFO PFX "Radio turned off\n"); +} + +void bcm43xx_radio_clear_tssi(struct bcm43xx_private *bcm) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + + switch (phy->type) { + case BCM43xx_PHYTYPE_A: + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0068, 0x7F7F); + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x006a, 0x7F7F); + break; + case BCM43xx_PHYTYPE_B: + case BCM43xx_PHYTYPE_G: + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0058, 0x7F7F); + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x005a, 0x7F7F); + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0070, 0x7F7F); + bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0072, 0x7F7F); + break; + } +} diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_radio.h b/drivers/net/wireless/bcm43xx/bcm43xx_radio.h new file mode 100644 index 000000000000..9ed18039fa3e --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_radio.h @@ -0,0 +1,99 @@ +/* + + Broadcom BCM43xx wireless driver + + Copyright (c) 2005 Martin Langer , + Stefano Brivio + Michael Buesch + Danny van Dyk + Andreas Jaggi + + Some parts of the code in this file are derived from the ipw2200 + driver Copyright(c) 2003 - 2004 Intel Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#ifndef BCM43xx_RADIO_H_ +#define BCM43xx_RADIO_H_ + +#include "bcm43xx.h" + + +#define BCM43xx_RADIO_DEFAULT_CHANNEL_A 36 +#define BCM43xx_RADIO_DEFAULT_CHANNEL_BG 6 + +/* Force antenna 0. */ +#define BCM43xx_RADIO_TXANTENNA_0 0 +/* Force antenna 1. */ +#define BCM43xx_RADIO_TXANTENNA_1 1 +/* Use the RX antenna, that was selected for the most recently + * received good PLCP header. + */ +#define BCM43xx_RADIO_TXANTENNA_LASTPLCP 3 +#define BCM43xx_RADIO_TXANTENNA_DEFAULT BCM43xx_RADIO_TXANTENNA_LASTPLCP + +#define BCM43xx_RADIO_INTERFMODE_NONE 0 +#define BCM43xx_RADIO_INTERFMODE_NONWLAN 1 +#define BCM43xx_RADIO_INTERFMODE_MANUALWLAN 2 +#define BCM43xx_RADIO_INTERFMODE_AUTOWLAN 3 + + +void bcm43xx_radio_lock(struct bcm43xx_private *bcm); +void bcm43xx_radio_unlock(struct bcm43xx_private *bcm); + +u16 bcm43xx_radio_read16(struct bcm43xx_private *bcm, u16 offset); +void bcm43xx_radio_write16(struct bcm43xx_private *bcm, u16 offset, u16 val); + +u16 bcm43xx_radio_init2050(struct bcm43xx_private *bcm); +void bcm43xx_radio_init2060(struct bcm43xx_private *bcm); + +void bcm43xx_radio_turn_on(struct bcm43xx_private *bcm); +void bcm43xx_radio_turn_off(struct bcm43xx_private *bcm); + +int bcm43xx_radio_selectchannel(struct bcm43xx_private *bcm, u8 channel, + int synthetic_pu_workaround); + +void bcm43xx_radio_set_txpower_a(struct bcm43xx_private *bcm, u16 txpower); +void bcm43xx_radio_set_txpower_bg(struct bcm43xx_private *bcm, + u16 baseband_attenuation, u16 attenuation, + u16 txpower); + +u16 bcm43xx_default_baseband_attenuation(struct bcm43xx_private *bcm); +u16 bcm43xx_default_radio_attenuation(struct bcm43xx_private *bcm); +u16 bcm43xx_default_txctl1(struct bcm43xx_private *bcm); + +void bcm43xx_radio_set_txantenna(struct bcm43xx_private *bcm, u32 val); + +void bcm43xx_radio_clear_tssi(struct bcm43xx_private *bcm); + +u8 bcm43xx_radio_aci_detect(struct bcm43xx_private *bcm, u8 channel); +u8 bcm43xx_radio_aci_scan(struct bcm43xx_private *bcm); + +int bcm43xx_radio_set_interference_mitigation(struct bcm43xx_private *bcm, int mode); + +void bcm43xx_calc_nrssi_slope(struct bcm43xx_private *bcm); +void bcm43xx_calc_nrssi_threshold(struct bcm43xx_private *bcm); +s16 bcm43xx_nrssi_hw_read(struct bcm43xx_private *bcm, u16 offset); +void bcm43xx_nrssi_hw_write(struct bcm43xx_private *bcm, u16 offset, s16 val); +void bcm43xx_nrssi_hw_update(struct bcm43xx_private *bcm, u16 val); +void bcm43xx_nrssi_mem_update(struct bcm43xx_private *bcm); + +void bcm43xx_radio_set_tx_iq(struct bcm43xx_private *bcm); +u16 bcm43xx_radio_calibrationvalue(struct bcm43xx_private *bcm); + +#endif /* BCM43xx_RADIO_H_ */ diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c b/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c new file mode 100644 index 000000000000..c44d890b949b --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c @@ -0,0 +1,322 @@ +/* + + Broadcom BCM43xx wireless driver + + SYSFS support routines + + Copyright (c) 2006 Michael Buesch + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "bcm43xx_sysfs.h" +#include "bcm43xx.h" +#include "bcm43xx_main.h" +#include "bcm43xx_radio.h" + +#include + + +#define GENERIC_FILESIZE 64 + + +static int get_integer(const char *buf, size_t count) +{ + char tmp[10 + 1] = { 0 }; + int ret = -EINVAL; + + if (count == 0) + goto out; + count = min(count, (size_t)10); + memcpy(tmp, buf, count); + ret = simple_strtol(tmp, NULL, 10); +out: + return ret; +} + +static int get_boolean(const char *buf, size_t count) +{ + if (count != 0) { + if (buf[0] == '1') + return 1; + if (buf[0] == '0') + return 0; + if (count >= 4 && memcmp(buf, "true", 4) == 0) + return 1; + if (count >= 5 && memcmp(buf, "false", 5) == 0) + return 0; + if (count >= 3 && memcmp(buf, "yes", 3) == 0) + return 1; + if (count >= 2 && memcmp(buf, "no", 2) == 0) + return 0; + if (count >= 2 && memcmp(buf, "on", 2) == 0) + return 1; + if (count >= 3 && memcmp(buf, "off", 3) == 0) + return 0; + } + return -EINVAL; +} + +static ssize_t bcm43xx_attr_sprom_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_sprom); + u16 *sprom; + unsigned long flags; + int i, err; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + assert(BCM43xx_SPROM_SIZE * sizeof(u16) <= PAGE_SIZE); + sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom), + GFP_KERNEL); + if (!sprom) + return -ENOMEM; + bcm43xx_lock_mmio(bcm, flags); + assert(bcm->initialized); + err = bcm43xx_sprom_read(bcm, sprom); + if (!err) { + for (i = 0; i < BCM43xx_SPROM_SIZE; i++) { + buf[i * 2] = sprom[i] & 0x00FF; + buf[i * 2 + 1] = (sprom[i] & 0xFF00) >> 8; + } + } + bcm43xx_unlock_mmio(bcm, flags); + kfree(sprom); + + return err ? err : BCM43xx_SPROM_SIZE * sizeof(u16); +} + +static ssize_t bcm43xx_attr_sprom_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_sprom); + u16 *sprom; + unsigned long flags; + int i, err; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (count != BCM43xx_SPROM_SIZE * sizeof(u16)) + return -EINVAL; + sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom), + GFP_KERNEL); + if (!sprom) + return -ENOMEM; + for (i = 0; i < BCM43xx_SPROM_SIZE; i++) { + sprom[i] = buf[i * 2] & 0xFF; + sprom[i] |= ((u16)(buf[i * 2 + 1] & 0xFF)) << 8; + } + bcm43xx_lock_mmio(bcm, flags); + assert(bcm->initialized); + err = bcm43xx_sprom_write(bcm, sprom); + bcm43xx_unlock_mmio(bcm, flags); + kfree(sprom); + + return err ? err : count; + +} + +static ssize_t bcm43xx_attr_interfmode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_interfmode); + unsigned long flags; + int err; + ssize_t count = 0; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + bcm43xx_lock(bcm, flags); + assert(bcm->initialized); + + switch (bcm43xx_current_radio(bcm)->interfmode) { + case BCM43xx_RADIO_INTERFMODE_NONE: + count = snprintf(buf, PAGE_SIZE, "0 (No Interference Mitigation)\n"); + break; + case BCM43xx_RADIO_INTERFMODE_NONWLAN: + count = snprintf(buf, PAGE_SIZE, "1 (Non-WLAN Interference Mitigation)\n"); + break; + case BCM43xx_RADIO_INTERFMODE_MANUALWLAN: + count = snprintf(buf, PAGE_SIZE, "2 (WLAN Interference Mitigation)\n"); + break; + default: + assert(0); + } + err = 0; + + bcm43xx_unlock(bcm, flags); + + return err ? err : count; + +} + +static ssize_t bcm43xx_attr_interfmode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_interfmode); + unsigned long flags; + int err; + int mode; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + mode = get_integer(buf, count); + switch (mode) { + case 0: + mode = BCM43xx_RADIO_INTERFMODE_NONE; + break; + case 1: + mode = BCM43xx_RADIO_INTERFMODE_NONWLAN; + break; + case 2: + mode = BCM43xx_RADIO_INTERFMODE_MANUALWLAN; + break; + case 3: + mode = BCM43xx_RADIO_INTERFMODE_AUTOWLAN; + break; + default: + return -EINVAL; + } + + bcm43xx_lock_mmio(bcm, flags); + assert(bcm->initialized); + + err = bcm43xx_radio_set_interference_mitigation(bcm, mode); + if (err) { + printk(KERN_ERR PFX "Interference Mitigation not " + "supported by device\n"); + } + + bcm43xx_unlock_mmio(bcm, flags); + + return err ? err : count; +} + +static ssize_t bcm43xx_attr_preamble_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_preamble); + unsigned long flags; + int err; + ssize_t count; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + bcm43xx_lock(bcm, flags); + assert(bcm->initialized); + + if (bcm->short_preamble) + count = snprintf(buf, PAGE_SIZE, "1 (Short Preamble enabled)\n"); + else + count = snprintf(buf, PAGE_SIZE, "0 (Short Preamble disabled)\n"); + + err = 0; + bcm43xx_unlock(bcm, flags); + + return err ? err : count; +} + +static ssize_t bcm43xx_attr_preamble_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_preamble); + unsigned long flags; + int err; + int value; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + value = get_boolean(buf, count); + if (value < 0) + return value; + bcm43xx_lock(bcm, flags); + assert(bcm->initialized); + + bcm->short_preamble = !!value; + + err = 0; + bcm43xx_unlock(bcm, flags); + + return err ? err : count; +} + +int bcm43xx_sysfs_register(struct bcm43xx_private *bcm) +{ + struct device *dev = &bcm->pci_dev->dev; + struct bcm43xx_sysfs *sysfs = &bcm->sysfs; + int err; + + assert(bcm->initialized); + + sysfs->attr_sprom.attr.name = "sprom"; + sysfs->attr_sprom.attr.owner = THIS_MODULE; + sysfs->attr_sprom.attr.mode = 0600; + sysfs->attr_sprom.show = bcm43xx_attr_sprom_show; + sysfs->attr_sprom.store = bcm43xx_attr_sprom_store; + err = device_create_file(dev, &sysfs->attr_sprom); + if (err) + goto out; + + sysfs->attr_interfmode.attr.name = "interference"; + sysfs->attr_interfmode.attr.owner = THIS_MODULE; + sysfs->attr_interfmode.attr.mode = 0600; + sysfs->attr_interfmode.show = bcm43xx_attr_interfmode_show; + sysfs->attr_interfmode.store = bcm43xx_attr_interfmode_store; + err = device_create_file(dev, &sysfs->attr_interfmode); + if (err) + goto err_remove_sprom; + + sysfs->attr_preamble.attr.name = "shortpreamble"; + sysfs->attr_preamble.attr.owner = THIS_MODULE; + sysfs->attr_preamble.attr.mode = 0600; + sysfs->attr_preamble.show = bcm43xx_attr_preamble_show; + sysfs->attr_preamble.store = bcm43xx_attr_preamble_store; + err = device_create_file(dev, &sysfs->attr_preamble); + if (err) + goto err_remove_interfmode; + +out: + return err; +err_remove_interfmode: + device_remove_file(dev, &sysfs->attr_interfmode); +err_remove_sprom: + device_remove_file(dev, &sysfs->attr_sprom); + goto out; +} + +void bcm43xx_sysfs_unregister(struct bcm43xx_private *bcm) +{ + struct device *dev = &bcm->pci_dev->dev; + struct bcm43xx_sysfs *sysfs = &bcm->sysfs; + + device_remove_file(dev, &sysfs->attr_preamble); + device_remove_file(dev, &sysfs->attr_interfmode); + device_remove_file(dev, &sysfs->attr_sprom); +} diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.h b/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.h new file mode 100644 index 000000000000..57f14514e3e0 --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.h @@ -0,0 +1,25 @@ +#ifndef BCM43xx_SYSFS_H_ +#define BCM43xx_SYSFS_H_ + +#include + + +struct bcm43xx_sysfs { + struct device_attribute attr_sprom; + struct device_attribute attr_interfmode; + struct device_attribute attr_preamble; +}; + +#define devattr_to_bcm(attr, attr_name) ({ \ + struct bcm43xx_sysfs *__s; struct bcm43xx_private *__p; \ + __s = container_of((attr), struct bcm43xx_sysfs, attr_name); \ + __p = container_of(__s, struct bcm43xx_private, sysfs); \ + __p; \ + }) + +struct bcm43xx_private; + +int bcm43xx_sysfs_register(struct bcm43xx_private *bcm); +void bcm43xx_sysfs_unregister(struct bcm43xx_private *bcm); + +#endif /* BCM43xx_SYSFS_H_ */ diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_wx.c b/drivers/net/wireless/bcm43xx/bcm43xx_wx.c new file mode 100644 index 000000000000..3daee828ef4b --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_wx.c @@ -0,0 +1,1002 @@ +/* + + Broadcom BCM43xx wireless driver + + Copyright (c) 2005 Martin Langer , + Stefano Brivio + Michael Buesch + Danny van Dyk + Andreas Jaggi + + Some parts of the code in this file are derived from the ipw2200 + driver Copyright(c) 2003 - 2004 Intel Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include +#include +#include +#include +#include +#include /* for capable() */ +#include + +#include "bcm43xx.h" +#include "bcm43xx_wx.h" +#include "bcm43xx_main.h" +#include "bcm43xx_radio.h" +#include "bcm43xx_phy.h" + + +/* The WIRELESS_EXT version, which is implemented by this driver. */ +#define BCM43xx_WX_VERSION 18 + +#define MAX_WX_STRING 80 + + +static int bcm43xx_wx_get_name(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + unsigned long flags; + int i; + struct bcm43xx_phyinfo *phy; + char suffix[7] = { 0 }; + int have_a = 0, have_b = 0, have_g = 0; + + bcm43xx_lock(bcm, flags); + for (i = 0; i < bcm->nr_80211_available; i++) { + phy = &(bcm->core_80211_ext[i].phy); + switch (phy->type) { + case BCM43xx_PHYTYPE_A: + have_a = 1; + break; + case BCM43xx_PHYTYPE_G: + have_g = 1; + case BCM43xx_PHYTYPE_B: + have_b = 1; + break; + default: + assert(0); + } + } + bcm43xx_unlock(bcm, flags); + + i = 0; + if (have_a) { + suffix[i++] = 'a'; + suffix[i++] = '/'; + } + if (have_b) { + suffix[i++] = 'b'; + suffix[i++] = '/'; + } + if (have_g) { + suffix[i++] = 'g'; + suffix[i++] = '/'; + } + if (i != 0) + suffix[i - 1] = '\0'; + + snprintf(data->name, IFNAMSIZ, "IEEE 802.11%s", suffix); + + return 0; +} + +static int bcm43xx_wx_set_channelfreq(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + unsigned long flags; + u8 channel; + int freq; + int err = -EINVAL; + + bcm43xx_lock_mmio(bcm, flags); + if ((data->freq.m >= 0) && (data->freq.m <= 1000)) { + channel = data->freq.m; + freq = bcm43xx_channel_to_freq(bcm, channel); + } else { + channel = bcm43xx_freq_to_channel(bcm, data->freq.m); + freq = data->freq.m; + } + if (!bcm43xx_is_valid_channel(bcm, channel)) + goto out_unlock; + if (bcm->initialized) { + //ieee80211softmac_disassoc(softmac, $REASON); + bcm43xx_mac_suspend(bcm); + err = bcm43xx_radio_selectchannel(bcm, channel, 0); + bcm43xx_mac_enable(bcm); + } else { + bcm43xx_current_radio(bcm)->initial_channel = channel; + err = 0; + } +out_unlock: + bcm43xx_unlock_mmio(bcm, flags); + + return err; +} + +static int bcm43xx_wx_get_channelfreq(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + struct bcm43xx_radioinfo *radio; + unsigned long flags; + int err = -ENODEV; + u16 channel; + + bcm43xx_lock(bcm, flags); + radio = bcm43xx_current_radio(bcm); + channel = radio->channel; + if (channel == 0xFF) { + assert(!bcm->initialized); + channel = radio->initial_channel; + if (channel == 0xFF) + goto out_unlock; + } + assert(channel > 0 && channel <= 1000); + data->freq.e = 1; + data->freq.m = bcm43xx_channel_to_freq(bcm, channel) * 100000; + data->freq.flags = 1; + + err = 0; +out_unlock: + bcm43xx_unlock(bcm, flags); + + return err; +} + +static int bcm43xx_wx_set_mode(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + unsigned long flags; + int mode; + + mode = data->mode; + if (mode == IW_MODE_AUTO) + mode = BCM43xx_INITIAL_IWMODE; + + bcm43xx_lock_mmio(bcm, flags); + if (bcm->ieee->iw_mode != mode) + bcm43xx_set_iwmode(bcm, mode); + bcm43xx_unlock_mmio(bcm, flags); + + return 0; +} + +static int bcm43xx_wx_get_mode(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + unsigned long flags; + + bcm43xx_lock(bcm, flags); + data->mode = bcm->ieee->iw_mode; + bcm43xx_unlock(bcm, flags); + + return 0; +} + +static int bcm43xx_wx_get_rangeparams(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + struct iw_range *range = (struct iw_range *)extra; + const struct ieee80211_geo *geo; + unsigned long flags; + int i, j; + struct bcm43xx_phyinfo *phy; + + data->data.length = sizeof(*range); + memset(range, 0, sizeof(*range)); + + //TODO: What about 802.11b? + /* 54Mb/s == ~27Mb/s payload throughput (802.11g) */ + range->throughput = 27 * 1000 * 1000; + + range->max_qual.qual = 100; + /* TODO: Real max RSSI */ + range->max_qual.level = 3; + range->max_qual.noise = 100; + range->max_qual.updated = 7; + + range->avg_qual.qual = 70; + range->avg_qual.level = 2; + range->avg_qual.noise = 40; + range->avg_qual.updated = 7; + + range->min_rts = BCM43xx_MIN_RTS_THRESHOLD; + range->max_rts = BCM43xx_MAX_RTS_THRESHOLD; + range->min_frag = MIN_FRAG_THRESHOLD; + range->max_frag = MAX_FRAG_THRESHOLD; + + range->encoding_size[0] = 5; + range->encoding_size[1] = 13; + range->num_encoding_sizes = 2; + range->max_encoding_tokens = WEP_KEYS; + + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = BCM43xx_WX_VERSION; + + range->enc_capa = IW_ENC_CAPA_WPA | + IW_ENC_CAPA_WPA2 | + IW_ENC_CAPA_CIPHER_TKIP | + IW_ENC_CAPA_CIPHER_CCMP; + + bcm43xx_lock(bcm, flags); + phy = bcm43xx_current_phy(bcm); + + range->num_bitrates = 0; + i = 0; + if (phy->type == BCM43xx_PHYTYPE_A || + phy->type == BCM43xx_PHYTYPE_G) { + range->num_bitrates = 8; + range->bitrate[i++] = IEEE80211_OFDM_RATE_6MB; + range->bitrate[i++] = IEEE80211_OFDM_RATE_9MB; + range->bitrate[i++] = IEEE80211_OFDM_RATE_12MB; + range->bitrate[i++] = IEEE80211_OFDM_RATE_18MB; + range->bitrate[i++] = IEEE80211_OFDM_RATE_24MB; + range->bitrate[i++] = IEEE80211_OFDM_RATE_36MB; + range->bitrate[i++] = IEEE80211_OFDM_RATE_48MB; + range->bitrate[i++] = IEEE80211_OFDM_RATE_54MB; + } + if (phy->type == BCM43xx_PHYTYPE_B || + phy->type == BCM43xx_PHYTYPE_G) { + range->num_bitrates += 4; + range->bitrate[i++] = IEEE80211_CCK_RATE_1MB; + range->bitrate[i++] = IEEE80211_CCK_RATE_2MB; + range->bitrate[i++] = IEEE80211_CCK_RATE_5MB; + range->bitrate[i++] = IEEE80211_CCK_RATE_11MB; + } + + geo = ieee80211_get_geo(bcm->ieee); + range->num_channels = geo->a_channels + geo->bg_channels; + j = 0; + for (i = 0; i < geo->a_channels; i++) { + if (j == IW_MAX_FREQUENCIES) + break; + range->freq[j].i = j + 1; + range->freq[j].m = geo->a[i].freq;//FIXME? + range->freq[j].e = 1; + j++; + } + for (i = 0; i < geo->bg_channels; i++) { + if (j == IW_MAX_FREQUENCIES) + break; + range->freq[j].i = j + 1; + range->freq[j].m = geo->bg[i].freq;//FIXME? + range->freq[j].e = 1; + j++; + } + range->num_frequency = j; + + bcm43xx_unlock(bcm, flags); + + return 0; +} + +static int bcm43xx_wx_set_nick(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + unsigned long flags; + size_t len; + + bcm43xx_lock(bcm, flags); + len = min((size_t)data->data.length, (size_t)IW_ESSID_MAX_SIZE); + memcpy(bcm->nick, extra, len); + bcm->nick[len] = '\0'; + bcm43xx_unlock(bcm, flags); + + return 0; +} + +static int bcm43xx_wx_get_nick(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + unsigned long flags; + size_t len; + + bcm43xx_lock(bcm, flags); + len = strlen(bcm->nick) + 1; + memcpy(extra, bcm->nick, len); + data->data.length = (__u16)len; + data->data.flags = 1; + bcm43xx_unlock(bcm, flags); + + return 0; +} + +static int bcm43xx_wx_set_rts(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + unsigned long flags; + int err = -EINVAL; + + bcm43xx_lock(bcm, flags); + if (data->rts.disabled) { + bcm->rts_threshold = BCM43xx_MAX_RTS_THRESHOLD; + err = 0; + } else { + if (data->rts.value >= BCM43xx_MIN_RTS_THRESHOLD && + data->rts.value <= BCM43xx_MAX_RTS_THRESHOLD) { + bcm->rts_threshold = data->rts.value; + err = 0; + } + } + bcm43xx_unlock(bcm, flags); + + return err; +} + +static int bcm43xx_wx_get_rts(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + unsigned long flags; + + bcm43xx_lock(bcm, flags); + data->rts.value = bcm->rts_threshold; + data->rts.fixed = 0; + data->rts.disabled = (bcm->rts_threshold == BCM43xx_MAX_RTS_THRESHOLD); + bcm43xx_unlock(bcm, flags); + + return 0; +} + +static int bcm43xx_wx_set_frag(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + unsigned long flags; + int err = -EINVAL; + + bcm43xx_lock(bcm, flags); + if (data->frag.disabled) { + bcm->ieee->fts = MAX_FRAG_THRESHOLD; + err = 0; + } else { + if (data->frag.value >= MIN_FRAG_THRESHOLD && + data->frag.value <= MAX_FRAG_THRESHOLD) { + bcm->ieee->fts = data->frag.value & ~0x1; + err = 0; + } + } + bcm43xx_unlock(bcm, flags); + + return err; +} + +static int bcm43xx_wx_get_frag(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + unsigned long flags; + + bcm43xx_lock(bcm, flags); + data->frag.value = bcm->ieee->fts; + data->frag.fixed = 0; + data->frag.disabled = (bcm->ieee->fts == MAX_FRAG_THRESHOLD); + bcm43xx_unlock(bcm, flags); + + return 0; +} + +static int bcm43xx_wx_set_xmitpower(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + struct bcm43xx_radioinfo *radio; + struct bcm43xx_phyinfo *phy; + unsigned long flags; + int err = -ENODEV; + u16 maxpower; + + if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) { + printk(PFX KERN_ERR "TX power not in dBm.\n"); + return -EOPNOTSUPP; + } + + bcm43xx_lock_mmio(bcm, flags); + if (!bcm->initialized) + goto out_unlock; + radio = bcm43xx_current_radio(bcm); + phy = bcm43xx_current_phy(bcm); + if (data->txpower.disabled != (!(radio->enabled))) { + if (data->txpower.disabled) + bcm43xx_radio_turn_off(bcm); + else + bcm43xx_radio_turn_on(bcm); + } + if (data->txpower.value > 0) { + /* desired and maxpower dBm values are in Q5.2 */ + if (phy->type == BCM43xx_PHYTYPE_A) + maxpower = bcm->sprom.maxpower_aphy; + else + maxpower = bcm->sprom.maxpower_bgphy; + radio->txpower_desired = limit_value(data->txpower.value << 2, + 0, maxpower); + bcm43xx_phy_xmitpower(bcm); + } + err = 0; + +out_unlock: + bcm43xx_unlock_mmio(bcm, flags); + + return err; +} + +static int bcm43xx_wx_get_xmitpower(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + struct bcm43xx_radioinfo *radio; + unsigned long flags; + int err = -ENODEV; + + bcm43xx_lock(bcm, flags); + if (!bcm->initialized) + goto out_unlock; + radio = bcm43xx_current_radio(bcm); + /* desired dBm value is in Q5.2 */ + data->txpower.value = radio->txpower_desired >> 2; + data->txpower.fixed = 1; + data->txpower.flags = IW_TXPOW_DBM; + data->txpower.disabled = !(radio->enabled); + + err = 0; +out_unlock: + bcm43xx_unlock(bcm, flags); + + return err; +} + +static int bcm43xx_wx_set_encoding(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + int err; + + err = ieee80211_wx_set_encode(bcm->ieee, info, data, extra); + + return err; +} + +static int bcm43xx_wx_set_encodingext(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + int err; + + err = ieee80211_wx_set_encodeext(bcm->ieee, info, data, extra); + + return err; +} + +static int bcm43xx_wx_get_encoding(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + int err; + + err = ieee80211_wx_get_encode(bcm->ieee, info, data, extra); + + return err; +} + +static int bcm43xx_wx_get_encodingext(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + int err; + + err = ieee80211_wx_get_encodeext(bcm->ieee, info, data, extra); + + return err; +} + +static int bcm43xx_wx_set_interfmode(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + unsigned long flags; + int mode, err = 0; + + mode = *((int *)extra); + switch (mode) { + case 0: + mode = BCM43xx_RADIO_INTERFMODE_NONE; + break; + case 1: + mode = BCM43xx_RADIO_INTERFMODE_NONWLAN; + break; + case 2: + mode = BCM43xx_RADIO_INTERFMODE_MANUALWLAN; + break; + case 3: + mode = BCM43xx_RADIO_INTERFMODE_AUTOWLAN; + break; + default: + printk(KERN_ERR PFX "set_interfmode allowed parameters are: " + "0 => None, 1 => Non-WLAN, 2 => WLAN, " + "3 => Auto-WLAN\n"); + return -EINVAL; + } + + bcm43xx_lock_mmio(bcm, flags); + if (bcm->initialized) { + err = bcm43xx_radio_set_interference_mitigation(bcm, mode); + if (err) { + printk(KERN_ERR PFX "Interference Mitigation not " + "supported by device\n"); + } + } else { + if (mode == BCM43xx_RADIO_INTERFMODE_AUTOWLAN) { + printk(KERN_ERR PFX "Interference Mitigation mode Auto-WLAN " + "not supported while the interface is down.\n"); + err = -ENODEV; + } else + bcm43xx_current_radio(bcm)->interfmode = mode; + } + bcm43xx_unlock_mmio(bcm, flags); + + return err; +} + +static int bcm43xx_wx_get_interfmode(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + unsigned long flags; + int mode; + + bcm43xx_lock(bcm, flags); + mode = bcm43xx_current_radio(bcm)->interfmode; + bcm43xx_unlock(bcm, flags); + + switch (mode) { + case BCM43xx_RADIO_INTERFMODE_NONE: + strncpy(extra, "0 (No Interference Mitigation)", MAX_WX_STRING); + break; + case BCM43xx_RADIO_INTERFMODE_NONWLAN: + strncpy(extra, "1 (Non-WLAN Interference Mitigation)", MAX_WX_STRING); + break; + case BCM43xx_RADIO_INTERFMODE_MANUALWLAN: + strncpy(extra, "2 (WLAN Interference Mitigation)", MAX_WX_STRING); + break; + default: + assert(0); + } + data->data.length = strlen(extra) + 1; + + return 0; +} + +static int bcm43xx_wx_set_shortpreamble(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + unsigned long flags; + int on; + + on = *((int *)extra); + bcm43xx_lock(bcm, flags); + bcm->short_preamble = !!on; + bcm43xx_unlock(bcm, flags); + + return 0; +} + +static int bcm43xx_wx_get_shortpreamble(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + unsigned long flags; + int on; + + bcm43xx_lock(bcm, flags); + on = bcm->short_preamble; + bcm43xx_unlock(bcm, flags); + + if (on) + strncpy(extra, "1 (Short Preamble enabled)", MAX_WX_STRING); + else + strncpy(extra, "0 (Short Preamble disabled)", MAX_WX_STRING); + data->data.length = strlen(extra) + 1; + + return 0; +} + +static int bcm43xx_wx_set_swencryption(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + unsigned long flags; + int on; + + on = *((int *)extra); + + bcm43xx_lock(bcm, flags); + bcm->ieee->host_encrypt = !!on; + bcm->ieee->host_decrypt = !!on; + bcm->ieee->host_build_iv = !on; + bcm43xx_unlock(bcm, flags); + + return 0; +} + +static int bcm43xx_wx_get_swencryption(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + unsigned long flags; + int on; + + bcm43xx_lock(bcm, flags); + on = bcm->ieee->host_encrypt; + bcm43xx_unlock(bcm, flags); + + if (on) + strncpy(extra, "1 (SW encryption enabled) ", MAX_WX_STRING); + else + strncpy(extra, "0 (SW encryption disabled) ", MAX_WX_STRING); + data->data.length = strlen(extra + 1); + + return 0; +} + +/* Enough buffer to hold a hexdump of the sprom data. */ +#define SPROM_BUFFERSIZE 512 + +static int sprom2hex(const u16 *sprom, char *dump) +{ + int i, pos = 0; + + for (i = 0; i < BCM43xx_SPROM_SIZE; i++) { + pos += snprintf(dump + pos, SPROM_BUFFERSIZE - pos - 1, + "%04X", swab16(sprom[i]) & 0xFFFF); + } + + return pos + 1; +} + +static int hex2sprom(u16 *sprom, const char *dump, unsigned int len) +{ + char tmp[5] = { 0 }; + int cnt = 0; + unsigned long parsed; + + if (len < BCM43xx_SPROM_SIZE * sizeof(u16) * 2) + return -EINVAL; + while (cnt < BCM43xx_SPROM_SIZE) { + memcpy(tmp, dump, 4); + dump += 4; + parsed = simple_strtoul(tmp, NULL, 16); + sprom[cnt++] = swab16((u16)parsed); + } + + return 0; +} + +static int bcm43xx_wx_sprom_read(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + int err = -EPERM; + u16 *sprom; + unsigned long flags; + + if (!capable(CAP_SYS_RAWIO)) + goto out; + + err = -ENOMEM; + sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom), + GFP_KERNEL); + if (!sprom) + goto out; + + bcm43xx_lock_mmio(bcm, flags); + err = -ENODEV; + if (bcm->initialized) + err = bcm43xx_sprom_read(bcm, sprom); + bcm43xx_unlock_mmio(bcm, flags); + if (!err) + data->data.length = sprom2hex(sprom, extra); + kfree(sprom); +out: + return err; +} + +static int bcm43xx_wx_sprom_write(struct net_device *net_dev, + struct iw_request_info *info, + union iwreq_data *data, + char *extra) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + int err = -EPERM; + u16 *sprom; + unsigned long flags; + char *input; + unsigned int len; + + if (!capable(CAP_SYS_RAWIO)) + goto out; + + err = -ENOMEM; + sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom), + GFP_KERNEL); + if (!sprom) + goto out; + + len = data->data.length; + extra[len - 1] = '\0'; + input = strchr(extra, ':'); + if (input) { + input++; + len -= input - extra; + } else + input = extra; + err = hex2sprom(sprom, input, len); + if (err) + goto out_kfree; + + bcm43xx_lock_mmio(bcm, flags); + err = -ENODEV; + if (bcm->initialized) + err = bcm43xx_sprom_write(bcm, sprom); + bcm43xx_unlock_mmio(bcm, flags); +out_kfree: + kfree(sprom); +out: + return err; +} + +/* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */ + +static struct iw_statistics *bcm43xx_get_wireless_stats(struct net_device *net_dev) +{ + struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); + struct ieee80211softmac_device *mac = ieee80211_priv(net_dev); + struct iw_statistics *wstats; + + wstats = &bcm->stats.wstats; + if (!mac->associated) { + wstats->miss.beacon = 0; +// bcm->ieee->ieee_stats.tx_retry_limit_exceeded = 0; // FIXME: should this be cleared here? + wstats->discard.retries = 0; +// bcm->ieee->ieee_stats.tx_discards_wrong_sa = 0; // FIXME: same question + wstats->discard.nwid = 0; +// bcm->ieee->ieee_stats.rx_discards_undecryptable = 0; // FIXME: ditto + wstats->discard.code = 0; +// bcm->ieee->ieee_stats.rx_fragments = 0; // FIXME: same here + wstats->discard.fragment = 0; + wstats->discard.misc = 0; + wstats->qual.qual = 0; + wstats->qual.level = 0; + wstats->qual.noise = 0; + wstats->qual.updated = 7; + wstats->qual.updated |= IW_QUAL_NOISE_INVALID | + IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID; + return wstats; + } + /* fill in the real statistics when iface associated */ + wstats->qual.qual = 100; // TODO: get the real signal quality + wstats->qual.level = 3 - bcm->stats.link_quality; + wstats->qual.noise = bcm->stats.noise; + wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED | + IW_QUAL_NOISE_UPDATED; + wstats->discard.code = bcm->ieee->ieee_stats.rx_discards_undecryptable; + wstats->discard.retries = bcm->ieee->ieee_stats.tx_retry_limit_exceeded; + wstats->discard.nwid = bcm->ieee->ieee_stats.tx_discards_wrong_sa; + wstats->discard.fragment = bcm->ieee->ieee_stats.rx_fragments; + wstats->discard.misc = 0; // FIXME + wstats->miss.beacon = 0; // FIXME + return wstats; +} + + +#ifdef WX +# undef WX +#endif +#define WX(ioctl) [(ioctl) - SIOCSIWCOMMIT] +static const iw_handler bcm43xx_wx_handlers[] = { + /* Wireless Identification */ + WX(SIOCGIWNAME) = bcm43xx_wx_get_name, + /* Basic operations */ + WX(SIOCSIWFREQ) = bcm43xx_wx_set_channelfreq, + WX(SIOCGIWFREQ) = bcm43xx_wx_get_channelfreq, + WX(SIOCSIWMODE) = bcm43xx_wx_set_mode, + WX(SIOCGIWMODE) = bcm43xx_wx_get_mode, + /* Informative stuff */ + WX(SIOCGIWRANGE) = bcm43xx_wx_get_rangeparams, + /* Access Point manipulation */ + WX(SIOCSIWAP) = ieee80211softmac_wx_set_wap, + WX(SIOCGIWAP) = ieee80211softmac_wx_get_wap, + WX(SIOCSIWSCAN) = ieee80211softmac_wx_trigger_scan, + WX(SIOCGIWSCAN) = ieee80211softmac_wx_get_scan_results, + /* 802.11 specific support */ + WX(SIOCSIWESSID) = ieee80211softmac_wx_set_essid, + WX(SIOCGIWESSID) = ieee80211softmac_wx_get_essid, + WX(SIOCSIWNICKN) = bcm43xx_wx_set_nick, + WX(SIOCGIWNICKN) = bcm43xx_wx_get_nick, + /* Other parameters */ + WX(SIOCSIWRATE) = ieee80211softmac_wx_set_rate, + WX(SIOCGIWRATE) = ieee80211softmac_wx_get_rate, + WX(SIOCSIWRTS) = bcm43xx_wx_set_rts, + WX(SIOCGIWRTS) = bcm43xx_wx_get_rts, + WX(SIOCSIWFRAG) = bcm43xx_wx_set_frag, + WX(SIOCGIWFRAG) = bcm43xx_wx_get_frag, + WX(SIOCSIWTXPOW) = bcm43xx_wx_set_xmitpower, + WX(SIOCGIWTXPOW) = bcm43xx_wx_get_xmitpower, +//TODO WX(SIOCSIWRETRY) = bcm43xx_wx_set_retry, +//TODO WX(SIOCGIWRETRY) = bcm43xx_wx_get_retry, + /* Encoding */ + WX(SIOCSIWENCODE) = bcm43xx_wx_set_encoding, + WX(SIOCGIWENCODE) = bcm43xx_wx_get_encoding, + WX(SIOCSIWENCODEEXT) = bcm43xx_wx_set_encodingext, + WX(SIOCGIWENCODEEXT) = bcm43xx_wx_get_encodingext, + /* Power saving */ +//TODO WX(SIOCSIWPOWER) = bcm43xx_wx_set_power, +//TODO WX(SIOCGIWPOWER) = bcm43xx_wx_get_power, + WX(SIOCSIWGENIE) = ieee80211softmac_wx_set_genie, + WX(SIOCGIWGENIE) = ieee80211softmac_wx_get_genie, + WX(SIOCSIWAUTH) = ieee80211_wx_set_auth, + WX(SIOCGIWAUTH) = ieee80211_wx_get_auth, +}; +#undef WX + +static const iw_handler bcm43xx_priv_wx_handlers[] = { + /* Set Interference Mitigation Mode. */ + bcm43xx_wx_set_interfmode, + /* Get Interference Mitigation Mode. */ + bcm43xx_wx_get_interfmode, + /* Enable/Disable Short Preamble mode. */ + bcm43xx_wx_set_shortpreamble, + /* Get Short Preamble mode. */ + bcm43xx_wx_get_shortpreamble, + /* Enable/Disable Software Encryption mode */ + bcm43xx_wx_set_swencryption, + /* Get Software Encryption mode */ + bcm43xx_wx_get_swencryption, + /* Write SRPROM data. */ + bcm43xx_wx_sprom_write, + /* Read SPROM data. */ + bcm43xx_wx_sprom_read, +}; + +#define PRIV_WX_SET_INTERFMODE (SIOCIWFIRSTPRIV + 0) +#define PRIV_WX_GET_INTERFMODE (SIOCIWFIRSTPRIV + 1) +#define PRIV_WX_SET_SHORTPREAMBLE (SIOCIWFIRSTPRIV + 2) +#define PRIV_WX_GET_SHORTPREAMBLE (SIOCIWFIRSTPRIV + 3) +#define PRIV_WX_SET_SWENCRYPTION (SIOCIWFIRSTPRIV + 4) +#define PRIV_WX_GET_SWENCRYPTION (SIOCIWFIRSTPRIV + 5) +#define PRIV_WX_SPROM_WRITE (SIOCIWFIRSTPRIV + 6) +#define PRIV_WX_SPROM_READ (SIOCIWFIRSTPRIV + 7) + +#define PRIV_WX_DUMMY(ioctl) \ + { \ + .cmd = (ioctl), \ + .name = "__unused" \ + } + +static const struct iw_priv_args bcm43xx_priv_wx_args[] = { + { + .cmd = PRIV_WX_SET_INTERFMODE, + .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + .name = "set_interfmode", + }, + { + .cmd = PRIV_WX_GET_INTERFMODE, + .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING, + .name = "get_interfmode", + }, + { + .cmd = PRIV_WX_SET_SHORTPREAMBLE, + .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + .name = "set_shortpreambl", + }, + { + .cmd = PRIV_WX_GET_SHORTPREAMBLE, + .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING, + .name = "get_shortpreambl", + }, + { + .cmd = PRIV_WX_SET_SWENCRYPTION, + .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + .name = "set_swencryption", + }, + { + .cmd = PRIV_WX_GET_SWENCRYPTION, + .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING, + .name = "get_swencryption", + }, + { + .cmd = PRIV_WX_SPROM_WRITE, + .set_args = IW_PRIV_TYPE_CHAR | SPROM_BUFFERSIZE, + .name = "write_sprom", + }, + { + .cmd = PRIV_WX_SPROM_READ, + .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | SPROM_BUFFERSIZE, + .name = "read_sprom", + }, +}; + +const struct iw_handler_def bcm43xx_wx_handlers_def = { + .standard = bcm43xx_wx_handlers, + .num_standard = ARRAY_SIZE(bcm43xx_wx_handlers), + .num_private = ARRAY_SIZE(bcm43xx_priv_wx_handlers), + .num_private_args = ARRAY_SIZE(bcm43xx_priv_wx_args), + .private = bcm43xx_priv_wx_handlers, + .private_args = bcm43xx_priv_wx_args, + .get_wireless_stats = bcm43xx_get_wireless_stats, +}; diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_wx.h b/drivers/net/wireless/bcm43xx/bcm43xx_wx.h new file mode 100644 index 000000000000..1f29ff3aa4c3 --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_wx.h @@ -0,0 +1,36 @@ +/* + + Broadcom BCM43xx wireless driver + + Copyright (c) 2005 Martin Langer , + Stefano Brivio + Michael Buesch + Danny van Dyk + Andreas Jaggi + + Some parts of the code in this file are derived from the ipw2200 + driver Copyright(c) 2003 - 2004 Intel Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#ifndef BCM43xx_WX_H_ +#define BCM43xx_WX_H_ + +extern const struct iw_handler_def bcm43xx_wx_handlers_def; + +#endif /* BCM43xx_WX_H_ */ diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c b/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c new file mode 100644 index 000000000000..d8ece28c079f --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c @@ -0,0 +1,582 @@ +/* + + Broadcom BCM43xx wireless driver + + Transmission (TX/RX) related functions. + + Copyright (c) 2005 Martin Langer , + Stefano Brivio + Michael Buesch + Danny van Dyk + Andreas Jaggi + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, + Boston, MA 02110-1301, USA. + +*/ + +#include "bcm43xx_xmit.h" + +#include + + +/* Extract the bitrate out of a CCK PLCP header. */ +static u8 bcm43xx_plcp_get_bitrate_cck(struct bcm43xx_plcp_hdr4 *plcp) +{ + switch (plcp->raw[0]) { + case 0x0A: + return IEEE80211_CCK_RATE_1MB; + case 0x14: + return IEEE80211_CCK_RATE_2MB; + case 0x37: + return IEEE80211_CCK_RATE_5MB; + case 0x6E: + return IEEE80211_CCK_RATE_11MB; + } + assert(0); + return 0; +} + +/* Extract the bitrate out of an OFDM PLCP header. */ +static u8 bcm43xx_plcp_get_bitrate_ofdm(struct bcm43xx_plcp_hdr4 *plcp) +{ + switch (plcp->raw[0] & 0xF) { + case 0xB: + return IEEE80211_OFDM_RATE_6MB; + case 0xF: + return IEEE80211_OFDM_RATE_9MB; + case 0xA: + return IEEE80211_OFDM_RATE_12MB; + case 0xE: + return IEEE80211_OFDM_RATE_18MB; + case 0x9: + return IEEE80211_OFDM_RATE_24MB; + case 0xD: + return IEEE80211_OFDM_RATE_36MB; + case 0x8: + return IEEE80211_OFDM_RATE_48MB; + case 0xC: + return IEEE80211_OFDM_RATE_54MB; + } + assert(0); + return 0; +} + +u8 bcm43xx_plcp_get_ratecode_cck(const u8 bitrate) +{ + switch (bitrate) { + case IEEE80211_CCK_RATE_1MB: + return 0x0A; + case IEEE80211_CCK_RATE_2MB: + return 0x14; + case IEEE80211_CCK_RATE_5MB: + return 0x37; + case IEEE80211_CCK_RATE_11MB: + return 0x6E; + } + assert(0); + return 0; +} + +u8 bcm43xx_plcp_get_ratecode_ofdm(const u8 bitrate) +{ + switch (bitrate) { + case IEEE80211_OFDM_RATE_6MB: + return 0xB; + case IEEE80211_OFDM_RATE_9MB: + return 0xF; + case IEEE80211_OFDM_RATE_12MB: + return 0xA; + case IEEE80211_OFDM_RATE_18MB: + return 0xE; + case IEEE80211_OFDM_RATE_24MB: + return 0x9; + case IEEE80211_OFDM_RATE_36MB: + return 0xD; + case IEEE80211_OFDM_RATE_48MB: + return 0x8; + case IEEE80211_OFDM_RATE_54MB: + return 0xC; + } + assert(0); + return 0; +} + +static void bcm43xx_generate_plcp_hdr(struct bcm43xx_plcp_hdr4 *plcp, + const u16 octets, const u8 bitrate, + const int ofdm_modulation) +{ + __le32 *data = &(plcp->data); + __u8 *raw = plcp->raw; + + if (ofdm_modulation) { + *data = bcm43xx_plcp_get_ratecode_ofdm(bitrate); + assert(!(octets & 0xF000)); + *data |= (octets << 5); + *data = cpu_to_le32(*data); + } else { + u32 plen; + + plen = octets * 16 / bitrate; + if ((octets * 16 % bitrate) > 0) { + plen++; + if ((bitrate == IEEE80211_CCK_RATE_11MB) + && ((octets * 8 % 11) < 4)) { + raw[1] = 0x84; + } else + raw[1] = 0x04; + } else + raw[1] = 0x04; + *data |= cpu_to_le32(plen << 16); + raw[0] = bcm43xx_plcp_get_ratecode_cck(bitrate); + } +} + +static u8 bcm43xx_calc_fallback_rate(u8 bitrate) +{ + switch (bitrate) { + case IEEE80211_CCK_RATE_1MB: + return IEEE80211_CCK_RATE_1MB; + case IEEE80211_CCK_RATE_2MB: + return IEEE80211_CCK_RATE_1MB; + case IEEE80211_CCK_RATE_5MB: + return IEEE80211_CCK_RATE_2MB; + case IEEE80211_CCK_RATE_11MB: + return IEEE80211_CCK_RATE_5MB; + case IEEE80211_OFDM_RATE_6MB: + return IEEE80211_CCK_RATE_5MB; + case IEEE80211_OFDM_RATE_9MB: + return IEEE80211_OFDM_RATE_6MB; + case IEEE80211_OFDM_RATE_12MB: + return IEEE80211_OFDM_RATE_9MB; + case IEEE80211_OFDM_RATE_18MB: + return IEEE80211_OFDM_RATE_12MB; + case IEEE80211_OFDM_RATE_24MB: + return IEEE80211_OFDM_RATE_18MB; + case IEEE80211_OFDM_RATE_36MB: + return IEEE80211_OFDM_RATE_24MB; + case IEEE80211_OFDM_RATE_48MB: + return IEEE80211_OFDM_RATE_36MB; + case IEEE80211_OFDM_RATE_54MB: + return IEEE80211_OFDM_RATE_48MB; + } + assert(0); + return 0; +} + +static +__le16 bcm43xx_calc_duration_id(const struct ieee80211_hdr *wireless_header, + u8 bitrate) +{ + const u16 frame_ctl = le16_to_cpu(wireless_header->frame_ctl); + __le16 duration_id = wireless_header->duration_id; + + switch (WLAN_FC_GET_TYPE(frame_ctl)) { + case IEEE80211_FTYPE_DATA: + case IEEE80211_FTYPE_MGMT: + //TODO: Steal the code from ieee80211, once it is completed there. + break; + case IEEE80211_FTYPE_CTL: + /* Use the original duration/id. */ + break; + default: + assert(0); + } + + return duration_id; +} + +static inline +u16 ceiling_div(u16 dividend, u16 divisor) +{ + return ((dividend + divisor - 1) / divisor); +} + +static void bcm43xx_generate_rts(const struct bcm43xx_phyinfo *phy, + struct bcm43xx_txhdr *txhdr, + u16 *flags, + u8 bitrate, + const struct ieee80211_hdr_4addr *wlhdr) +{ + u16 fctl; + u16 dur; + u8 fallback_bitrate; + int ofdm_modulation; + int fallback_ofdm_modulation; +// u8 *sa, *da; + u16 flen; + +//FIXME sa = ieee80211_get_SA((struct ieee80211_hdr *)wlhdr); +//FIXME da = ieee80211_get_DA((struct ieee80211_hdr *)wlhdr); + fallback_bitrate = bcm43xx_calc_fallback_rate(bitrate); + ofdm_modulation = !(ieee80211_is_cck_rate(bitrate)); + fallback_ofdm_modulation = !(ieee80211_is_cck_rate(fallback_bitrate)); + + flen = sizeof(u16) + sizeof(u16) + ETH_ALEN + ETH_ALEN + IEEE80211_FCS_LEN, + bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4 *)(&txhdr->rts_cts_plcp), + flen, bitrate, + !ieee80211_is_cck_rate(bitrate)); + bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4 *)(&txhdr->rts_cts_fallback_plcp), + flen, fallback_bitrate, + !ieee80211_is_cck_rate(fallback_bitrate)); + fctl = IEEE80211_FTYPE_CTL; + fctl |= IEEE80211_STYPE_RTS; + dur = le16_to_cpu(wlhdr->duration_id); +/*FIXME: should we test for dur==0 here and let it unmodified in this case? + * The following assert checks for this case... + */ +assert(dur); +/*FIXME: The duration calculation is not really correct. + * I am not 100% sure which bitrate to use. We use the RTS rate here, + * but this is likely to be wrong. + */ + if (phy->type == BCM43xx_PHYTYPE_A) { + /* Three times SIFS */ + dur += 16 * 3; + /* Add ACK duration. */ + dur += ceiling_div((16 + 8 * (14 /*bytes*/) + 6) * 10, + bitrate * 4); + /* Add CTS duration. */ + dur += ceiling_div((16 + 8 * (14 /*bytes*/) + 6) * 10, + bitrate * 4); + } else { + /* Three times SIFS */ + dur += 10 * 3; + /* Add ACK duration. */ + dur += ceiling_div(8 * (14 /*bytes*/) * 10, + bitrate); + /* Add CTS duration. */ + dur += ceiling_div(8 * (14 /*bytes*/) * 10, + bitrate); + } + + txhdr->rts_cts_frame_control = cpu_to_le16(fctl); + txhdr->rts_cts_dur = cpu_to_le16(dur); +//printk(BCM43xx_MACFMT " " BCM43xx_MACFMT " " BCM43xx_MACFMT "\n", BCM43xx_MACARG(wlhdr->addr1), BCM43xx_MACARG(wlhdr->addr2), BCM43xx_MACARG(wlhdr->addr3)); +//printk(BCM43xx_MACFMT " " BCM43xx_MACFMT "\n", BCM43xx_MACARG(sa), BCM43xx_MACARG(da)); + memcpy(txhdr->rts_cts_mac1, wlhdr->addr1, ETH_ALEN);//FIXME! +// memcpy(txhdr->rts_cts_mac2, sa, ETH_ALEN); + + *flags |= BCM43xx_TXHDRFLAG_RTSCTS; + *flags |= BCM43xx_TXHDRFLAG_RTS; + if (ofdm_modulation) + *flags |= BCM43xx_TXHDRFLAG_RTSCTS_OFDM; + if (fallback_ofdm_modulation) + *flags |= BCM43xx_TXHDRFLAG_RTSCTSFALLBACK_OFDM; +} + +void bcm43xx_generate_txhdr(struct bcm43xx_private *bcm, + struct bcm43xx_txhdr *txhdr, + const unsigned char *fragment_data, + const unsigned int fragment_len, + const int is_first_fragment, + const u16 cookie) +{ + const struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + const struct ieee80211_hdr_4addr *wireless_header = (const struct ieee80211_hdr_4addr *)fragment_data; + const struct ieee80211_security *secinfo = &bcm->ieee->sec; + u8 bitrate; + u8 fallback_bitrate; + int ofdm_modulation; + int fallback_ofdm_modulation; + u16 plcp_fragment_len = fragment_len; + u16 flags = 0; + u16 control = 0; + u16 wsec_rate = 0; + u16 encrypt_frame; + + /* Now construct the TX header. */ + memset(txhdr, 0, sizeof(*txhdr)); + + bitrate = bcm->softmac->txrates.default_rate; + ofdm_modulation = !(ieee80211_is_cck_rate(bitrate)); + fallback_bitrate = bcm43xx_calc_fallback_rate(bitrate); + fallback_ofdm_modulation = !(ieee80211_is_cck_rate(fallback_bitrate)); + + /* Set Frame Control from 80211 header. */ + txhdr->frame_control = wireless_header->frame_ctl; + /* Copy address1 from 80211 header. */ + memcpy(txhdr->mac1, wireless_header->addr1, 6); + /* Set the fallback duration ID. */ + txhdr->fallback_dur_id = bcm43xx_calc_duration_id((const struct ieee80211_hdr *)wireless_header, + fallback_bitrate); + /* Set the cookie (used as driver internal ID for the frame) */ + txhdr->cookie = cpu_to_le16(cookie); + + /* Hardware appends FCS. */ + plcp_fragment_len += IEEE80211_FCS_LEN; + + /* Hardware encryption. */ + encrypt_frame = le16_to_cpup(&wireless_header->frame_ctl) & IEEE80211_FCTL_PROTECTED; + if (encrypt_frame && !bcm->ieee->host_encrypt) { + const struct ieee80211_hdr_3addr *hdr = (struct ieee80211_hdr_3addr *)wireless_header; + memcpy(txhdr->wep_iv, hdr->payload, 4); + /* Hardware appends ICV. */ + plcp_fragment_len += 4; + + wsec_rate |= (bcm->key[secinfo->active_key].algorithm << BCM43xx_TXHDR_WSEC_ALGO_SHIFT) + & BCM43xx_TXHDR_WSEC_ALGO_MASK; + wsec_rate |= (secinfo->active_key << BCM43xx_TXHDR_WSEC_KEYINDEX_SHIFT) + & BCM43xx_TXHDR_WSEC_KEYINDEX_MASK; + } + + /* Generate the PLCP header and the fallback PLCP header. */ + bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4 *)(&txhdr->plcp), + plcp_fragment_len, + bitrate, ofdm_modulation); + bcm43xx_generate_plcp_hdr(&txhdr->fallback_plcp, plcp_fragment_len, + fallback_bitrate, fallback_ofdm_modulation); + + /* Set the CONTROL field */ + if (ofdm_modulation) + control |= BCM43xx_TXHDRCTL_OFDM; + if (bcm->short_preamble) //FIXME: could be the other way around, please test + control |= BCM43xx_TXHDRCTL_SHORT_PREAMBLE; + control |= (phy->antenna_diversity << BCM43xx_TXHDRCTL_ANTENNADIV_SHIFT) + & BCM43xx_TXHDRCTL_ANTENNADIV_MASK; + + /* Set the FLAGS field */ + if (!is_multicast_ether_addr(wireless_header->addr1) && + !is_broadcast_ether_addr(wireless_header->addr1)) + flags |= BCM43xx_TXHDRFLAG_EXPECTACK; + if (1 /* FIXME: PS poll?? */) + flags |= 0x10; // FIXME: unknown meaning. + if (fallback_ofdm_modulation) + flags |= BCM43xx_TXHDRFLAG_FALLBACKOFDM; + if (is_first_fragment) + flags |= BCM43xx_TXHDRFLAG_FIRSTFRAGMENT; + + /* Set WSEC/RATE field */ + wsec_rate |= (txhdr->plcp.raw[0] << BCM43xx_TXHDR_RATE_SHIFT) + & BCM43xx_TXHDR_RATE_MASK; + + /* Generate the RTS/CTS packet, if required. */ + /* FIXME: We should first try with CTS-to-self, + * if we are on 80211g. If we get too many + * failures (hidden nodes), we should switch back to RTS/CTS. + */ + if (0/*FIXME txctl->use_rts_cts*/) { + bcm43xx_generate_rts(phy, txhdr, &flags, + 0/*FIXME txctl->rts_cts_rate*/, + wireless_header); + } + + txhdr->flags = cpu_to_le16(flags); + txhdr->control = cpu_to_le16(control); + txhdr->wsec_rate = cpu_to_le16(wsec_rate); +} + +static s8 bcm43xx_rssi_postprocess(struct bcm43xx_private *bcm, + u8 in_rssi, int ofdm, + int adjust_2053, int adjust_2050) +{ + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + s32 tmp; + + switch (radio->version) { + case 0x2050: + if (ofdm) { + tmp = in_rssi; + if (tmp > 127) + tmp -= 256; + tmp *= 73; + tmp /= 64; + if (adjust_2050) + tmp += 25; + else + tmp -= 3; + } else { + if (bcm->sprom.boardflags & BCM43xx_BFL_RSSI) { + if (in_rssi > 63) + in_rssi = 63; + tmp = radio->nrssi_lt[in_rssi]; + tmp = 31 - tmp; + tmp *= -131; + tmp /= 128; + tmp -= 57; + } else { + tmp = in_rssi; + tmp = 31 - tmp; + tmp *= -149; + tmp /= 128; + tmp -= 68; + } + if (phy->type == BCM43xx_PHYTYPE_G && + adjust_2050) + tmp += 25; + } + break; + case 0x2060: + if (in_rssi > 127) + tmp = in_rssi - 256; + else + tmp = in_rssi; + break; + default: + tmp = in_rssi; + tmp -= 11; + tmp *= 103; + tmp /= 64; + if (adjust_2053) + tmp -= 109; + else + tmp -= 83; + } + + return (s8)tmp; +} + +//TODO +#if 0 +static s8 bcm43xx_rssinoise_postprocess(struct bcm43xx_private *bcm, + u8 in_rssi) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + s8 ret; + + if (phy->type == BCM43xx_PHYTYPE_A) { + //TODO: Incomplete specs. + ret = 0; + } else + ret = bcm43xx_rssi_postprocess(bcm, in_rssi, 0, 1, 1); + + return ret; +} +#endif + +int bcm43xx_rx(struct bcm43xx_private *bcm, + struct sk_buff *skb, + struct bcm43xx_rxhdr *rxhdr) +{ + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_plcp_hdr4 *plcp; + struct ieee80211_rx_stats stats; + struct ieee80211_hdr_4addr *wlhdr; + u16 frame_ctl; + int is_packet_for_us = 0; + int err = -EINVAL; + const u16 rxflags1 = le16_to_cpu(rxhdr->flags1); + const u16 rxflags2 = le16_to_cpu(rxhdr->flags2); + const u16 rxflags3 = le16_to_cpu(rxhdr->flags3); + const int is_ofdm = !!(rxflags1 & BCM43xx_RXHDR_FLAGS1_OFDM); + + if (rxflags2 & BCM43xx_RXHDR_FLAGS2_TYPE2FRAME) { + plcp = (struct bcm43xx_plcp_hdr4 *)(skb->data + 2); + /* Skip two unknown bytes and the PLCP header. */ + skb_pull(skb, 2 + sizeof(struct bcm43xx_plcp_hdr6)); + } else { + plcp = (struct bcm43xx_plcp_hdr4 *)(skb->data); + /* Skip the PLCP header. */ + skb_pull(skb, sizeof(struct bcm43xx_plcp_hdr6)); + } + /* The SKB contains the PAYLOAD (wireless header + data) + * at this point. The FCS at the end is stripped. + */ + + memset(&stats, 0, sizeof(stats)); + stats.mac_time = le16_to_cpu(rxhdr->mactime); + stats.rssi = bcm43xx_rssi_postprocess(bcm, rxhdr->rssi, is_ofdm, + !!(rxflags1 & BCM43xx_RXHDR_FLAGS1_2053RSSIADJ), + !!(rxflags3 & BCM43xx_RXHDR_FLAGS3_2050RSSIADJ)); + stats.signal = rxhdr->signal_quality; //FIXME +//TODO stats.noise = + if (is_ofdm) + stats.rate = bcm43xx_plcp_get_bitrate_ofdm(plcp); + else + stats.rate = bcm43xx_plcp_get_bitrate_cck(plcp); +//printk("RX ofdm %d, rate == %u\n", is_ofdm, stats.rate); + stats.received_channel = radio->channel; +//TODO stats.control = + stats.mask = IEEE80211_STATMASK_SIGNAL | +//TODO IEEE80211_STATMASK_NOISE | + IEEE80211_STATMASK_RATE | + IEEE80211_STATMASK_RSSI; + if (phy->type == BCM43xx_PHYTYPE_A) + stats.freq = IEEE80211_52GHZ_BAND; + else + stats.freq = IEEE80211_24GHZ_BAND; + stats.len = skb->len; + + bcm->stats.last_rx = jiffies; + if (bcm->ieee->iw_mode == IW_MODE_MONITOR) { + err = ieee80211_rx(bcm->ieee, skb, &stats); + return (err == 0) ? -EINVAL : 0; + } + + wlhdr = (struct ieee80211_hdr_4addr *)(skb->data); + + switch (bcm->ieee->iw_mode) { + case IW_MODE_ADHOC: + if (memcmp(wlhdr->addr1, bcm->net_dev->dev_addr, ETH_ALEN) == 0 || + memcmp(wlhdr->addr3, bcm->ieee->bssid, ETH_ALEN) == 0 || + is_broadcast_ether_addr(wlhdr->addr1) || + is_multicast_ether_addr(wlhdr->addr1) || + bcm->net_dev->flags & IFF_PROMISC) + is_packet_for_us = 1; + break; + case IW_MODE_INFRA: + default: + /* When receiving multicast or broadcast packets, filter out + the packets we send ourself; we shouldn't see those */ + if (memcmp(wlhdr->addr3, bcm->ieee->bssid, ETH_ALEN) == 0 || + memcmp(wlhdr->addr1, bcm->net_dev->dev_addr, ETH_ALEN) == 0 || + (memcmp(wlhdr->addr3, bcm->net_dev->dev_addr, ETH_ALEN) && + (is_broadcast_ether_addr(wlhdr->addr1) || + is_multicast_ether_addr(wlhdr->addr1) || + bcm->net_dev->flags & IFF_PROMISC))) + is_packet_for_us = 1; + break; + } + + frame_ctl = le16_to_cpu(wlhdr->frame_ctl); + if ((frame_ctl & IEEE80211_FCTL_PROTECTED) && !bcm->ieee->host_decrypt) { + frame_ctl &= ~IEEE80211_FCTL_PROTECTED; + wlhdr->frame_ctl = cpu_to_le16(frame_ctl); + /* trim IV and ICV */ + /* FIXME: this must be done only for WEP encrypted packets */ + if (skb->len < 32) { + dprintkl(KERN_ERR PFX "RX packet dropped (PROTECTED flag " + "set and length < 32)\n"); + return -EINVAL; + } else { + memmove(skb->data + 4, skb->data, 24); + skb_pull(skb, 4); + skb_trim(skb, skb->len - 4); + stats.len -= 8; + } + wlhdr = (struct ieee80211_hdr_4addr *)(skb->data); + } + + switch (WLAN_FC_GET_TYPE(frame_ctl)) { + case IEEE80211_FTYPE_MGMT: + ieee80211_rx_mgt(bcm->ieee, wlhdr, &stats); + break; + case IEEE80211_FTYPE_DATA: + if (is_packet_for_us) { + err = ieee80211_rx(bcm->ieee, skb, &stats); + err = (err == 0) ? -EINVAL : 0; + } + break; + case IEEE80211_FTYPE_CTL: + break; + default: + assert(0); + return -EINVAL; + } + + return err; +} diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_xmit.h b/drivers/net/wireless/bcm43xx/bcm43xx_xmit.h new file mode 100644 index 000000000000..2aed19e35c77 --- /dev/null +++ b/drivers/net/wireless/bcm43xx/bcm43xx_xmit.h @@ -0,0 +1,156 @@ +#ifndef BCM43xx_XMIT_H_ +#define BCM43xx_XMIT_H_ + +#include "bcm43xx_main.h" + + +#define _bcm43xx_declare_plcp_hdr(size) \ + struct bcm43xx_plcp_hdr##size { \ + union { \ + __le32 data; \ + __u8 raw[size]; \ + } __attribute__((__packed__)); \ + } __attribute__((__packed__)) + +/* struct bcm43xx_plcp_hdr4 */ +_bcm43xx_declare_plcp_hdr(4); +/* struct bcm43xx_plcp_hdr6 */ +_bcm43xx_declare_plcp_hdr(6); + +#undef _bcm43xx_declare_plcp_hdr + +/* Device specific TX header. To be prepended to TX frames. */ +struct bcm43xx_txhdr { + union { + struct { + __le16 flags; + __le16 wsec_rate; + __le16 frame_control; + u16 unknown_zeroed_0; + __le16 control; + u8 wep_iv[10]; + u8 unknown_wsec_tkip_data[3]; //FIXME + PAD_BYTES(3); + u8 mac1[6]; + u16 unknown_zeroed_1; + struct bcm43xx_plcp_hdr4 rts_cts_fallback_plcp; + __le16 rts_cts_dur_fallback; + struct bcm43xx_plcp_hdr4 fallback_plcp; + __le16 fallback_dur_id; + PAD_BYTES(2); + __le16 cookie; + __le16 unknown_scb_stuff; //FIXME + struct bcm43xx_plcp_hdr6 rts_cts_plcp; + __le16 rts_cts_frame_control; + __le16 rts_cts_dur; + u8 rts_cts_mac1[6]; + u8 rts_cts_mac2[6]; + PAD_BYTES(2); + struct bcm43xx_plcp_hdr6 plcp; + } __attribute__((__packed__)); + u8 raw[82]; + } __attribute__((__packed__)); +} __attribute__((__packed__)); + +/* Values/Masks for the device TX header */ +#define BCM43xx_TXHDRFLAG_EXPECTACK 0x0001 +#define BCM43xx_TXHDRFLAG_RTSCTS 0x0002 +#define BCM43xx_TXHDRFLAG_RTS 0x0004 +#define BCM43xx_TXHDRFLAG_FIRSTFRAGMENT 0x0008 +#define BCM43xx_TXHDRFLAG_DESTPSMODE 0x0020 +#define BCM43xx_TXHDRFLAG_RTSCTS_OFDM 0x0080 +#define BCM43xx_TXHDRFLAG_FALLBACKOFDM 0x0100 +#define BCM43xx_TXHDRFLAG_RTSCTSFALLBACK_OFDM 0x0200 +#define BCM43xx_TXHDRFLAG_CTS 0x0400 +#define BCM43xx_TXHDRFLAG_FRAMEBURST 0x0800 + +#define BCM43xx_TXHDRCTL_OFDM 0x0001 +#define BCM43xx_TXHDRCTL_SHORT_PREAMBLE 0x0010 +#define BCM43xx_TXHDRCTL_ANTENNADIV_MASK 0x0030 +#define BCM43xx_TXHDRCTL_ANTENNADIV_SHIFT 8 + +#define BCM43xx_TXHDR_RATE_MASK 0x0F00 +#define BCM43xx_TXHDR_RATE_SHIFT 8 +#define BCM43xx_TXHDR_RTSRATE_MASK 0xF000 +#define BCM43xx_TXHDR_RTSRATE_SHIFT 12 +#define BCM43xx_TXHDR_WSEC_KEYINDEX_MASK 0x00F0 +#define BCM43xx_TXHDR_WSEC_KEYINDEX_SHIFT 4 +#define BCM43xx_TXHDR_WSEC_ALGO_MASK 0x0003 +#define BCM43xx_TXHDR_WSEC_ALGO_SHIFT 0 + +void bcm43xx_generate_txhdr(struct bcm43xx_private *bcm, + struct bcm43xx_txhdr *txhdr, + const unsigned char *fragment_data, + const unsigned int fragment_len, + const int is_first_fragment, + const u16 cookie); + +/* RX header as received from the hardware. */ +struct bcm43xx_rxhdr { + /* Frame Length. Must be generated explicitely in PIO mode. */ + __le16 frame_length; + PAD_BYTES(2); + /* Flags field 1 */ + __le16 flags1; + u8 rssi; + u8 signal_quality; + PAD_BYTES(2); + /* Flags field 3 */ + __le16 flags3; + /* Flags field 2 */ + __le16 flags2; + /* Lower 16bits of the TSF at the time the frame started. */ + __le16 mactime; + PAD_BYTES(14); +} __attribute__((__packed__)); + +#define BCM43xx_RXHDR_FLAGS1_OFDM (1 << 0) +/*#define BCM43xx_RXHDR_FLAGS1_SIGNAL??? (1 << 3) FIXME */ +#define BCM43xx_RXHDR_FLAGS1_SHORTPREAMBLE (1 << 7) +#define BCM43xx_RXHDR_FLAGS1_2053RSSIADJ (1 << 14) + +#define BCM43xx_RXHDR_FLAGS2_INVALIDFRAME (1 << 0) +#define BCM43xx_RXHDR_FLAGS2_TYPE2FRAME (1 << 2) +/*FIXME: WEP related flags */ + +#define BCM43xx_RXHDR_FLAGS3_2050RSSIADJ (1 << 10) + +/* Transmit Status as received from the hardware. */ +struct bcm43xx_hwxmitstatus { + PAD_BYTES(4); + __le16 cookie; + u8 flags; + u8 cnt1:4, + cnt2:4; + PAD_BYTES(2); + __le16 seq; + __le16 unknown; //FIXME +} __attribute__((__packed__)); + +/* Transmit Status in CPU byteorder. */ +struct bcm43xx_xmitstatus { + u16 cookie; + u8 flags; + u8 cnt1:4, + cnt2:4; + u16 seq; + u16 unknown; //FIXME +}; + +#define BCM43xx_TXSTAT_FLAG_ACK 0x01 +//TODO #define BCM43xx_TXSTAT_FLAG_??? 0x02 +//TODO #define BCM43xx_TXSTAT_FLAG_??? 0x04 +//TODO #define BCM43xx_TXSTAT_FLAG_??? 0x08 +//TODO #define BCM43xx_TXSTAT_FLAG_??? 0x10 +#define BCM43xx_TXSTAT_FLAG_IGNORE 0x20 +//TODO #define BCM43xx_TXSTAT_FLAG_??? 0x40 +//TODO #define BCM43xx_TXSTAT_FLAG_??? 0x80 + +u8 bcm43xx_plcp_get_ratecode_cck(const u8 bitrate); +u8 bcm43xx_plcp_get_ratecode_ofdm(const u8 bitrate); + +int bcm43xx_rx(struct bcm43xx_private *bcm, + struct sk_buff *skb, + struct bcm43xx_rxhdr *rxhdr); + +#endif /* BCM43xx_XMIT_H_ */ diff --git a/drivers/net/wireless/hostap/hostap_80211.h b/drivers/net/wireless/hostap/hostap_80211.h index 1fc72fe511e9..cc1ee7f4f5f8 100644 --- a/drivers/net/wireless/hostap/hostap_80211.h +++ b/drivers/net/wireless/hostap/hostap_80211.h @@ -92,8 +92,6 @@ void hostap_dump_rx_80211(const char *name, struct sk_buff *skb, void hostap_dump_tx_80211(const char *name, struct sk_buff *skb); int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev); int hostap_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev); -struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb, - struct ieee80211_crypt_data *crypt); int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev); #endif /* HOSTAP_80211_H */ diff --git a/drivers/net/wireless/hostap/hostap_80211_tx.c b/drivers/net/wireless/hostap/hostap_80211_tx.c index 4a85e63906f1..06a5214145e3 100644 --- a/drivers/net/wireless/hostap/hostap_80211_tx.c +++ b/drivers/net/wireless/hostap/hostap_80211_tx.c @@ -299,8 +299,8 @@ int hostap_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev) /* Called only from software IRQ */ -struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb, - struct ieee80211_crypt_data *crypt) +static struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb, + struct ieee80211_crypt_data *crypt) { struct hostap_interface *iface; local_info_t *local; @@ -317,7 +317,7 @@ struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb, } if (local->tkip_countermeasures && - crypt && crypt->ops && strcmp(crypt->ops->name, "TKIP") == 0) { + strcmp(crypt->ops->name, "TKIP") == 0) { hdr = (struct ieee80211_hdr_4addr *) skb->data; if (net_ratelimit()) { printk(KERN_DEBUG "%s: TKIP countermeasures: dropped " @@ -469,7 +469,7 @@ int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev) } if (local->ieee_802_1x && meta->ethertype == ETH_P_PAE && tx.crypt && - !(fc & IEEE80211_FCTL_VERS)) { + !(fc & IEEE80211_FCTL_PROTECTED)) { no_encrypt = 1; PDEBUG(DEBUG_EXTRA2, "%s: TX: IEEE 802.1X - passing " "unencrypted EAPOL frame\n", dev->name); @@ -535,5 +535,4 @@ int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev) EXPORT_SYMBOL(hostap_dump_tx_80211); -EXPORT_SYMBOL(hostap_tx_encrypt); EXPORT_SYMBOL(hostap_master_start_xmit); diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c index d335b250923a..55bed923fbe9 100644 --- a/drivers/net/wireless/hostap/hostap_cs.c +++ b/drivers/net/wireless/hostap/hostap_cs.c @@ -42,7 +42,7 @@ MODULE_PARM_DESC(ignore_cis_vcc, "Ignore broken CIS VCC entry"); /* struct local_info::hw_priv */ struct hostap_cs_priv { dev_node_t node; - dev_link_t *link; + struct pcmcia_device *link; int sandisk_connectplus; }; @@ -204,15 +204,13 @@ static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len) static void prism2_detach(struct pcmcia_device *p_dev); static void prism2_release(u_long arg); -static int prism2_config(dev_link_t *link); +static int prism2_config(struct pcmcia_device *link); static int prism2_pccard_card_present(local_info_t *local) { struct hostap_cs_priv *hw_priv = local->hw_priv; - if (hw_priv != NULL && hw_priv->link != NULL && - ((hw_priv->link->state & (DEV_PRESENT | DEV_CONFIG)) == - (DEV_PRESENT | DEV_CONFIG))) + if (hw_priv != NULL && hw_priv->link != NULL && pcmcia_dev_present(hw_priv->link)) return 1; return 0; } @@ -237,7 +235,7 @@ static void sandisk_set_iobase(local_info_t *local) reg.Action = CS_WRITE; reg.Offset = 0x10; /* 0x3f0 IO base 1 */ reg.Value = hw_priv->link->io.BasePort1 & 0x00ff; - res = pcmcia_access_configuration_register(hw_priv->link->handle, + res = pcmcia_access_configuration_register(hw_priv->link, ®); if (res != CS_SUCCESS) { printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 0 -" @@ -249,7 +247,7 @@ static void sandisk_set_iobase(local_info_t *local) reg.Action = CS_WRITE; reg.Offset = 0x12; /* 0x3f2 IO base 2 */ reg.Value = (hw_priv->link->io.BasePort1 & 0xff00) >> 8; - res = pcmcia_access_configuration_register(hw_priv->link->handle, + res = pcmcia_access_configuration_register(hw_priv->link, ®); if (res != CS_SUCCESS) { printk(KERN_DEBUG "Prism3 SanDisk - failed to set I/O base 1 -" @@ -301,9 +299,9 @@ static int sandisk_enable_wireless(struct net_device *dev) tuple.TupleData = buf; tuple.TupleDataMax = sizeof(buf); tuple.TupleOffset = 0; - if (pcmcia_get_first_tuple(hw_priv->link->handle, &tuple) || - pcmcia_get_tuple_data(hw_priv->link->handle, &tuple) || - pcmcia_parse_tuple(hw_priv->link->handle, &tuple, parse) || + if (pcmcia_get_first_tuple(hw_priv->link, &tuple) || + pcmcia_get_tuple_data(hw_priv->link, &tuple) || + pcmcia_parse_tuple(hw_priv->link, &tuple, parse) || parse->manfid.manf != 0xd601 || parse->manfid.card != 0x0101) { /* No SanDisk manfid found */ ret = -ENODEV; @@ -311,9 +309,9 @@ static int sandisk_enable_wireless(struct net_device *dev) } tuple.DesiredTuple = CISTPL_LONGLINK_MFC; - if (pcmcia_get_first_tuple(hw_priv->link->handle, &tuple) || - pcmcia_get_tuple_data(hw_priv->link->handle, &tuple) || - pcmcia_parse_tuple(hw_priv->link->handle, &tuple, parse) || + if (pcmcia_get_first_tuple(hw_priv->link, &tuple) || + pcmcia_get_tuple_data(hw_priv->link, &tuple) || + pcmcia_parse_tuple(hw_priv->link, &tuple, parse) || parse->longlink_mfc.nfn < 2) { /* No multi-function links found */ ret = -ENODEV; @@ -328,7 +326,7 @@ static int sandisk_enable_wireless(struct net_device *dev) reg.Action = CS_WRITE; reg.Offset = CISREG_COR; reg.Value = COR_SOFT_RESET; - res = pcmcia_access_configuration_register(hw_priv->link->handle, + res = pcmcia_access_configuration_register(hw_priv->link, ®); if (res != CS_SUCCESS) { printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n", @@ -345,7 +343,7 @@ static int sandisk_enable_wireless(struct net_device *dev) * will be enabled during the first cor_sreset call. */ reg.Value = COR_LEVEL_REQ | 0x8 | COR_ADDR_DECODE | COR_FUNC_ENA; - res = pcmcia_access_configuration_register(hw_priv->link->handle, + res = pcmcia_access_configuration_register(hw_priv->link, ®); if (res != CS_SUCCESS) { printk(KERN_DEBUG "%s: SanDisk - COR sreset failed (%d)\n", @@ -380,7 +378,7 @@ static void prism2_pccard_cor_sreset(local_info_t *local) reg.Action = CS_READ; reg.Offset = CISREG_COR; reg.Value = 0; - res = pcmcia_access_configuration_register(hw_priv->link->handle, + res = pcmcia_access_configuration_register(hw_priv->link, ®); if (res != CS_SUCCESS) { printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 1 (%d)\n", @@ -392,7 +390,7 @@ static void prism2_pccard_cor_sreset(local_info_t *local) reg.Action = CS_WRITE; reg.Value |= COR_SOFT_RESET; - res = pcmcia_access_configuration_register(hw_priv->link->handle, + res = pcmcia_access_configuration_register(hw_priv->link, ®); if (res != CS_SUCCESS) { printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 2 (%d)\n", @@ -405,7 +403,7 @@ static void prism2_pccard_cor_sreset(local_info_t *local) reg.Value &= ~COR_SOFT_RESET; if (hw_priv->sandisk_connectplus) reg.Value |= COR_IREQ_ENA; - res = pcmcia_access_configuration_register(hw_priv->link->handle, + res = pcmcia_access_configuration_register(hw_priv->link, ®); if (res != CS_SUCCESS) { printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 3 (%d)\n", @@ -439,7 +437,7 @@ static void prism2_pccard_genesis_reset(local_info_t *local, int hcr) reg.Action = CS_READ; reg.Offset = CISREG_COR; reg.Value = 0; - res = pcmcia_access_configuration_register(hw_priv->link->handle, + res = pcmcia_access_configuration_register(hw_priv->link, ®); if (res != CS_SUCCESS) { printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 1 " @@ -452,7 +450,7 @@ static void prism2_pccard_genesis_reset(local_info_t *local, int hcr) reg.Action = CS_WRITE; reg.Value |= COR_SOFT_RESET; - res = pcmcia_access_configuration_register(hw_priv->link->handle, + res = pcmcia_access_configuration_register(hw_priv->link, ®); if (res != CS_SUCCESS) { printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 2 " @@ -466,7 +464,7 @@ static void prism2_pccard_genesis_reset(local_info_t *local, int hcr) reg.Action = CS_WRITE; reg.Value = hcr; reg.Offset = CISREG_CCSR; - res = pcmcia_access_configuration_register(hw_priv->link->handle, + res = pcmcia_access_configuration_register(hw_priv->link, ®); if (res != CS_SUCCESS) { printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 3 " @@ -478,7 +476,7 @@ static void prism2_pccard_genesis_reset(local_info_t *local, int hcr) reg.Action = CS_WRITE; reg.Offset = CISREG_COR; reg.Value = old_cor & ~COR_SOFT_RESET; - res = pcmcia_access_configuration_register(hw_priv->link->handle, + res = pcmcia_access_configuration_register(hw_priv->link, ®); if (res != CS_SUCCESS) { printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 4 " @@ -501,40 +499,27 @@ static struct prism2_helper_functions prism2_pccard_funcs = /* allocate local data and register with CardServices * initialize dev_link structure, but do not configure the card yet */ -static int prism2_attach(struct pcmcia_device *p_dev) +static int hostap_cs_probe(struct pcmcia_device *p_dev) { - dev_link_t *link; - - link = kmalloc(sizeof(dev_link_t), GFP_KERNEL); - if (link == NULL) - return -ENOMEM; - - memset(link, 0, sizeof(dev_link_t)); + int ret; PDEBUG(DEBUG_HW, "%s: setting Vcc=33 (constant)\n", dev_info); - link->conf.Vcc = 33; - link->conf.IntType = INT_MEMORY_AND_IO; + p_dev->conf.IntType = INT_MEMORY_AND_IO; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - if (prism2_config(link)) + ret = prism2_config(p_dev); + if (ret) { PDEBUG(DEBUG_EXTRA, "prism2_config() failed\n"); + } - return 0; + return ret; } -static void prism2_detach(struct pcmcia_device *p_dev) +static void prism2_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); - PDEBUG(DEBUG_FLOW, "prism2_detach\n"); - if (link->state & DEV_CONFIG) { - prism2_release((u_long)link); - } + prism2_release((u_long)link); /* release net devices */ if (link->priv) { @@ -547,7 +532,6 @@ static void prism2_detach(struct pcmcia_device *p_dev) prism2_free_local_data(dev); kfree(hw_priv); } - kfree(link); } @@ -558,7 +542,7 @@ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) do { int ret = (retf); \ if (ret != 0) { \ PDEBUG(DEBUG_EXTRA, "CardServices(" #fn ") returned %d\n", ret); \ - cs_error(link->handle, fn, ret); \ + cs_error(link, fn, ret); \ goto next_entry; \ } \ } while (0) @@ -566,7 +550,7 @@ if (ret != 0) { \ /* run after a CARD_INSERTION event is received to configure the PCMCIA * socket and make the device available to the system */ -static int prism2_config(dev_link_t *link) +static int prism2_config(struct pcmcia_device *link) { struct net_device *dev; struct hostap_interface *iface; @@ -595,27 +579,24 @@ static int prism2_config(dev_link_t *link) tuple.TupleData = buf; tuple.TupleDataMax = sizeof(buf); tuple.TupleOffset = 0; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link->handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link->handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(link->handle, &tuple, parse)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, parse)); link->conf.ConfigBase = parse->config.base; link->conf.Present = parse->config.rmask[0]; CS_CHECK(GetConfigurationInfo, - pcmcia_get_configuration_info(link->handle, &conf)); - PDEBUG(DEBUG_HW, "%s: %s Vcc=%d (from config)\n", dev_info, - ignore_cis_vcc ? "ignoring" : "setting", conf.Vcc); - link->conf.Vcc = conf.Vcc; + pcmcia_get_configuration_info(link, &conf)); /* Look for an appropriate configuration table entry in the CIS */ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link->handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); for (;;) { cistpl_cftable_entry_t *cfg = &(parse->cftable_entry); CFG_CHECK2(GetTupleData, - pcmcia_get_tuple_data(link->handle, &tuple)); + pcmcia_get_tuple_data(link, &tuple)); CFG_CHECK2(ParseTuple, - pcmcia_parse_tuple(link->handle, &tuple, parse)); + pcmcia_parse_tuple(link, &tuple, parse)); if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg; @@ -650,10 +631,10 @@ static int prism2_config(dev_link_t *link) } if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM)) - link->conf.Vpp1 = link->conf.Vpp2 = + link->conf.Vpp = cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000; else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM)) - link->conf.Vpp1 = link->conf.Vpp2 = + link->conf.Vpp = dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000; /* Do we need to allocate an interrupt? */ @@ -695,19 +676,19 @@ static int prism2_config(dev_link_t *link) /* This reserves IO space but doesn't actually enable it */ CFG_CHECK2(RequestIO, - pcmcia_request_io(link->handle, &link->io)); + pcmcia_request_io(link, &link->io)); /* This configuration table entry is OK */ break; next_entry: CS_CHECK(GetNextTuple, - pcmcia_get_next_tuple(link->handle, &tuple)); + pcmcia_get_next_tuple(link, &tuple)); } /* Need to allocate net_device before requesting IRQ handler */ dev = prism2_init_local_data(&prism2_pccard_funcs, 0, - &handle_to_dev(link->handle)); + &handle_to_dev(link)); if (dev == NULL) goto failed; link->priv = dev; @@ -717,7 +698,7 @@ static int prism2_config(dev_link_t *link) local->hw_priv = hw_priv; hw_priv->link = link; strcpy(hw_priv->node.dev_name, dev->name); - link->dev = &hw_priv->node; + link->dev_node = &hw_priv->node; /* * Allocate an interrupt line. Note that this does not assign a @@ -730,7 +711,7 @@ static int prism2_config(dev_link_t *link) link->irq.Handler = prism2_interrupt; link->irq.Instance = dev; CS_CHECK(RequestIRQ, - pcmcia_request_irq(link->handle, &link->irq)); + pcmcia_request_irq(link, &link->irq)); } /* @@ -739,18 +720,17 @@ static int prism2_config(dev_link_t *link) * card and host interface into "Memory and IO" mode. */ CS_CHECK(RequestConfiguration, - pcmcia_request_configuration(link->handle, &link->conf)); + pcmcia_request_configuration(link, &link->conf)); dev->irq = link->irq.AssignedIRQ; dev->base_addr = link->io.BasePort1; /* Finally, report what we've done */ - printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d", - dev_info, link->conf.ConfigIndex, - link->conf.Vcc / 10, link->conf.Vcc % 10); - if (link->conf.Vpp1) - printk(", Vpp %d.%d", link->conf.Vpp1 / 10, - link->conf.Vpp1 % 10); + printk(KERN_INFO "%s: index 0x%02x: ", + dev_info, link->conf.ConfigIndex); + if (link->conf.Vpp) + printk(", Vpp %d.%d", link->conf.Vpp / 10, + link->conf.Vpp % 10); if (link->conf.Attributes & CONF_ENABLE_IRQ) printk(", irq %d", link->irq.AssignedIRQ); if (link->io.NumPorts1) @@ -761,9 +741,6 @@ static int prism2_config(dev_link_t *link) link->io.BasePort2+link->io.NumPorts2-1); printk("\n"); - link->state |= DEV_CONFIG; - link->state &= ~DEV_CONFIG_PENDING; - local->shutdown = 0; sandisk_enable_wireless(dev); @@ -778,7 +755,7 @@ static int prism2_config(dev_link_t *link) return ret; cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); failed: kfree(parse); @@ -790,7 +767,7 @@ static int prism2_config(dev_link_t *link) static void prism2_release(u_long arg) { - dev_link_t *link = (dev_link_t *)arg; + struct pcmcia_device *link = (struct pcmcia_device *)arg; PDEBUG(DEBUG_FLOW, "prism2_release\n"); @@ -799,71 +776,54 @@ static void prism2_release(u_long arg) struct hostap_interface *iface; iface = netdev_priv(dev); - if (link->state & DEV_CONFIG) - prism2_hw_shutdown(dev, 0); + prism2_hw_shutdown(dev, 0); iface->local->shutdown = 1; } - if (link->win) - pcmcia_release_window(link->win); - pcmcia_release_configuration(link->handle); - if (link->io.NumPorts1) - pcmcia_release_io(link->handle, &link->io); - if (link->irq.AssignedIRQ) - pcmcia_release_irq(link->handle, &link->irq); - - link->state &= ~DEV_CONFIG; - + pcmcia_disable_device(link); PDEBUG(DEBUG_FLOW, "release - done\n"); } -static int hostap_cs_suspend(struct pcmcia_device *p_dev) +static int hostap_cs_suspend(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = (struct net_device *) link->priv; int dev_open = 0; + struct hostap_interface *iface = NULL; + + if (dev) + iface = netdev_priv(dev); PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_SUSPEND\n", dev_info); - - link->state |= DEV_SUSPEND; - - if (link->state & DEV_CONFIG) { - struct hostap_interface *iface = netdev_priv(dev); - if (iface && iface->local) - dev_open = iface->local->num_dev_open > 0; - if (dev_open) { - netif_stop_queue(dev); - netif_device_detach(dev); - } - prism2_suspend(dev); - pcmcia_release_configuration(link->handle); + if (iface && iface->local) + dev_open = iface->local->num_dev_open > 0; + if (dev_open) { + netif_stop_queue(dev); + netif_device_detach(dev); } + prism2_suspend(dev); return 0; } -static int hostap_cs_resume(struct pcmcia_device *p_dev) +static int hostap_cs_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = (struct net_device *) link->priv; int dev_open = 0; + struct hostap_interface *iface = NULL; + + if (dev) + iface = netdev_priv(dev); PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_RESUME\n", dev_info); - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - struct hostap_interface *iface = netdev_priv(dev); - if (iface && iface->local) - dev_open = iface->local->num_dev_open > 0; + if (iface && iface->local) + dev_open = iface->local->num_dev_open > 0; - pcmcia_request_configuration(link->handle, &link->conf); - - prism2_hw_shutdown(dev, 1); - prism2_hw_config(dev, dev_open ? 0 : 1); - if (dev_open) { - netif_device_attach(dev); - netif_start_queue(dev); - } + prism2_hw_shutdown(dev, 1); + prism2_hw_config(dev, dev_open ? 0 : 1); + if (dev_open) { + netif_device_attach(dev); + netif_start_queue(dev); } return 0; @@ -930,7 +890,7 @@ static struct pcmcia_driver hostap_driver = { .drv = { .name = "hostap_cs", }, - .probe = prism2_attach, + .probe = hostap_cs_probe, .remove = prism2_detach, .owner = THIS_MODULE, .id_table = hostap_cs_ids, diff --git a/drivers/net/wireless/ipw2200.c b/drivers/net/wireless/ipw2200.c index 9dce522526c5..bca89cff85a6 100644 --- a/drivers/net/wireless/ipw2200.c +++ b/drivers/net/wireless/ipw2200.c @@ -5573,8 +5573,7 @@ static void ipw_adhoc_create(struct ipw_priv *priv, case IEEE80211_52GHZ_BAND: network->mode = IEEE_A; i = ieee80211_channel_to_index(priv->ieee, priv->channel); - if (i == -1) - BUG(); + BUG_ON(i == -1); if (geo->a[i].flags & IEEE80211_CH_PASSIVE_ONLY) { IPW_WARNING("Overriding invalid channel\n"); priv->channel = geo->a[0].channel; @@ -5587,8 +5586,7 @@ static void ipw_adhoc_create(struct ipw_priv *priv, else network->mode = IEEE_B; i = ieee80211_channel_to_index(priv->ieee, priv->channel); - if (i == -1) - BUG(); + BUG_ON(i == -1); if (geo->bg[i].flags & IEEE80211_CH_PASSIVE_ONLY) { IPW_WARNING("Overriding invalid channel\n"); priv->channel = geo->bg[0].channel; @@ -6715,8 +6713,7 @@ static int ipw_qos_association(struct ipw_priv *priv, switch (priv->ieee->iw_mode) { case IW_MODE_ADHOC: - if (!(network->capability & WLAN_CAPABILITY_IBSS)) - BUG(); + BUG_ON(!(network->capability & WLAN_CAPABILITY_IBSS)); qos_data = &ibss_data; break; diff --git a/drivers/net/wireless/netwave_cs.c b/drivers/net/wireless/netwave_cs.c index 75ce6ddb0cf5..9343d970537b 100644 --- a/drivers/net/wireless/netwave_cs.c +++ b/drivers/net/wireless/netwave_cs.c @@ -190,8 +190,8 @@ module_param(mem_speed, int, 0); /*====================================================================*/ /* PCMCIA (Card Services) related functions */ -static void netwave_release(dev_link_t *link); /* Card removal */ -static void netwave_pcmcia_config(dev_link_t *arg); /* Runs after card +static void netwave_release(struct pcmcia_device *link); /* Card removal */ +static int netwave_pcmcia_config(struct pcmcia_device *arg); /* Runs after card insertion */ static void netwave_detach(struct pcmcia_device *p_dev); /* Destroy instance */ @@ -221,10 +221,10 @@ static struct iw_statistics* netwave_get_wireless_stats(struct net_device *dev); static void set_multicast_list(struct net_device *dev); /* - A dev_link_t structure has fields for most things that are needed + A struct pcmcia_device structure has fields for most things that are needed to keep track of a socket, but there will usually be some device specific information that also needs to be kept track of. The - 'priv' pointer in a dev_link_t structure can be used to point to + 'priv' pointer in a struct pcmcia_device structure can be used to point to a device-specific private data structure, like this. A driver needs to provide a dev_node_t structure for each device @@ -232,7 +232,7 @@ static void set_multicast_list(struct net_device *dev); example, ethernet cards, modems). In other cases, there may be many actual or logical devices (SCSI adapters, memory cards with multiple partitions). The dev_node_t structures need to be kept - in a linked list starting at the 'dev' field of a dev_link_t + in a linked list starting at the 'dev' field of a struct pcmcia_device structure. We allocate them in the card's private data structure, because they generally can't be allocated dynamically. */ @@ -268,7 +268,7 @@ struct site_survey { }; typedef struct netwave_private { - dev_link_t link; + struct pcmcia_device *p_dev; spinlock_t spinlock; /* Serialize access to the hardware (SMP) */ dev_node_t node; u_char __iomem *ramBase; @@ -376,20 +376,19 @@ static struct iw_statistics *netwave_get_wireless_stats(struct net_device *dev) * configure the card at this point -- we wait until we receive a * card insertion event. */ -static int netwave_attach(struct pcmcia_device *p_dev) +static int netwave_probe(struct pcmcia_device *link) { - dev_link_t *link; struct net_device *dev; netwave_private *priv; DEBUG(0, "netwave_attach()\n"); - /* Initialize the dev_link_t structure */ + /* Initialize the struct pcmcia_device structure */ dev = alloc_etherdev(sizeof(netwave_private)); if (!dev) return -ENOMEM; priv = netdev_priv(dev); - link = &priv->link; + priv->p_dev = link; link->priv = dev; /* The io structure describes IO port mapping */ @@ -406,7 +405,6 @@ static int netwave_attach(struct pcmcia_device *p_dev) /* General socket configuration */ link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; link->conf.ConfigIndex = 1; link->conf.Present = PRESENT_OPTION; @@ -430,13 +428,7 @@ static int netwave_attach(struct pcmcia_device *p_dev) dev->stop = &netwave_close; link->irq.Instance = dev; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - netwave_pcmcia_config( link); - - return 0; + return netwave_pcmcia_config( link); } /* netwave_attach */ /* @@ -447,17 +439,15 @@ static int netwave_attach(struct pcmcia_device *p_dev) * structures are freed. Otherwise, the structures will be freed * when the device is released. */ -static void netwave_detach(struct pcmcia_device *p_dev) +static void netwave_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; DEBUG(0, "netwave_detach(0x%p)\n", link); - if (link->state & DEV_CONFIG) - netwave_release(link); + netwave_release(link); - if (link->dev) + if (link->dev_node) unregister_netdev(dev); free_netdev(dev); @@ -743,8 +733,7 @@ static const struct iw_handler_def netwave_handler_def = #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) -static void netwave_pcmcia_config(dev_link_t *link) { - client_handle_t handle = link->handle; +static int netwave_pcmcia_config(struct pcmcia_device *link) { struct net_device *dev = link->priv; netwave_private *priv = netdev_priv(dev); tuple_t tuple; @@ -766,15 +755,12 @@ static void netwave_pcmcia_config(dev_link_t *link) { tuple.TupleDataMax = 64; tuple.TupleOffset = 0; tuple.DesiredTuple = CISTPL_CONFIG; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; - /* Configure card */ - link->state |= DEV_CONFIG; - /* * Try allocating IO ports. This tries a few fixed addresses. * If you want, you can also read the card's config table to @@ -782,11 +768,11 @@ static void netwave_pcmcia_config(dev_link_t *link) { */ for (i = j = 0x0; j < 0x400; j += 0x20) { link->io.BasePort1 = j ^ 0x300; - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) break; } if (i != CS_SUCCESS) { - cs_error(link->handle, RequestIO, i); + cs_error(link, RequestIO, i); goto failed; } @@ -794,16 +780,16 @@ static void netwave_pcmcia_config(dev_link_t *link) { * Now allocate an interrupt line. Note that this does not * actually assign a handler to the interrupt. */ - CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); /* * This actually configures the PCMCIA socket -- setting up * the I/O windows and the interrupt mapping. */ - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); /* - * Allocate a 32K memory window. Note that the dev_link_t + * Allocate a 32K memory window. Note that the struct pcmcia_device * structure provides space for one window handle -- if your * device needs several windows, you'll need to keep track of * the handles in your private data structure, dev->priv. @@ -813,7 +799,7 @@ static void netwave_pcmcia_config(dev_link_t *link) { req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_CM|WIN_ENABLE; req.Base = 0; req.Size = 0x8000; req.AccessSpeed = mem_speed; - CS_CHECK(RequestWindow, pcmcia_request_window(&link->handle, &req, &link->win)); + CS_CHECK(RequestWindow, pcmcia_request_window(&link, &req, &link->win)); mem.CardOffset = 0x20000; mem.Page = 0; CS_CHECK(MapMemPage, pcmcia_map_mem_page(link->win, &mem)); @@ -823,7 +809,7 @@ static void netwave_pcmcia_config(dev_link_t *link) { dev->irq = link->irq.AssignedIRQ; dev->base_addr = link->io.BasePort1; - SET_NETDEV_DEV(dev, &handle_to_dev(handle)); + SET_NETDEV_DEV(dev, &handle_to_dev(link)); if (register_netdev(dev) != 0) { printk(KERN_DEBUG "netwave_cs: register_netdev() failed\n"); @@ -831,8 +817,7 @@ static void netwave_pcmcia_config(dev_link_t *link) { } strcpy(priv->node.dev_name, dev->name); - link->dev = &priv->node; - link->state &= ~DEV_CONFIG_PENDING; + link->dev_node = &priv->node; /* Reset card before reading physical address */ netwave_doreset(dev->base_addr, ramBase); @@ -852,12 +837,13 @@ static void netwave_pcmcia_config(dev_link_t *link) { printk(KERN_DEBUG "Netwave_reset: revision %04x %04x\n", get_uint16(ramBase + NETWAVE_EREG_ARW), get_uint16(ramBase + NETWAVE_EREG_ARW+2)); - return; + return 0; cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); failed: netwave_release(link); + return -ENODEV; } /* netwave_pcmcia_config */ /* @@ -867,52 +853,35 @@ failed: * device, and release the PCMCIA configuration. If the device is * still open, this will be postponed until it is closed. */ -static void netwave_release(dev_link_t *link) +static void netwave_release(struct pcmcia_device *link) { - struct net_device *dev = link->priv; - netwave_private *priv = netdev_priv(dev); + struct net_device *dev = link->priv; + netwave_private *priv = netdev_priv(dev); - DEBUG(0, "netwave_release(0x%p)\n", link); + DEBUG(0, "netwave_release(0x%p)\n", link); - /* Don't bother checking to see if these succeed or not */ - if (link->win) { - iounmap(priv->ramBase); - pcmcia_release_window(link->win); - } - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - - link->state &= ~DEV_CONFIG; + pcmcia_disable_device(link); + if (link->win) + iounmap(priv->ramBase); } -static int netwave_suspend(struct pcmcia_device *p_dev) +static int netwave_suspend(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - if (link->open) - netif_device_detach(dev); - pcmcia_release_configuration(link->handle); - } + if (link->open) + netif_device_detach(dev); return 0; } -static int netwave_resume(struct pcmcia_device *p_dev) +static int netwave_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - pcmcia_request_configuration(link->handle, &link->conf); - if (link->open) { - netwave_reset(dev); - netif_device_attach(dev); - } + if (link->open) { + netwave_reset(dev); + netif_device_attach(dev); } return 0; @@ -1119,7 +1088,7 @@ static irqreturn_t netwave_interrupt(int irq, void* dev_id, struct pt_regs *regs u_char __iomem *ramBase; struct net_device *dev = (struct net_device *)dev_id; struct netwave_private *priv = netdev_priv(dev); - dev_link_t *link = &priv->link; + struct pcmcia_device *link = priv->p_dev; int i; if (!netif_device_present(dev)) @@ -1138,7 +1107,7 @@ static irqreturn_t netwave_interrupt(int irq, void* dev_id, struct pt_regs *regs status = inb(iobase + NETWAVE_REG_ASR); - if (!DEV_OK(link)) { + if (!pcmcia_dev_present(link)) { DEBUG(1, "netwave_interrupt: Interrupt with status 0x%x " "from removed or suspended card!\n", status); break; @@ -1373,11 +1342,11 @@ static int netwave_rx(struct net_device *dev) static int netwave_open(struct net_device *dev) { netwave_private *priv = netdev_priv(dev); - dev_link_t *link = &priv->link; + struct pcmcia_device *link = priv->p_dev; DEBUG(1, "netwave_open: starting.\n"); - if (!DEV_OK(link)) + if (!pcmcia_dev_present(link)) return -ENODEV; link->open++; @@ -1390,7 +1359,7 @@ static int netwave_open(struct net_device *dev) { static int netwave_close(struct net_device *dev) { netwave_private *priv = netdev_priv(dev); - dev_link_t *link = &priv->link; + struct pcmcia_device *link = priv->p_dev; DEBUG(1, "netwave_close: finishing.\n"); @@ -1411,7 +1380,7 @@ static struct pcmcia_driver netwave_driver = { .drv = { .name = "netwave_cs", }, - .probe = netwave_attach, + .probe = netwave_probe, .remove = netwave_detach, .id_table = netwave_ids, .suspend = netwave_suspend, diff --git a/drivers/net/wireless/orinoco_cs.c b/drivers/net/wireless/orinoco_cs.c index ec6f2a48895b..434f7d7ad841 100644 --- a/drivers/net/wireless/orinoco_cs.c +++ b/drivers/net/wireless/orinoco_cs.c @@ -49,7 +49,7 @@ MODULE_PARM_DESC(ignore_cis_vcc, "Allow voltage mismatch between card and socket /* PCMCIA specific device information (goes in the card field of * struct orinoco_private */ struct orinoco_pccard { - dev_link_t link; + struct pcmcia_device *p_dev; dev_node_t node; /* Used to handle hard reset */ @@ -63,8 +63,8 @@ struct orinoco_pccard { /* Function prototypes */ /********************************************************************/ -static void orinoco_cs_config(dev_link_t *link); -static void orinoco_cs_release(dev_link_t *link); +static int orinoco_cs_config(struct pcmcia_device *link); +static void orinoco_cs_release(struct pcmcia_device *link); static void orinoco_cs_detach(struct pcmcia_device *p_dev); /********************************************************************/ @@ -75,13 +75,13 @@ static int orinoco_cs_hard_reset(struct orinoco_private *priv) { struct orinoco_pccard *card = priv->card; - dev_link_t *link = &card->link; + struct pcmcia_device *link = card->p_dev; int err; /* We need atomic ops here, because we're not holding the lock */ set_bit(0, &card->hard_reset_in_progress); - err = pcmcia_reset_card(link->handle, NULL); + err = pcmcia_reset_card(link, NULL); if (err) return err; @@ -104,12 +104,11 @@ orinoco_cs_hard_reset(struct orinoco_private *priv) * configure the card at this point -- we wait until we receive a card * insertion event. */ static int -orinoco_cs_attach(struct pcmcia_device *p_dev) +orinoco_cs_probe(struct pcmcia_device *link) { struct net_device *dev; struct orinoco_private *priv; struct orinoco_pccard *card; - dev_link_t *link; dev = alloc_orinocodev(sizeof(*card), orinoco_cs_hard_reset); if (! dev) @@ -118,7 +117,7 @@ orinoco_cs_attach(struct pcmcia_device *p_dev) card = priv->card; /* Link both structures together */ - link = &card->link; + card->p_dev = link; link->priv = dev; /* Interrupt setup */ @@ -135,16 +134,7 @@ orinoco_cs_attach(struct pcmcia_device *p_dev) link->conf.Attributes = 0; link->conf.IntType = INT_MEMORY_AND_IO; - /* Register with Card Services */ - link->next = NULL; - - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - orinoco_cs_config(link); - - return 0; + return orinoco_cs_config(link); } /* orinoco_cs_attach */ /* @@ -153,16 +143,14 @@ orinoco_cs_attach(struct pcmcia_device *p_dev) * are freed. Otherwise, the structures will be freed when the device * is released. */ -static void orinoco_cs_detach(struct pcmcia_device *p_dev) +static void orinoco_cs_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; - if (link->state & DEV_CONFIG) - orinoco_cs_release(link); + orinoco_cs_release(link); - DEBUG(0, PFX "detach: link=%p link->dev=%p\n", link, link->dev); - if (link->dev) { + DEBUG(0, PFX "detach: link=%p link->dev_node=%p\n", link, link->dev_node); + if (link->dev_node) { DEBUG(0, PFX "About to unregister net device %p\n", dev); unregister_netdev(dev); @@ -180,11 +168,10 @@ static void orinoco_cs_detach(struct pcmcia_device *p_dev) last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; \ } while (0) -static void -orinoco_cs_config(dev_link_t *link) +static int +orinoco_cs_config(struct pcmcia_device *link) { struct net_device *dev = link->priv; - client_handle_t handle = link->handle; struct orinoco_private *priv = netdev_priv(dev); struct orinoco_pccard *card = priv->card; hermes_t *hw = &priv->hw; @@ -196,7 +183,7 @@ orinoco_cs_config(dev_link_t *link) cisparse_t parse; void __iomem *mem; - CS_CHECK(ValidateCIS, pcmcia_validate_cis(handle, &info)); + CS_CHECK(ValidateCIS, pcmcia_validate_cis(link, &info)); /* * This reads the card's CONFIG tuple to find its @@ -207,19 +194,15 @@ orinoco_cs_config(dev_link_t *link) tuple.TupleData = buf; tuple.TupleDataMax = sizeof(buf); tuple.TupleOffset = 0; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; - /* Configure card */ - link->state |= DEV_CONFIG; - /* Look up the current Vcc */ CS_CHECK(GetConfigurationInfo, - pcmcia_get_configuration_info(handle, &conf)); - link->conf.Vcc = conf.Vcc; + pcmcia_get_configuration_info(link, &conf)); /* * In this loop, we scan the CIS for configuration table @@ -236,13 +219,13 @@ orinoco_cs_config(dev_link_t *link) * implementation-defined details. */ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); while (1) { cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); cistpl_cftable_entry_t dflt = { .index = 0 }; - if ( (pcmcia_get_tuple_data(handle, &tuple) != 0) - || (pcmcia_parse_tuple(handle, &tuple, &parse) != 0)) + if ( (pcmcia_get_tuple_data(link, &tuple) != 0) + || (pcmcia_parse_tuple(link, &tuple, &parse) != 0)) goto next_entry; if (cfg->flags & CISTPL_CFTABLE_DEFAULT) @@ -274,10 +257,10 @@ orinoco_cs_config(dev_link_t *link) } if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM)) - link->conf.Vpp1 = link->conf.Vpp2 = + link->conf.Vpp = cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000; else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM)) - link->conf.Vpp1 = link->conf.Vpp2 = + link->conf.Vpp = dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000; /* Do we need to allocate an interrupt? */ @@ -307,7 +290,7 @@ orinoco_cs_config(dev_link_t *link) } /* This reserves IO space but doesn't actually enable it */ - if (pcmcia_request_io(link->handle, &link->io) != 0) + if (pcmcia_request_io(link, &link->io) != 0) goto next_entry; } @@ -317,9 +300,8 @@ orinoco_cs_config(dev_link_t *link) break; next_entry: - if (link->io.NumPorts1) - pcmcia_release_io(link->handle, &link->io); - last_ret = pcmcia_get_next_tuple(handle, &tuple); + pcmcia_disable_device(link); + last_ret = pcmcia_get_next_tuple(link, &tuple); if (last_ret == CS_NO_MORE_ITEMS) { printk(KERN_ERR PFX "GetNextTuple(): No matching " "CIS configuration. Maybe you need the " @@ -333,7 +315,7 @@ orinoco_cs_config(dev_link_t *link) * a handler to the interrupt, unless the 'Handler' member of * the irq structure is initialized. */ - CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); /* We initialize the hermes structure before completing PCMCIA * configuration just in case the interrupt handler gets @@ -350,7 +332,7 @@ orinoco_cs_config(dev_link_t *link) * card and host interface into "Memory and IO" mode. */ CS_CHECK(RequestConfiguration, - pcmcia_request_configuration(link->handle, &link->conf)); + pcmcia_request_configuration(link, &link->conf)); /* Ok, we have the configuration, prepare to register the netdev */ dev->base_addr = link->io.BasePort1; @@ -358,7 +340,7 @@ orinoco_cs_config(dev_link_t *link) SET_MODULE_OWNER(dev); card->node.major = card->node.minor = 0; - SET_NETDEV_DEV(dev, &handle_to_dev(handle)); + SET_NETDEV_DEV(dev, &handle_to_dev(link)); /* Tell the stack we exist */ if (register_netdev(dev) != 0) { printk(KERN_ERR PFX "register_netdev() failed\n"); @@ -366,20 +348,18 @@ orinoco_cs_config(dev_link_t *link) } /* At this point, the dev_node_t structure(s) needs to be - * initialized and arranged in a linked list at link->dev. */ + * initialized and arranged in a linked list at link->dev_node. */ strcpy(card->node.dev_name, dev->name); - link->dev = &card->node; /* link->dev being non-NULL is also + link->dev_node = &card->node; /* link->dev_node being non-NULL is also used to indicate that the net_device has been registered */ - link->state &= ~DEV_CONFIG_PENDING; /* Finally, report what we've done */ - printk(KERN_DEBUG "%s: index 0x%02x: Vcc %d.%d", - dev->name, link->conf.ConfigIndex, - link->conf.Vcc / 10, link->conf.Vcc % 10); - if (link->conf.Vpp1) - printk(", Vpp %d.%d", link->conf.Vpp1 / 10, - link->conf.Vpp1 % 10); + printk(KERN_DEBUG "%s: index 0x%02x: ", + dev->name, link->conf.ConfigIndex); + if (link->conf.Vpp) + printk(", Vpp %d.%d", link->conf.Vpp / 10, + link->conf.Vpp % 10); printk(", irq %d", link->irq.AssignedIRQ); if (link->io.NumPorts1) printk(", io 0x%04x-0x%04x", link->io.BasePort1, @@ -389,13 +369,14 @@ orinoco_cs_config(dev_link_t *link) link->io.BasePort2 + link->io.NumPorts2 - 1); printk("\n"); - return; + return 0; cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); failed: orinoco_cs_release(link); + return -ENODEV; } /* orinoco_cs_config */ /* @@ -404,7 +385,7 @@ orinoco_cs_config(dev_link_t *link) * still open, this will be postponed until it is closed. */ static void -orinoco_cs_release(dev_link_t *link) +orinoco_cs_release(struct pcmcia_device *link) { struct net_device *dev = link->priv; struct orinoco_private *priv = netdev_priv(dev); @@ -416,88 +397,68 @@ orinoco_cs_release(dev_link_t *link) priv->hw_unavailable++; spin_unlock_irqrestore(&priv->lock, flags); - /* Don't bother checking to see if these succeed or not */ - pcmcia_release_configuration(link->handle); - if (link->io.NumPorts1) - pcmcia_release_io(link->handle, &link->io); - if (link->irq.AssignedIRQ) - pcmcia_release_irq(link->handle, &link->irq); - link->state &= ~DEV_CONFIG; + pcmcia_disable_device(link); if (priv->hw.iobase) ioport_unmap(priv->hw.iobase); } /* orinoco_cs_release */ -static int orinoco_cs_suspend(struct pcmcia_device *p_dev) +static int orinoco_cs_suspend(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; struct orinoco_private *priv = netdev_priv(dev); struct orinoco_pccard *card = priv->card; int err = 0; unsigned long flags; - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - /* This is probably racy, but I can't think of - a better way, short of rewriting the PCMCIA - layer to not suck :-( */ - if (! test_bit(0, &card->hard_reset_in_progress)) { - spin_lock_irqsave(&priv->lock, flags); + /* This is probably racy, but I can't think of + a better way, short of rewriting the PCMCIA + layer to not suck :-( */ + if (! test_bit(0, &card->hard_reset_in_progress)) { + spin_lock_irqsave(&priv->lock, flags); - err = __orinoco_down(dev); - if (err) - printk(KERN_WARNING "%s: Error %d downing interface\n", - dev->name, err); + err = __orinoco_down(dev); + if (err) + printk(KERN_WARNING "%s: Error %d downing interface\n", + dev->name, err); - netif_device_detach(dev); - priv->hw_unavailable++; + netif_device_detach(dev); + priv->hw_unavailable++; - spin_unlock_irqrestore(&priv->lock, flags); - } - - pcmcia_release_configuration(link->handle); + spin_unlock_irqrestore(&priv->lock, flags); } return 0; } -static int orinoco_cs_resume(struct pcmcia_device *p_dev) +static int orinoco_cs_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; struct orinoco_private *priv = netdev_priv(dev); struct orinoco_pccard *card = priv->card; int err = 0; unsigned long flags; - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - /* FIXME: should we double check that this is - * the same card as we had before */ - pcmcia_request_configuration(link->handle, &link->conf); - - if (! test_bit(0, &card->hard_reset_in_progress)) { - err = orinoco_reinit_firmware(dev); - if (err) { - printk(KERN_ERR "%s: Error %d re-initializing firmware\n", - dev->name, err); - return -EIO; - } - - spin_lock_irqsave(&priv->lock, flags); - - netif_device_attach(dev); - priv->hw_unavailable--; - - if (priv->open && ! priv->hw_unavailable) { - err = __orinoco_up(dev); - if (err) - printk(KERN_ERR "%s: Error %d restarting card\n", - dev->name, err); - } - - spin_unlock_irqrestore(&priv->lock, flags); + if (! test_bit(0, &card->hard_reset_in_progress)) { + err = orinoco_reinit_firmware(dev); + if (err) { + printk(KERN_ERR "%s: Error %d re-initializing firmware\n", + dev->name, err); + return -EIO; } + + spin_lock_irqsave(&priv->lock, flags); + + netif_device_attach(dev); + priv->hw_unavailable--; + + if (priv->open && ! priv->hw_unavailable) { + err = __orinoco_up(dev); + if (err) + printk(KERN_ERR "%s: Error %d restarting card\n", + dev->name, err); + } + + spin_unlock_irqrestore(&priv->lock, flags); } return 0; @@ -604,7 +565,7 @@ static struct pcmcia_driver orinoco_driver = { .drv = { .name = DRIVER_NAME, }, - .probe = orinoco_cs_attach, + .probe = orinoco_cs_probe, .remove = orinoco_cs_detach, .id_table = orinoco_cs_ids, .suspend = orinoco_cs_suspend, diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c index 7880d8c31aad..879eb427607c 100644 --- a/drivers/net/wireless/ray_cs.c +++ b/drivers/net/wireless/ray_cs.c @@ -90,8 +90,8 @@ module_param(pc_debug, int, 0); #define DEBUG(n, args...) #endif /** Prototypes based on PCMCIA skeleton driver *******************************/ -static void ray_config(dev_link_t *link); -static void ray_release(dev_link_t *link); +static int ray_config(struct pcmcia_device *link); +static void ray_release(struct pcmcia_device *link); static void ray_detach(struct pcmcia_device *p_dev); /***** Prototypes indicated by device structure ******************************/ @@ -190,20 +190,17 @@ static int bc; static char *phy_addr = NULL; -/* A linked list of "instances" of the ray device. Each actual - PCMCIA card corresponds to one device instance, and is described - by one dev_link_t structure (defined in ds.h). -*/ -static dev_link_t *dev_list = NULL; - -/* A dev_link_t structure has fields for most things that are needed +/* A struct pcmcia_device structure has fields for most things that are needed to keep track of a socket, but there will usually be some device specific information that also needs to be kept track of. The - 'priv' pointer in a dev_link_t structure can be used to point to + 'priv' pointer in a struct pcmcia_device structure can be used to point to a device-specific private data structure, like this. */ static unsigned int ray_mem_speed = 500; +/* WARNING: THIS DRIVER IS NOT CAPABLE OF HANDLING MULTIPLE DEVICES! */ +static struct pcmcia_device *this_device = NULL; + MODULE_AUTHOR("Corey Thomas "); MODULE_DESCRIPTION("Raylink/WebGear wireless LAN driver"); MODULE_LICENSE("GPL"); @@ -306,56 +303,46 @@ static char rcsid[] = "Raylink/WebGear wireless LAN - Corey priv; - - memset(link, 0, sizeof(struct dev_link_t)); + local->finder = p_dev; /* The io structure describes IO port mapping. None used here */ - link->io.NumPorts1 = 0; - link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; - link->io.IOAddrLines = 5; + p_dev->io.NumPorts1 = 0; + p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + p_dev->io.IOAddrLines = 5; /* Interrupt setup. For PCMCIA, driver takes what's given */ - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; - link->irq.IRQInfo1 = IRQ_LEVEL_ID; - link->irq.Handler = &ray_interrupt; + p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; + p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID; + p_dev->irq.Handler = &ray_interrupt; /* General socket configuration */ - link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; - link->conf.IntType = INT_MEMORY_AND_IO; - link->conf.ConfigIndex = 1; - link->conf.Present = PRESENT_OPTION; + p_dev->conf.Attributes = CONF_ENABLE_IRQ; + p_dev->conf.IntType = INT_MEMORY_AND_IO; + p_dev->conf.ConfigIndex = 1; + p_dev->conf.Present = PRESENT_OPTION; - link->priv = dev; - link->irq.Instance = dev; + p_dev->priv = dev; + p_dev->irq.Instance = dev; - local->finder = link; + local->finder = p_dev; local->card_status = CARD_INSERTED; local->authentication_state = UNAUTHENTICATED; local->num_multi = 0; - DEBUG(2,"ray_attach link = %p, dev = %p, local = %p, intr = %p\n", - link,dev,local,&ray_interrupt); + DEBUG(2,"ray_attach p_dev = %p, dev = %p, local = %p, intr = %p\n", + p_dev,dev,local,&ray_interrupt); /* Raylink entries in the device structure */ dev->hard_start_xmit = &ray_dev_start_xmit; @@ -379,16 +366,10 @@ static int ray_attach(struct pcmcia_device *p_dev) init_timer(&local->timer); - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - ray_config(link); - - return 0; + this_device = p_dev; + return ray_config(p_dev); fail_alloc_dev: - kfree(link); return -ENOMEM; } /* ray_attach */ /*============================================================================= @@ -397,37 +378,25 @@ fail_alloc_dev: structures are freed. Otherwise, the structures will be freed when the device is released. =============================================================================*/ -static void ray_detach(struct pcmcia_device *p_dev) +static void ray_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); - dev_link_t **linkp; struct net_device *dev; ray_dev_t *local; DEBUG(1, "ray_detach(0x%p)\n", link); - - /* Locate device structure */ - for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) - if (*linkp == link) break; - if (*linkp == NULL) - return; + this_device = NULL; dev = link->priv; - if (link->state & DEV_CONFIG) { - ray_release(link); + ray_release(link); - local = (ray_dev_t *)dev->priv; - del_timer(&local->timer); - } + local = (ray_dev_t *)dev->priv; + del_timer(&local->timer); - /* Unlink device structure, free pieces */ - *linkp = link->next; if (link->priv) { - if (link->dev) unregister_netdev(dev); + if (link->dev_node) unregister_netdev(dev); free_netdev(dev); } - kfree(link); DEBUG(2,"ray_cs ray_detach ending\n"); } /* ray_detach */ /*============================================================================= @@ -438,9 +407,8 @@ static void ray_detach(struct pcmcia_device *p_dev) #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) #define MAX_TUPLE_SIZE 128 -static void ray_config(dev_link_t *link) +static int ray_config(struct pcmcia_device *link) { - client_handle_t handle = link->handle; tuple_t tuple; cisparse_t parse; int last_fn = 0, last_ret = 0; @@ -455,48 +423,45 @@ static void ray_config(dev_link_t *link) /* This reads the card's CONFIG tuple to find its configuration regs */ tuple.DesiredTuple = CISTPL_CONFIG; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); tuple.TupleData = buf; tuple.TupleDataMax = MAX_TUPLE_SIZE; tuple.TupleOffset = 0; - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; /* Determine card type and firmware version */ buf[0] = buf[MAX_TUPLE_SIZE - 1] = 0; tuple.DesiredTuple = CISTPL_VERS_1; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); tuple.TupleData = buf; tuple.TupleDataMax = MAX_TUPLE_SIZE; tuple.TupleOffset = 2; - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); for (i=0; istate |= DEV_CONFIG; - /* Now allocate an interrupt line. Note that this does not actually assign a handler to the interrupt. */ - CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); dev->irq = link->irq.AssignedIRQ; /* This actually configures the PCMCIA socket -- setting up the I/O windows and the interrupt mapping. */ - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); /*** Set up 32k window for shared memory (transmit and control) ************/ req.Attributes = WIN_DATA_WIDTH_8 | WIN_MEMORY_TYPE_CM | WIN_ENABLE | WIN_USE_WAIT; req.Base = 0; req.Size = 0x8000; req.AccessSpeed = ray_mem_speed; - CS_CHECK(RequestWindow, pcmcia_request_window(&link->handle, &req, &link->win)); + CS_CHECK(RequestWindow, pcmcia_request_window(&link, &req, &link->win)); mem.CardOffset = 0x0000; mem.Page = 0; CS_CHECK(MapMemPage, pcmcia_map_mem_page(link->win, &mem)); local->sram = ioremap(req.Base,req.Size); @@ -506,7 +471,7 @@ static void ray_config(dev_link_t *link) req.Base = 0; req.Size = 0x4000; req.AccessSpeed = ray_mem_speed; - CS_CHECK(RequestWindow, pcmcia_request_window(&link->handle, &req, &local->rmem_handle)); + CS_CHECK(RequestWindow, pcmcia_request_window(&link, &req, &local->rmem_handle)); mem.CardOffset = 0x8000; mem.Page = 0; CS_CHECK(MapMemPage, pcmcia_map_mem_page(local->rmem_handle, &mem)); local->rmem = ioremap(req.Base,req.Size); @@ -516,7 +481,7 @@ static void ray_config(dev_link_t *link) req.Base = 0; req.Size = 0x1000; req.AccessSpeed = ray_mem_speed; - CS_CHECK(RequestWindow, pcmcia_request_window(&link->handle, &req, &local->amem_handle)); + CS_CHECK(RequestWindow, pcmcia_request_window(&link, &req, &local->amem_handle)); mem.CardOffset = 0x0000; mem.Page = 0; CS_CHECK(MapMemPage, pcmcia_map_mem_page(local->amem_handle, &mem)); local->amem = ioremap(req.Base,req.Size); @@ -526,32 +491,32 @@ static void ray_config(dev_link_t *link) DEBUG(3,"ray_config amem=%p\n",local->amem); if (ray_init(dev) < 0) { ray_release(link); - return; + return -ENODEV; } - SET_NETDEV_DEV(dev, &handle_to_dev(handle)); + SET_NETDEV_DEV(dev, &handle_to_dev(link)); i = register_netdev(dev); if (i != 0) { printk("ray_config register_netdev() failed\n"); ray_release(link); - return; + return i; } strcpy(local->node.dev_name, dev->name); - link->dev = &local->node; + link->dev_node = &local->node; - link->state &= ~DEV_CONFIG_PENDING; printk(KERN_INFO "%s: RayLink, irq %d, hw_addr ", dev->name, dev->irq); for (i = 0; i < 6; i++) printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n")); - return; + return 0; cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); ray_release(link); + return -ENODEV; } /* ray_config */ static inline struct ccs __iomem *ccs_base(ray_dev_t *dev) @@ -578,9 +543,9 @@ static int ray_init(struct net_device *dev) UCHAR *p; struct ccs __iomem *pccs; ray_dev_t *local = (ray_dev_t *)dev->priv; - dev_link_t *link = local->finder; + struct pcmcia_device *link = local->finder; DEBUG(1, "ray_init(0x%p)\n", dev); - if (!(link->state & DEV_PRESENT)) { + if (!(pcmcia_dev_present(link))) { DEBUG(0,"ray_init - device not present\n"); return -1; } @@ -640,10 +605,10 @@ static int dl_startup_params(struct net_device *dev) int ccsindex; ray_dev_t *local = (ray_dev_t *)dev->priv; struct ccs __iomem *pccs; - dev_link_t *link = local->finder; + struct pcmcia_device *link = local->finder; DEBUG(1,"dl_startup_params entered\n"); - if (!(link->state & DEV_PRESENT)) { + if (!(pcmcia_dev_present(link))) { DEBUG(2,"ray_cs dl_startup_params - device not present\n"); return -1; } @@ -747,9 +712,9 @@ static void verify_dl_startup(u_long data) ray_dev_t *local = (ray_dev_t *)data; struct ccs __iomem *pccs = ccs_base(local) + local->dl_param_ccs; UCHAR status; - dev_link_t *link = local->finder; + struct pcmcia_device *link = local->finder; - if (!(link->state & DEV_PRESENT)) { + if (!(pcmcia_dev_present(link))) { DEBUG(2,"ray_cs verify_dl_startup - device not present\n"); return; } @@ -787,8 +752,8 @@ static void start_net(u_long data) ray_dev_t *local = (ray_dev_t *)data; struct ccs __iomem *pccs; int ccsindex; - dev_link_t *link = local->finder; - if (!(link->state & DEV_PRESENT)) { + struct pcmcia_device *link = local->finder; + if (!(pcmcia_dev_present(link))) { DEBUG(2,"ray_cs start_net - device not present\n"); return; } @@ -814,9 +779,9 @@ static void join_net(u_long data) struct ccs __iomem *pccs; int ccsindex; - dev_link_t *link = local->finder; + struct pcmcia_device *link = local->finder; - if (!(link->state & DEV_PRESENT)) { + if (!(pcmcia_dev_present(link))) { DEBUG(2,"ray_cs join_net - device not present\n"); return; } @@ -840,7 +805,7 @@ static void join_net(u_long data) device, and release the PCMCIA configuration. If the device is still open, this will be postponed until it is closed. =============================================================================*/ -static void ray_release(dev_link_t *link) +static void ray_release(struct pcmcia_device *link) { struct net_device *dev = link->priv; ray_dev_t *local = dev->priv; @@ -849,56 +814,38 @@ static void ray_release(dev_link_t *link) DEBUG(1, "ray_release(0x%p)\n", link); del_timer(&local->timer); - link->state &= ~DEV_CONFIG; iounmap(local->sram); iounmap(local->rmem); iounmap(local->amem); /* Do bother checking to see if these succeed or not */ - i = pcmcia_release_window(link->win); - if ( i != CS_SUCCESS ) DEBUG(0,"ReleaseWindow(link->win) ret = %x\n",i); i = pcmcia_release_window(local->amem_handle); if ( i != CS_SUCCESS ) DEBUG(0,"ReleaseWindow(local->amem) ret = %x\n",i); i = pcmcia_release_window(local->rmem_handle); if ( i != CS_SUCCESS ) DEBUG(0,"ReleaseWindow(local->rmem) ret = %x\n",i); - i = pcmcia_release_configuration(link->handle); - if ( i != CS_SUCCESS ) DEBUG(0,"ReleaseConfiguration ret = %x\n",i); - i = pcmcia_release_irq(link->handle, &link->irq); - if ( i != CS_SUCCESS ) DEBUG(0,"ReleaseIRQ ret = %x\n",i); + pcmcia_disable_device(link); DEBUG(2,"ray_release ending\n"); } -static int ray_suspend(struct pcmcia_device *p_dev) +static int ray_suspend(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - if (link->open) - netif_device_detach(dev); - - pcmcia_release_configuration(link->handle); - } - + if (link->open) + netif_device_detach(dev); return 0; } -static int ray_resume(struct pcmcia_device *p_dev) +static int ray_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - pcmcia_request_configuration(link->handle, &link->conf); - if (link->open) { - ray_reset(dev); - netif_device_attach(dev); - } - } + if (link->open) { + ray_reset(dev); + netif_device_attach(dev); + } return 0; } @@ -910,10 +857,10 @@ int ray_dev_init(struct net_device *dev) int i; #endif /* RAY_IMMEDIATE_INIT */ ray_dev_t *local = dev->priv; - dev_link_t *link = local->finder; + struct pcmcia_device *link = local->finder; DEBUG(1,"ray_dev_init(dev=%p)\n",dev); - if (!(link->state & DEV_PRESENT)) { + if (!(pcmcia_dev_present(link))) { DEBUG(2,"ray_dev_init - device not present\n"); return -1; } @@ -944,10 +891,10 @@ int ray_dev_init(struct net_device *dev) static int ray_dev_config(struct net_device *dev, struct ifmap *map) { ray_dev_t *local = dev->priv; - dev_link_t *link = local->finder; + struct pcmcia_device *link = local->finder; /* Dummy routine to satisfy device structure */ DEBUG(1,"ray_dev_config(dev=%p,ifmap=%p)\n",dev,map); - if (!(link->state & DEV_PRESENT)) { + if (!(pcmcia_dev_present(link))) { DEBUG(2,"ray_dev_config - device not present\n"); return -1; } @@ -958,10 +905,10 @@ static int ray_dev_config(struct net_device *dev, struct ifmap *map) static int ray_dev_start_xmit(struct sk_buff *skb, struct net_device *dev) { ray_dev_t *local = dev->priv; - dev_link_t *link = local->finder; + struct pcmcia_device *link = local->finder; short length = skb->len; - if (!(link->state & DEV_PRESENT)) { + if (!(pcmcia_dev_present(link))) { DEBUG(2,"ray_dev_start_xmit - device not present\n"); return -1; } @@ -1570,7 +1517,7 @@ static int ray_commit(struct net_device *dev, static iw_stats * ray_get_wireless_stats(struct net_device * dev) { ray_dev_t * local = (ray_dev_t *) dev->priv; - dev_link_t *link = local->finder; + struct pcmcia_device *link = local->finder; struct status __iomem *p = local->sram + STATUS_BASE; if(local == (ray_dev_t *) NULL) @@ -1588,7 +1535,7 @@ static iw_stats * ray_get_wireless_stats(struct net_device * dev) } #endif /* WIRELESS_SPY */ - if((link->state & DEV_PRESENT)) { + if(pcmcia_dev_present(link)) { local->wstats.qual.noise = readb(&p->rxnoise); local->wstats.qual.updated |= 4; } @@ -1657,18 +1604,14 @@ static const struct iw_handler_def ray_handler_def = /*===========================================================================*/ static int ray_open(struct net_device *dev) { - dev_link_t *link; ray_dev_t *local = (ray_dev_t *)dev->priv; + struct pcmcia_device *link; + link = local->finder; DEBUG(1, "ray_open('%s')\n", dev->name); - for (link = dev_list; link; link = link->next) - if (link->priv == dev) break; - if (!DEV_OK(link)) { - return -ENODEV; - } - - if (link->open == 0) local->num_multi = 0; + if (link->open == 0) + local->num_multi = 0; link->open++; /* If the card is not started, time to start it ! - Jean II */ @@ -1695,15 +1638,12 @@ static int ray_open(struct net_device *dev) /*===========================================================================*/ static int ray_dev_close(struct net_device *dev) { - dev_link_t *link; + ray_dev_t *local = (ray_dev_t *)dev->priv; + struct pcmcia_device *link; + link = local->finder; DEBUG(1, "ray_dev_close('%s')\n", dev->name); - for (link = dev_list; link; link = link->next) - if (link->priv == dev) break; - if (link == NULL) - return -ENODEV; - link->open--; netif_stop_queue(dev); @@ -1725,9 +1665,9 @@ static void ray_reset(struct net_device *dev) { static int interrupt_ecf(ray_dev_t *local, int ccs) { int i = 50; - dev_link_t *link = local->finder; + struct pcmcia_device *link = local->finder; - if (!(link->state & DEV_PRESENT)) { + if (!(pcmcia_dev_present(link))) { DEBUG(2,"ray_cs interrupt_ecf - device not present\n"); return -1; } @@ -1752,9 +1692,9 @@ static int get_free_tx_ccs(ray_dev_t *local) { int i; struct ccs __iomem *pccs = ccs_base(local); - dev_link_t *link = local->finder; + struct pcmcia_device *link = local->finder; - if (!(link->state & DEV_PRESENT)) { + if (!(pcmcia_dev_present(link))) { DEBUG(2,"ray_cs get_free_tx_ccs - device not present\n"); return ECARDGONE; } @@ -1783,9 +1723,9 @@ static int get_free_ccs(ray_dev_t *local) { int i; struct ccs __iomem *pccs = ccs_base(local); - dev_link_t *link = local->finder; + struct pcmcia_device *link = local->finder; - if (!(link->state & DEV_PRESENT)) { + if (!(pcmcia_dev_present(link))) { DEBUG(2,"ray_cs get_free_ccs - device not present\n"); return ECARDGONE; } @@ -1858,9 +1798,9 @@ static int parse_addr(char *in_str, UCHAR *out) static struct net_device_stats *ray_get_stats(struct net_device *dev) { ray_dev_t *local = (ray_dev_t *)dev->priv; - dev_link_t *link = local->finder; + struct pcmcia_device *link = local->finder; struct status __iomem *p = local->sram + STATUS_BASE; - if (!(link->state & DEV_PRESENT)) { + if (!(pcmcia_dev_present(link))) { DEBUG(2,"ray_cs net_device_stats - device not present\n"); return &local->stats; } @@ -1888,12 +1828,12 @@ static struct net_device_stats *ray_get_stats(struct net_device *dev) static void ray_update_parm(struct net_device *dev, UCHAR objid, UCHAR *value, int len) { ray_dev_t *local = (ray_dev_t *)dev->priv; - dev_link_t *link = local->finder; + struct pcmcia_device *link = local->finder; int ccsindex; int i; struct ccs __iomem *pccs; - if (!(link->state & DEV_PRESENT)) { + if (!(pcmcia_dev_present(link))) { DEBUG(2,"ray_update_parm - device not present\n"); return; } @@ -1925,10 +1865,10 @@ static void ray_update_multi_list(struct net_device *dev, int all) struct ccs __iomem *pccs; int i = 0; ray_dev_t *local = (ray_dev_t *)dev->priv; - dev_link_t *link = local->finder; + struct pcmcia_device *link = local->finder; void __iomem *p = local->sram + HOST_TO_ECF_BASE; - if (!(link->state & DEV_PRESENT)) { + if (!(pcmcia_dev_present(link))) { DEBUG(2,"ray_update_multi_list - device not present\n"); return; } @@ -2005,7 +1945,7 @@ static void set_multicast_list(struct net_device *dev) static irqreturn_t ray_interrupt(int irq, void *dev_id, struct pt_regs * regs) { struct net_device *dev = (struct net_device *)dev_id; - dev_link_t *link; + struct pcmcia_device *link; ray_dev_t *local; struct ccs __iomem *pccs; struct rcs __iomem *prcs; @@ -2020,8 +1960,8 @@ static irqreturn_t ray_interrupt(int irq, void *dev_id, struct pt_regs * regs) DEBUG(4,"ray_cs: interrupt for *dev=%p\n",dev); local = (ray_dev_t *)dev->priv; - link = (dev_link_t *)local->finder; - if ( ! (link->state & DEV_PRESENT) || link->state & DEV_SUSPEND ) { + link = (struct pcmcia_device *)local->finder; + if (!pcmcia_dev_present(link)) { DEBUG(2,"ray_cs interrupt from device not present or suspended.\n"); return IRQ_NONE; } @@ -2540,9 +2480,9 @@ static void release_frag_chain(ray_dev_t *local, struct rcs __iomem * prcs) /*===========================================================================*/ static void authenticate(ray_dev_t *local) { - dev_link_t *link = local->finder; + struct pcmcia_device *link = local->finder; DEBUG(0,"ray_cs Starting authentication.\n"); - if (!(link->state & DEV_PRESENT)) { + if (!(pcmcia_dev_present(link))) { DEBUG(2,"ray_cs authenticate - device not present\n"); return; } @@ -2606,10 +2546,10 @@ static void rx_authenticate(ray_dev_t *local, struct rcs __iomem *prcs, static void associate(ray_dev_t *local) { struct ccs __iomem *pccs; - dev_link_t *link = local->finder; + struct pcmcia_device *link = local->finder; struct net_device *dev = link->priv; int ccsindex; - if (!(link->state & DEV_PRESENT)) { + if (!(pcmcia_dev_present(link))) { DEBUG(2,"ray_cs associate - device not present\n"); return; } @@ -2689,14 +2629,14 @@ static int ray_cs_proc_read(char *buf, char **start, off_t offset, int len) * eg ifconfig */ int i; - dev_link_t *link; + struct pcmcia_device *link; struct net_device *dev; ray_dev_t *local; UCHAR *p; struct freq_hop_element *pfh; UCHAR c[33]; - link = dev_list; + link = this_device; if (!link) return 0; dev = (struct net_device *)link->priv; @@ -2898,7 +2838,7 @@ static struct pcmcia_driver ray_driver = { .drv = { .name = "ray_cs", }, - .probe = ray_attach, + .probe = ray_probe, .remove = ray_detach, .id_table = ray_ids, .suspend = ray_suspend, @@ -2940,7 +2880,6 @@ static void __exit exit_ray_cs(void) #endif pcmcia_unregister_driver(&ray_driver); - BUG_ON(dev_list != NULL); } /* exit_ray_cs */ module_init(init_ray_cs); diff --git a/drivers/net/wireless/ray_cs.h b/drivers/net/wireless/ray_cs.h index 42660fe64bfd..bd73ebf03340 100644 --- a/drivers/net/wireless/ray_cs.h +++ b/drivers/net/wireless/ray_cs.h @@ -31,7 +31,7 @@ typedef struct ray_dev_t { void __iomem *sram; /* pointer to beginning of shared RAM */ void __iomem *amem; /* pointer to attribute mem window */ void __iomem *rmem; /* pointer to receive buffer window */ - dev_link_t *finder; /* pointer back to dev_link_t for card */ + struct pcmcia_device *finder; /* pointer back to struct pcmcia_device for card */ struct timer_list timer; long tx_ccs_lock; long ccs_lock; diff --git a/drivers/net/wireless/spectrum_cs.c b/drivers/net/wireless/spectrum_cs.c index 5fa6fbe35bb9..f7b77ce54d7b 100644 --- a/drivers/net/wireless/spectrum_cs.c +++ b/drivers/net/wireless/spectrum_cs.c @@ -63,7 +63,7 @@ MODULE_PARM_DESC(ignore_cis_vcc, "Allow voltage mismatch between card and socket /* PCMCIA specific device information (goes in the card field of * struct orinoco_private */ struct orinoco_pccard { - dev_link_t link; + struct pcmcia_device *p_dev; dev_node_t node; }; @@ -71,8 +71,8 @@ struct orinoco_pccard { /* Function prototypes */ /********************************************************************/ -static void spectrum_cs_config(dev_link_t *link); -static void spectrum_cs_release(dev_link_t *link); +static int spectrum_cs_config(struct pcmcia_device *link); +static void spectrum_cs_release(struct pcmcia_device *link); /********************************************************************/ /* Firmware downloader */ @@ -238,14 +238,14 @@ spectrum_aux_open(hermes_t *hw) * If IDLE is 1, stop the firmware, so that it can be safely rewritten. */ static int -spectrum_reset(dev_link_t *link, int idle) +spectrum_reset(struct pcmcia_device *link, int idle) { int last_ret, last_fn; conf_reg_t reg; u_int save_cor; /* Doing it if hardware is gone is guaranteed crash */ - if (!(link->state & DEV_CONFIG)) + if (pcmcia_dev_present(link)) return -ENODEV; /* Save original COR value */ @@ -253,7 +253,7 @@ spectrum_reset(dev_link_t *link, int idle) reg.Action = CS_READ; reg.Offset = CISREG_COR; CS_CHECK(AccessConfigurationRegister, - pcmcia_access_configuration_register(link->handle, ®)); + pcmcia_access_configuration_register(link, ®)); save_cor = reg.Value; /* Soft-Reset card */ @@ -261,14 +261,14 @@ spectrum_reset(dev_link_t *link, int idle) reg.Offset = CISREG_COR; reg.Value = (save_cor | COR_SOFT_RESET); CS_CHECK(AccessConfigurationRegister, - pcmcia_access_configuration_register(link->handle, ®)); + pcmcia_access_configuration_register(link, ®)); udelay(1000); /* Read CCSR */ reg.Action = CS_READ; reg.Offset = CISREG_CCSR; CS_CHECK(AccessConfigurationRegister, - pcmcia_access_configuration_register(link->handle, ®)); + pcmcia_access_configuration_register(link, ®)); /* * Start or stop the firmware. Memory width bit should be @@ -278,7 +278,7 @@ spectrum_reset(dev_link_t *link, int idle) reg.Offset = CISREG_CCSR; reg.Value = (idle ? HCR_IDLE : HCR_RUN) | (reg.Value & HCR_MEM16); CS_CHECK(AccessConfigurationRegister, - pcmcia_access_configuration_register(link->handle, ®)); + pcmcia_access_configuration_register(link, ®)); udelay(1000); /* Restore original COR configuration index */ @@ -286,12 +286,12 @@ spectrum_reset(dev_link_t *link, int idle) reg.Offset = CISREG_COR; reg.Value = (save_cor & ~COR_SOFT_RESET); CS_CHECK(AccessConfigurationRegister, - pcmcia_access_configuration_register(link->handle, ®)); + pcmcia_access_configuration_register(link, ®)); udelay(1000); return 0; cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); return -ENODEV; } @@ -441,7 +441,7 @@ spectrum_load_blocks(hermes_t *hw, const struct dblock *first_block) * care of the PDA - read it and then write it on top of the firmware. */ static int -spectrum_dl_image(hermes_t *hw, dev_link_t *link, +spectrum_dl_image(hermes_t *hw, struct pcmcia_device *link, const unsigned char *image) { int ret; @@ -505,14 +505,13 @@ spectrum_dl_image(hermes_t *hw, dev_link_t *link, * reset on the card, to make sure it's in a sane state. */ static int -spectrum_dl_firmware(hermes_t *hw, dev_link_t *link) +spectrum_dl_firmware(hermes_t *hw, struct pcmcia_device *link) { int ret; - client_handle_t handle = link->handle; const struct firmware *fw_entry; if (request_firmware(&fw_entry, primary_fw_name, - &handle_to_dev(handle)) == 0) { + &handle_to_dev(link)) == 0) { primsym = fw_entry->data; } else { printk(KERN_ERR PFX "Cannot find firmware: %s\n", @@ -521,7 +520,7 @@ spectrum_dl_firmware(hermes_t *hw, dev_link_t *link) } if (request_firmware(&fw_entry, secondary_fw_name, - &handle_to_dev(handle)) == 0) { + &handle_to_dev(link)) == 0) { secsym = fw_entry->data; } else { printk(KERN_ERR PFX "Cannot find firmware: %s\n", @@ -554,12 +553,12 @@ static int spectrum_cs_hard_reset(struct orinoco_private *priv) { struct orinoco_pccard *card = priv->card; - dev_link_t *link = &card->link; + struct pcmcia_device *link = card->p_dev; int err; if (!hermes_present(&priv->hw)) { /* The firmware needs to be reloaded */ - if (spectrum_dl_firmware(&priv->hw, &card->link) != 0) { + if (spectrum_dl_firmware(&priv->hw, link) != 0) { printk(KERN_ERR PFX "Firmware download failed\n"); err = -ENODEV; } @@ -584,12 +583,11 @@ spectrum_cs_hard_reset(struct orinoco_private *priv) * configure the card at this point -- we wait until we receive a card * insertion event. */ static int -spectrum_cs_attach(struct pcmcia_device *p_dev) +spectrum_cs_probe(struct pcmcia_device *link) { struct net_device *dev; struct orinoco_private *priv; struct orinoco_pccard *card; - dev_link_t *link; dev = alloc_orinocodev(sizeof(*card), spectrum_cs_hard_reset); if (! dev) @@ -598,7 +596,7 @@ spectrum_cs_attach(struct pcmcia_device *p_dev) card = priv->card; /* Link both structures together */ - link = &card->link; + card->p_dev = link; link->priv = dev; /* Interrupt setup */ @@ -615,13 +613,7 @@ spectrum_cs_attach(struct pcmcia_device *p_dev) link->conf.Attributes = 0; link->conf.IntType = INT_MEMORY_AND_IO; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - spectrum_cs_config(link); - - return 0; + return spectrum_cs_config(link); } /* spectrum_cs_attach */ /* @@ -630,16 +622,14 @@ spectrum_cs_attach(struct pcmcia_device *p_dev) * are freed. Otherwise, the structures will be freed when the device * is released. */ -static void spectrum_cs_detach(struct pcmcia_device *p_dev) +static void spectrum_cs_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; - if (link->state & DEV_CONFIG) - spectrum_cs_release(link); + spectrum_cs_release(link); - DEBUG(0, PFX "detach: link=%p link->dev=%p\n", link, link->dev); - if (link->dev) { + DEBUG(0, PFX "detach: link=%p link->dev_node=%p\n", link, link->dev_node); + if (link->dev_node) { DEBUG(0, PFX "About to unregister net device %p\n", dev); unregister_netdev(dev); @@ -653,11 +643,10 @@ static void spectrum_cs_detach(struct pcmcia_device *p_dev) * device available to the system. */ -static void -spectrum_cs_config(dev_link_t *link) +static int +spectrum_cs_config(struct pcmcia_device *link) { struct net_device *dev = link->priv; - client_handle_t handle = link->handle; struct orinoco_private *priv = netdev_priv(dev); struct orinoco_pccard *card = priv->card; hermes_t *hw = &priv->hw; @@ -669,7 +658,7 @@ spectrum_cs_config(dev_link_t *link) cisparse_t parse; void __iomem *mem; - CS_CHECK(ValidateCIS, pcmcia_validate_cis(handle, &info)); + CS_CHECK(ValidateCIS, pcmcia_validate_cis(link, &info)); /* * This reads the card's CONFIG tuple to find its @@ -680,19 +669,15 @@ spectrum_cs_config(dev_link_t *link) tuple.TupleData = buf; tuple.TupleDataMax = sizeof(buf); tuple.TupleOffset = 0; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; - /* Configure card */ - link->state |= DEV_CONFIG; - /* Look up the current Vcc */ CS_CHECK(GetConfigurationInfo, - pcmcia_get_configuration_info(handle, &conf)); - link->conf.Vcc = conf.Vcc; + pcmcia_get_configuration_info(link, &conf)); /* * In this loop, we scan the CIS for configuration table @@ -709,13 +694,13 @@ spectrum_cs_config(dev_link_t *link) * implementation-defined details. */ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); while (1) { cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); cistpl_cftable_entry_t dflt = { .index = 0 }; - if ( (pcmcia_get_tuple_data(handle, &tuple) != 0) - || (pcmcia_parse_tuple(handle, &tuple, &parse) != 0)) + if ( (pcmcia_get_tuple_data(link, &tuple) != 0) + || (pcmcia_parse_tuple(link, &tuple, &parse) != 0)) goto next_entry; if (cfg->flags & CISTPL_CFTABLE_DEFAULT) @@ -747,10 +732,10 @@ spectrum_cs_config(dev_link_t *link) } if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM)) - link->conf.Vpp1 = link->conf.Vpp2 = + link->conf.Vpp = cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000; else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM)) - link->conf.Vpp1 = link->conf.Vpp2 = + link->conf.Vpp = dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000; /* Do we need to allocate an interrupt? */ @@ -780,7 +765,7 @@ spectrum_cs_config(dev_link_t *link) } /* This reserves IO space but doesn't actually enable it */ - if (pcmcia_request_io(link->handle, &link->io) != 0) + if (pcmcia_request_io(link, &link->io) != 0) goto next_entry; } @@ -790,9 +775,8 @@ spectrum_cs_config(dev_link_t *link) break; next_entry: - if (link->io.NumPorts1) - pcmcia_release_io(link->handle, &link->io); - last_ret = pcmcia_get_next_tuple(handle, &tuple); + pcmcia_disable_device(link); + last_ret = pcmcia_get_next_tuple(link, &tuple); if (last_ret == CS_NO_MORE_ITEMS) { printk(KERN_ERR PFX "GetNextTuple(): No matching " "CIS configuration. Maybe you need the " @@ -806,7 +790,7 @@ spectrum_cs_config(dev_link_t *link) * a handler to the interrupt, unless the 'Handler' member of * the irq structure is initialized. */ - CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); /* We initialize the hermes structure before completing PCMCIA * configuration just in case the interrupt handler gets @@ -823,7 +807,7 @@ spectrum_cs_config(dev_link_t *link) * card and host interface into "Memory and IO" mode. */ CS_CHECK(RequestConfiguration, - pcmcia_request_configuration(link->handle, &link->conf)); + pcmcia_request_configuration(link, &link->conf)); /* Ok, we have the configuration, prepare to register the netdev */ dev->base_addr = link->io.BasePort1; @@ -836,7 +820,7 @@ spectrum_cs_config(dev_link_t *link) goto failed; } - SET_NETDEV_DEV(dev, &handle_to_dev(handle)); + SET_NETDEV_DEV(dev, &handle_to_dev(link)); /* Tell the stack we exist */ if (register_netdev(dev) != 0) { printk(KERN_ERR PFX "register_netdev() failed\n"); @@ -844,20 +828,18 @@ spectrum_cs_config(dev_link_t *link) } /* At this point, the dev_node_t structure(s) needs to be - * initialized and arranged in a linked list at link->dev. */ + * initialized and arranged in a linked list at link->dev_node. */ strcpy(card->node.dev_name, dev->name); - link->dev = &card->node; /* link->dev being non-NULL is also + link->dev_node = &card->node; /* link->dev_node being non-NULL is also used to indicate that the net_device has been registered */ - link->state &= ~DEV_CONFIG_PENDING; /* Finally, report what we've done */ - printk(KERN_DEBUG "%s: index 0x%02x: Vcc %d.%d", - dev->name, link->conf.ConfigIndex, - link->conf.Vcc / 10, link->conf.Vcc % 10); - if (link->conf.Vpp1) - printk(", Vpp %d.%d", link->conf.Vpp1 / 10, - link->conf.Vpp1 % 10); + printk(KERN_DEBUG "%s: index 0x%02x: ", + dev->name, link->conf.ConfigIndex); + if (link->conf.Vpp) + printk(", Vpp %d.%d", link->conf.Vpp / 10, + link->conf.Vpp % 10); printk(", irq %d", link->irq.AssignedIRQ); if (link->io.NumPorts1) printk(", io 0x%04x-0x%04x", link->io.BasePort1, @@ -867,13 +849,14 @@ spectrum_cs_config(dev_link_t *link) link->io.BasePort2 + link->io.NumPorts2 - 1); printk("\n"); - return; + return 0; cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); failed: spectrum_cs_release(link); + return -ENODEV; } /* spectrum_cs_config */ /* @@ -882,7 +865,7 @@ spectrum_cs_config(dev_link_t *link) * still open, this will be postponed until it is closed. */ static void -spectrum_cs_release(dev_link_t *link) +spectrum_cs_release(struct pcmcia_device *link) { struct net_device *dev = link->priv; struct orinoco_private *priv = netdev_priv(dev); @@ -894,64 +877,46 @@ spectrum_cs_release(dev_link_t *link) priv->hw_unavailable++; spin_unlock_irqrestore(&priv->lock, flags); - /* Don't bother checking to see if these succeed or not */ - pcmcia_release_configuration(link->handle); - if (link->io.NumPorts1) - pcmcia_release_io(link->handle, &link->io); - if (link->irq.AssignedIRQ) - pcmcia_release_irq(link->handle, &link->irq); - link->state &= ~DEV_CONFIG; + pcmcia_disable_device(link); if (priv->hw.iobase) ioport_unmap(priv->hw.iobase); } /* spectrum_cs_release */ static int -spectrum_cs_suspend(struct pcmcia_device *p_dev) +spectrum_cs_suspend(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; struct orinoco_private *priv = netdev_priv(dev); unsigned long flags; int err = 0; - link->state |= DEV_SUSPEND; /* Mark the device as stopped, to block IO until later */ - if (link->state & DEV_CONFIG) { - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irqsave(&priv->lock, flags); - err = __orinoco_down(dev); - if (err) - printk(KERN_WARNING "%s: Error %d downing interface\n", - dev->name, err); + err = __orinoco_down(dev); + if (err) + printk(KERN_WARNING "%s: Error %d downing interface\n", + dev->name, err); - netif_device_detach(dev); - priv->hw_unavailable++; + netif_device_detach(dev); + priv->hw_unavailable++; - spin_unlock_irqrestore(&priv->lock, flags); - - pcmcia_release_configuration(link->handle); - } + spin_unlock_irqrestore(&priv->lock, flags); return 0; } static int -spectrum_cs_resume(struct pcmcia_device *p_dev) +spectrum_cs_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; struct orinoco_private *priv = netdev_priv(dev); - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - /* FIXME: should we double check that this is - * the same card as we had before */ - pcmcia_request_configuration(link->handle, &link->conf); - netif_device_attach(dev); - priv->hw_unavailable--; - schedule_work(&priv->reset_work); - } + netif_device_attach(dev); + priv->hw_unavailable--; + schedule_work(&priv->reset_work); + return 0; } @@ -979,7 +944,7 @@ static struct pcmcia_driver orinoco_driver = { .drv = { .name = DRIVER_NAME, }, - .probe = spectrum_cs_attach, + .probe = spectrum_cs_probe, .remove = spectrum_cs_detach, .suspend = spectrum_cs_suspend, .resume = spectrum_cs_resume, diff --git a/drivers/net/wireless/wavelan_cs.c b/drivers/net/wireless/wavelan_cs.c index 98122f3a4bc2..f7724eb2fa7e 100644 --- a/drivers/net/wireless/wavelan_cs.c +++ b/drivers/net/wireless/wavelan_cs.c @@ -1005,7 +1005,7 @@ static inline void wv_82593_reconfig(struct net_device * dev) { net_local * lp = netdev_priv(dev); - dev_link_t * link = lp->link; + struct pcmcia_device * link = lp->link; unsigned long flags; /* Arm the flag, will be cleard in wv_82593_config() */ @@ -3744,16 +3744,16 @@ wv_pcmcia_reset(struct net_device * dev) { int i; conf_reg_t reg = { 0, CS_READ, CISREG_COR, 0 }; - dev_link_t * link = ((net_local *)netdev_priv(dev))->link; + struct pcmcia_device * link = ((net_local *)netdev_priv(dev))->link; #ifdef DEBUG_CONFIG_TRACE printk(KERN_DEBUG "%s: ->wv_pcmcia_reset()\n", dev->name); #endif - i = pcmcia_access_configuration_register(link->handle, ®); + i = pcmcia_access_configuration_register(link, ®); if(i != CS_SUCCESS) { - cs_error(link->handle, AccessConfigurationRegister, i); + cs_error(link, AccessConfigurationRegister, i); return FALSE; } @@ -3764,19 +3764,19 @@ wv_pcmcia_reset(struct net_device * dev) reg.Action = CS_WRITE; reg.Value = reg.Value | COR_SW_RESET; - i = pcmcia_access_configuration_register(link->handle, ®); + i = pcmcia_access_configuration_register(link, ®); if(i != CS_SUCCESS) { - cs_error(link->handle, AccessConfigurationRegister, i); + cs_error(link, AccessConfigurationRegister, i); return FALSE; } reg.Action = CS_WRITE; reg.Value = COR_LEVEL_IRQ | COR_CONFIG; - i = pcmcia_access_configuration_register(link->handle, ®); + i = pcmcia_access_configuration_register(link, ®); if(i != CS_SUCCESS) { - cs_error(link->handle, AccessConfigurationRegister, i); + cs_error(link, AccessConfigurationRegister, i); return FALSE; } @@ -3940,9 +3940,8 @@ wv_hw_reset(struct net_device * dev) * (called by wavelan_event()) */ static inline int -wv_pcmcia_config(dev_link_t * link) +wv_pcmcia_config(struct pcmcia_device * link) { - client_handle_t handle = link->handle; tuple_t tuple; cisparse_t parse; struct net_device * dev = (struct net_device *) link->priv; @@ -3965,16 +3964,16 @@ wv_pcmcia_config(dev_link_t * link) { tuple.Attributes = 0; tuple.DesiredTuple = CISTPL_CONFIG; - i = pcmcia_get_first_tuple(handle, &tuple); + i = pcmcia_get_first_tuple(link, &tuple); if(i != CS_SUCCESS) break; tuple.TupleData = (cisdata_t *)buf; tuple.TupleDataMax = 64; tuple.TupleOffset = 0; - i = pcmcia_get_tuple_data(handle, &tuple); + i = pcmcia_get_tuple_data(link, &tuple); if(i != CS_SUCCESS) break; - i = pcmcia_parse_tuple(handle, &tuple, &parse); + i = pcmcia_parse_tuple(link, &tuple, &parse); if(i != CS_SUCCESS) break; link->conf.ConfigBase = parse.config.base; @@ -3983,19 +3982,16 @@ wv_pcmcia_config(dev_link_t * link) while(0); if(i != CS_SUCCESS) { - cs_error(link->handle, ParseTuple, i); - link->state &= ~DEV_CONFIG_PENDING; + cs_error(link, ParseTuple, i); return FALSE; } - - /* Configure card */ - link->state |= DEV_CONFIG; + do { - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); if(i != CS_SUCCESS) { - cs_error(link->handle, RequestIO, i); + cs_error(link, RequestIO, i); break; } @@ -4003,10 +3999,10 @@ wv_pcmcia_config(dev_link_t * link) * Now allocate an interrupt line. Note that this does not * actually assign a handler to the interrupt. */ - i = pcmcia_request_irq(link->handle, &link->irq); + i = pcmcia_request_irq(link, &link->irq); if(i != CS_SUCCESS) { - cs_error(link->handle, RequestIRQ, i); + cs_error(link, RequestIRQ, i); break; } @@ -4015,15 +4011,15 @@ wv_pcmcia_config(dev_link_t * link) * the I/O windows and the interrupt mapping. */ link->conf.ConfigIndex = 1; - i = pcmcia_request_configuration(link->handle, &link->conf); + i = pcmcia_request_configuration(link, &link->conf); if(i != CS_SUCCESS) { - cs_error(link->handle, RequestConfiguration, i); + cs_error(link, RequestConfiguration, i); break; } /* - * Allocate a small memory window. Note that the dev_link_t + * Allocate a small memory window. Note that the struct pcmcia_device * structure provides space for one window handle -- if your * device needs several windows, you'll need to keep track of * the handles in your private data structure, link->priv. @@ -4031,10 +4027,10 @@ wv_pcmcia_config(dev_link_t * link) req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; req.Base = req.Size = 0; req.AccessSpeed = mem_speed; - i = pcmcia_request_window(&link->handle, &req, &link->win); + i = pcmcia_request_window(&link, &req, &link->win); if(i != CS_SUCCESS) { - cs_error(link->handle, RequestWindow, i); + cs_error(link, RequestWindow, i); break; } @@ -4046,7 +4042,7 @@ wv_pcmcia_config(dev_link_t * link) i = pcmcia_map_mem_page(link->win, &mem); if(i != CS_SUCCESS) { - cs_error(link->handle, MapMemPage, i); + cs_error(link, MapMemPage, i); break; } @@ -4060,7 +4056,7 @@ wv_pcmcia_config(dev_link_t * link) lp->mem, dev->irq, (u_int) dev->base_addr); #endif - SET_NETDEV_DEV(dev, &handle_to_dev(handle)); + SET_NETDEV_DEV(dev, &handle_to_dev(link)); i = register_netdev(dev); if(i != 0) { @@ -4072,7 +4068,6 @@ wv_pcmcia_config(dev_link_t * link) } while(0); /* Humm... Disguised goto !!! */ - link->state &= ~DEV_CONFIG_PENDING; /* If any step failed, release any partially configured state */ if(i != 0) { @@ -4081,7 +4076,7 @@ wv_pcmcia_config(dev_link_t * link) } strcpy(((net_local *) netdev_priv(dev))->node.dev_name, dev->name); - link->dev = &((net_local *) netdev_priv(dev))->node; + link->dev_node = &((net_local *) netdev_priv(dev))->node; #ifdef DEBUG_CONFIG_TRACE printk(KERN_DEBUG "<-wv_pcmcia_config()\n"); @@ -4096,26 +4091,20 @@ wv_pcmcia_config(dev_link_t * link) * still open, this will be postponed until it is closed. */ static void -wv_pcmcia_release(dev_link_t *link) +wv_pcmcia_release(struct pcmcia_device *link) { - struct net_device * dev = (struct net_device *) link->priv; - net_local * lp = netdev_priv(dev); + struct net_device * dev = (struct net_device *) link->priv; + net_local * lp = netdev_priv(dev); #ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: -> wv_pcmcia_release(0x%p)\n", dev->name, link); + printk(KERN_DEBUG "%s: -> wv_pcmcia_release(0x%p)\n", dev->name, link); #endif - /* Don't bother checking to see if these succeed or not */ - iounmap(lp->mem); - pcmcia_release_window(link->win); - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - - link->state &= ~DEV_CONFIG; + iounmap(lp->mem); + pcmcia_disable_device(link); #ifdef DEBUG_CONFIG_TRACE - printk(KERN_DEBUG "%s: <- wv_pcmcia_release()\n", dev->name); + printk(KERN_DEBUG "%s: <- wv_pcmcia_release()\n", dev->name); #endif } @@ -4479,7 +4468,7 @@ static int wavelan_open(struct net_device * dev) { net_local * lp = netdev_priv(dev); - dev_link_t * link = lp->link; + struct pcmcia_device * link = lp->link; kio_addr_t base = dev->base_addr; #ifdef DEBUG_CALLBACK_TRACE @@ -4533,7 +4522,7 @@ wavelan_open(struct net_device * dev) static int wavelan_close(struct net_device * dev) { - dev_link_t * link = ((net_local *)netdev_priv(dev))->link; + struct pcmcia_device * link = ((net_local *)netdev_priv(dev))->link; kio_addr_t base = dev->base_addr; #ifdef DEBUG_CALLBACK_TRACE @@ -4587,45 +4576,36 @@ wavelan_close(struct net_device * dev) * card insertion event. */ static int -wavelan_attach(struct pcmcia_device *p_dev) +wavelan_probe(struct pcmcia_device *p_dev) { - dev_link_t * link; /* Info for cardmgr */ struct net_device * dev; /* Interface generic data */ net_local * lp; /* Interface specific data */ + int ret; #ifdef DEBUG_CALLBACK_TRACE printk(KERN_DEBUG "-> wavelan_attach()\n"); #endif - /* Initialize the dev_link_t structure */ - link = kzalloc(sizeof(struct dev_link_t), GFP_KERNEL); - if (!link) return -ENOMEM; - /* The io structure describes IO port mapping */ - link->io.NumPorts1 = 8; - link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; - link->io.IOAddrLines = 3; + p_dev->io.NumPorts1 = 8; + p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + p_dev->io.IOAddrLines = 3; /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; - link->irq.IRQInfo1 = IRQ_LEVEL_ID; - link->irq.Handler = wavelan_interrupt; + p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; + p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID; + p_dev->irq.Handler = wavelan_interrupt; /* General socket configuration */ - link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; - link->conf.IntType = INT_MEMORY_AND_IO; - - /* Chain drivers */ - link->next = NULL; + p_dev->conf.Attributes = CONF_ENABLE_IRQ; + p_dev->conf.IntType = INT_MEMORY_AND_IO; /* Allocate the generic data structure */ dev = alloc_etherdev(sizeof(net_local)); - if (!dev) { - kfree(link); + if (!dev) return -ENOMEM; - } - link->priv = link->irq.Instance = dev; + + p_dev->priv = p_dev->irq.Instance = dev; lp = netdev_priv(dev); @@ -4642,7 +4622,6 @@ wavelan_attach(struct pcmcia_device *p_dev) spin_lock_init(&lp->spinlock); /* back links */ - lp->link = link; lp->dev = dev; /* wavelan NET3 callbacks */ @@ -4668,15 +4647,18 @@ wavelan_attach(struct pcmcia_device *p_dev) /* Other specific data */ dev->mtu = WAVELAN_MTU; - link->handle = p_dev; - p_dev->instance = link; + ret = wv_pcmcia_config(p_dev); + if (ret) + return ret; - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - if(wv_pcmcia_config(link) && - wv_hw_config(dev)) - wv_init_info(dev); - else + ret = wv_hw_config(dev); + if (ret) { dev->irq = 0; + pcmcia_disable_device(p_dev); + return ret; + } + + wv_init_info(dev); #ifdef DEBUG_CALLBACK_TRACE printk(KERN_DEBUG "<- wavelan_attach()\n"); @@ -4693,25 +4675,14 @@ wavelan_attach(struct pcmcia_device *p_dev) * is released. */ static void -wavelan_detach(struct pcmcia_device *p_dev) +wavelan_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); - #ifdef DEBUG_CALLBACK_TRACE printk(KERN_DEBUG "-> wavelan_detach(0x%p)\n", link); #endif - /* - * If the device is currently configured and active, we won't - * actually delete it yet. Instead, it is marked so that when the - * release() function is called, that will trigger a proper - * detach(). - */ - if(link->state & DEV_CONFIG) - { - /* Some others haven't done their job : give them another chance */ - wv_pcmcia_release(link); - } + /* Some others haven't done their job : give them another chance */ + wv_pcmcia_release(link); /* Free pieces */ if(link->priv) @@ -4720,23 +4691,21 @@ wavelan_detach(struct pcmcia_device *p_dev) /* Remove ourselves from the kernel list of ethernet devices */ /* Warning : can't be called from interrupt, timer or wavelan_close() */ - if (link->dev) + if (link->dev_node) unregister_netdev(dev); - link->dev = NULL; + link->dev_node = NULL; ((net_local *)netdev_priv(dev))->link = NULL; ((net_local *)netdev_priv(dev))->dev = NULL; free_netdev(dev); } - kfree(link); #ifdef DEBUG_CALLBACK_TRACE printk(KERN_DEBUG "<- wavelan_detach()\n"); #endif } -static int wavelan_suspend(struct pcmcia_device *p_dev) +static int wavelan_suspend(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device * dev = (struct net_device *) link->priv; /* NB: wavelan_close will be called, but too late, so we are @@ -4748,36 +4717,22 @@ static int wavelan_suspend(struct pcmcia_device *p_dev) /* Stop receiving new messages and wait end of transmission */ wv_ru_stop(dev); + if (link->open) + netif_device_detach(dev); + /* Power down the module */ hacr_write(dev->base_addr, HACR_DEFAULT & (~HACR_PWR_STAT)); - /* The card is now suspended */ - link->state |= DEV_SUSPEND; - - if(link->state & DEV_CONFIG) - { - if(link->open) - netif_device_detach(dev); - pcmcia_release_configuration(link->handle); - } - return 0; } -static int wavelan_resume(struct pcmcia_device *p_dev) +static int wavelan_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device * dev = (struct net_device *) link->priv; - link->state &= ~DEV_SUSPEND; - if(link->state & DEV_CONFIG) - { - pcmcia_request_configuration(link->handle, &link->conf); - if(link->open) /* If RESET -> True, If RESUME -> False ? */ - { - wv_hw_reset(dev); - netif_device_attach(dev); - } + if (link->open) { + wv_hw_reset(dev); + netif_device_attach(dev); } return 0; @@ -4798,7 +4753,7 @@ static struct pcmcia_driver wavelan_driver = { .drv = { .name = "wavelan_cs", }, - .probe = wavelan_attach, + .probe = wavelan_probe, .remove = wavelan_detach, .id_table = wavelan_ids, .suspend = wavelan_suspend, diff --git a/drivers/net/wireless/wavelan_cs.p.h b/drivers/net/wireless/wavelan_cs.p.h index 451f6271dcbc..c65fe7a391ec 100644 --- a/drivers/net/wireless/wavelan_cs.p.h +++ b/drivers/net/wireless/wavelan_cs.p.h @@ -602,7 +602,7 @@ struct net_local dev_node_t node; /* ???? What is this stuff ???? */ struct net_device * dev; /* Reverse link... */ spinlock_t spinlock; /* Serialize access to the hardware (SMP) */ - dev_link_t * link; /* pcmcia structure */ + struct pcmcia_device * link; /* pcmcia structure */ en_stats stats; /* Ethernet interface statistics */ int nresets; /* Number of hw resets */ u_char configured; /* If it is configured */ @@ -733,9 +733,9 @@ static int static inline void wv_hw_reset(struct net_device *); /* Same, + start receiver unit */ static inline int - wv_pcmcia_config(dev_link_t *); /* Configure the pcmcia interface */ + wv_pcmcia_config(struct pcmcia_device *); /* Configure the pcmcia interface */ static void - wv_pcmcia_release(dev_link_t *);/* Remove a device */ + wv_pcmcia_release(struct pcmcia_device *);/* Remove a device */ /* ---------------------- INTERRUPT HANDLING ---------------------- */ static irqreturn_t wavelan_interrupt(int, /* Interrupt handler */ diff --git a/drivers/net/wireless/wl3501.h b/drivers/net/wireless/wl3501.h index 4303c50c2ab6..65ceb088f700 100644 --- a/drivers/net/wireless/wl3501.h +++ b/drivers/net/wireless/wl3501.h @@ -611,5 +611,6 @@ struct wl3501_card { struct iw_spy_data spy_data; struct iw_public_data wireless_data; struct dev_node_t node; + struct pcmcia_device *p_dev; }; #endif diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c index 48e10b0c7e74..e52a650f6737 100644 --- a/drivers/net/wireless/wl3501_cs.c +++ b/drivers/net/wireless/wl3501_cs.c @@ -103,8 +103,8 @@ module_param(pc_debug, int, 0); * release a socket, in response to card insertion and ejection events. They * are invoked from the wl24 event handler. */ -static void wl3501_config(dev_link_t *link); -static void wl3501_release(dev_link_t *link); +static int wl3501_config(struct pcmcia_device *link); +static void wl3501_release(struct pcmcia_device *link); /* * The dev_info variable is the "key" that is used to match up this @@ -226,17 +226,6 @@ static void iw_copy_mgmt_info_element(struct iw_mgmt_info_element *to, iw_set_mgmt_info_element(from->id, to, from->data, from->len); } -/* - * A linked list of "instances" of the wl24 device. Each actual PCMCIA card - * corresponds to one device instance, and is described by one dev_link_t - * structure (defined in ds.h). - * - * You may not want to use a linked list for this -- for example, the memory - * card driver uses an array of dev_link_t pointers, where minor device numbers - * are used to derive the corresponding array index. - */ -static dev_link_t *wl3501_dev_list; - static inline void wl3501_switch_page(struct wl3501_card *this, u8 page) { wl3501_outb(page, this->base_addr + WL3501_NIC_BSS); @@ -1281,15 +1270,10 @@ static int wl3501_close(struct net_device *dev) struct wl3501_card *this = dev->priv; int rc = -ENODEV; unsigned long flags; - dev_link_t *link; + struct pcmcia_device *link; + link = this->p_dev; spin_lock_irqsave(&this->lock, flags); - /* Check if the device is in wl3501_dev_list */ - for (link = wl3501_dev_list; link; link = link->next) - if (link->priv == dev) - break; - if (!link) - goto out; link->open--; /* Stop wl3501_hard_start_xmit() from now on */ @@ -1301,7 +1285,6 @@ static int wl3501_close(struct net_device *dev) rc = 0; printk(KERN_INFO "%s: WL3501 closed\n", dev->name); -out: spin_unlock_irqrestore(&this->lock, flags); return rc; } @@ -1400,14 +1383,11 @@ static int wl3501_open(struct net_device *dev) int rc = -ENODEV; struct wl3501_card *this = dev->priv; unsigned long flags; - dev_link_t *link; + struct pcmcia_device *link; + link = this->p_dev; spin_lock_irqsave(&this->lock, flags); - /* Check if the device is in wl3501_dev_list */ - for (link = wl3501_dev_list; link; link = link->next) - if (link->priv == dev) - break; - if (!DEV_OK(link)) + if (!pcmcia_dev_present(link)) goto out; netif_device_attach(dev); link->open++; @@ -1497,38 +1477,23 @@ static struct ethtool_ops ops = { * Services. If it has been released, all local data structures are freed. * Otherwise, the structures will be freed when the device is released. */ -static void wl3501_detach(struct pcmcia_device *p_dev) +static void wl3501_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); - dev_link_t **linkp; struct net_device *dev = link->priv; - /* Locate device structure */ - for (linkp = &wl3501_dev_list; *linkp; linkp = &(*linkp)->next) - if (*linkp == link) - break; - if (!*linkp) - goto out; - /* If the device is currently configured and active, we won't actually * delete it yet. Instead, it is marked so that when the release() * function is called, that will trigger a proper detach(). */ - if (link->state & DEV_CONFIG) { - while (link->open > 0) - wl3501_close(dev); + while (link->open > 0) + wl3501_close(dev); - netif_device_detach(dev); - wl3501_release(link); - } - - /* Unlink device structure, free pieces */ - *linkp = link->next; + netif_device_detach(dev); + wl3501_release(link); if (link->priv) free_netdev(link->priv); - kfree(link); -out: + return; } @@ -1953,33 +1918,26 @@ static const struct iw_handler_def wl3501_handler_def = { * The dev_link structure is initialized, but we don't actually configure the * card at this point -- we wait until we receive a card insertion event. */ -static int wl3501_attach(struct pcmcia_device *p_dev) +static int wl3501_probe(struct pcmcia_device *p_dev) { - dev_link_t *link; struct net_device *dev; struct wl3501_card *this; - /* Initialize the dev_link_t structure */ - link = kzalloc(sizeof(*link), GFP_KERNEL); - if (!link) - return -ENOMEM; - /* The io structure describes IO port mapping */ - link->io.NumPorts1 = 16; - link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; - link->io.IOAddrLines = 5; + p_dev->io.NumPorts1 = 16; + p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + p_dev->io.IOAddrLines = 5; /* Interrupt setup */ - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; - link->irq.IRQInfo1 = IRQ_LEVEL_ID; - link->irq.Handler = wl3501_interrupt; + p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; + p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID; + p_dev->irq.Handler = wl3501_interrupt; /* General socket configuration */ - link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; - link->conf.IntType = INT_MEMORY_AND_IO; - link->conf.ConfigIndex = 1; - link->conf.Present = PRESENT_OPTION; + p_dev->conf.Attributes = CONF_ENABLE_IRQ; + p_dev->conf.IntType = INT_MEMORY_AND_IO; + p_dev->conf.ConfigIndex = 1; + p_dev->conf.Present = PRESENT_OPTION; dev = alloc_etherdev(sizeof(struct wl3501_card)); if (!dev) @@ -1992,22 +1950,15 @@ static int wl3501_attach(struct pcmcia_device *p_dev) dev->get_stats = wl3501_get_stats; this = dev->priv; this->wireless_data.spy_data = &this->spy_data; + this->p_dev = p_dev; dev->wireless_data = &this->wireless_data; dev->wireless_handlers = (struct iw_handler_def *)&wl3501_handler_def; SET_ETHTOOL_OPS(dev, &ops); netif_stop_queue(dev); - link->priv = link->irq.Instance = dev; + p_dev->priv = p_dev->irq.Instance = dev; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - wl3501_config(link); - - return 0; + return wl3501_config(p_dev); out_link: - kfree(link); - link = NULL; return -ENOMEM; } @@ -2022,11 +1973,10 @@ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) * received, to configure the PCMCIA socket, and to make the ethernet device * available to the system. */ -static void wl3501_config(dev_link_t *link) +static int wl3501_config(struct pcmcia_device *link) { tuple_t tuple; cisparse_t parse; - client_handle_t handle = link->handle; struct net_device *dev = link->priv; int i = 0, j, last_fn, last_ret; unsigned char bf[64]; @@ -2035,18 +1985,15 @@ static void wl3501_config(dev_link_t *link) /* This reads the card's CONFIG tuple to find its config registers. */ tuple.Attributes = 0; tuple.DesiredTuple = CISTPL_CONFIG; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); tuple.TupleData = bf; tuple.TupleDataMax = sizeof(bf); tuple.TupleOffset = 0; - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; - /* Configure card */ - link->state |= DEV_CONFIG; - /* Try allocating IO ports. This tries a few fixed addresses. If you * want, you can also read the card's config table to pick addresses -- * see the serial driver for an example. */ @@ -2056,28 +2003,28 @@ static void wl3501_config(dev_link_t *link) * 0x200-0x2ff, and so on, because this seems safer */ link->io.BasePort1 = j; link->io.BasePort2 = link->io.BasePort1 + 0x10; - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) break; } if (i != CS_SUCCESS) { - cs_error(link->handle, RequestIO, i); + cs_error(link, RequestIO, i); goto failed; } /* Now allocate an interrupt line. Note that this does not actually * assign a handler to the interrupt. */ - CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); /* This actually configures the PCMCIA socket -- setting up the I/O * windows and the interrupt mapping. */ - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); dev->irq = link->irq.AssignedIRQ; dev->base_addr = link->io.BasePort1; - SET_NETDEV_DEV(dev, &handle_to_dev(handle)); + SET_NETDEV_DEV(dev, &handle_to_dev(link)); if (register_netdev(dev)) { printk(KERN_NOTICE "wl3501_cs: register_netdev() failed\n"); goto failed; @@ -2088,10 +2035,9 @@ static void wl3501_config(dev_link_t *link) this = dev->priv; /* * At this point, the dev_node_t structure(s) should be initialized and - * arranged in a linked list at link->dev. + * arranged in a linked list at link->dev_node. */ - link->dev = &this->node; - link->state &= ~DEV_CONFIG_PENDING; + link->dev_node = &this->node; this->base_addr = dev->base_addr; @@ -2127,13 +2073,13 @@ static void wl3501_config(dev_link_t *link) spin_lock_init(&this->lock); init_waitqueue_head(&this->wait); netif_start_queue(dev); - goto out; + return 0; + cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); failed: wl3501_release(link); -out: - return; + return -ENODEV; } /** @@ -2144,52 +2090,36 @@ out: * and release the PCMCIA configuration. If the device is still open, this * will be postponed until it is closed. */ -static void wl3501_release(dev_link_t *link) +static void wl3501_release(struct pcmcia_device *link) { struct net_device *dev = link->priv; /* Unlink the device chain */ - if (link->dev) { + if (link->dev_node) unregister_netdev(dev); - link->dev = NULL; - } - /* Don't bother checking to see if these succeed or not */ - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - link->state &= ~DEV_CONFIG; + pcmcia_disable_device(link); } -static int wl3501_suspend(struct pcmcia_device *p_dev) +static int wl3501_suspend(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; - link->state |= DEV_SUSPEND; - wl3501_pwr_mgmt(dev->priv, WL3501_SUSPEND); - if (link->state & DEV_CONFIG) { - if (link->open) - netif_device_detach(dev); - pcmcia_release_configuration(link->handle); - } + if (link->open) + netif_device_detach(dev); return 0; } -static int wl3501_resume(struct pcmcia_device *p_dev) +static int wl3501_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct net_device *dev = link->priv; wl3501_pwr_mgmt(dev->priv, WL3501_RESUME); - if (link->state & DEV_CONFIG) { - pcmcia_request_configuration(link->handle, &link->conf); - if (link->open) { - wl3501_reset(dev); - netif_device_attach(dev); - } + if (link->open) { + wl3501_reset(dev); + netif_device_attach(dev); } return 0; @@ -2207,7 +2137,7 @@ static struct pcmcia_driver wl3501_driver = { .drv = { .name = "wl3501_cs", }, - .probe = wl3501_attach, + .probe = wl3501_probe, .remove = wl3501_detach, .id_table = wl3501_ids, .suspend = wl3501_suspend, @@ -2221,9 +2151,7 @@ static int __init wl3501_init_module(void) static void __exit wl3501_exit_module(void) { - dprintk(0, ": unloading"); pcmcia_unregister_driver(&wl3501_driver); - BUG_ON(wl3501_dev_list != NULL); } module_init(wl3501_init_module); diff --git a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c index 75d56bfef0ee..fd0f43b7db5b 100644 --- a/drivers/net/yellowfin.c +++ b/drivers/net/yellowfin.c @@ -1441,8 +1441,7 @@ static void __devexit yellowfin_remove_one (struct pci_dev *pdev) struct net_device *dev = pci_get_drvdata(pdev); struct yellowfin_private *np; - if (!dev) - BUG(); + BUG_ON(!dev); np = netdev_priv(dev); pci_free_consistent(pdev, STATUS_TOTAL_SIZE, np->tx_status, diff --git a/drivers/parisc/ccio-dma.c b/drivers/parisc/ccio-dma.c index 93f8a8fa8890..a5d826237b26 100644 --- a/drivers/parisc/ccio-dma.c +++ b/drivers/parisc/ccio-dma.c @@ -1560,7 +1560,7 @@ static int ccio_probe(struct parisc_device *dev) *ioc_p = ioc; ioc->hw_path = dev->hw_path; - ioc->ioc_regs = ioremap(dev->hpa.start, 4096); + ioc->ioc_regs = ioremap_nocache(dev->hpa.start, 4096); ccio_ioc_init(ioc); ccio_init_resources(ioc); hppa_dma_ops = &ccio_ops; diff --git a/drivers/parisc/dino.c b/drivers/parisc/dino.c index 3d1a7f98c676..6e8ed0c81a6c 100644 --- a/drivers/parisc/dino.c +++ b/drivers/parisc/dino.c @@ -5,6 +5,7 @@ ** (c) Copyright 1999 SuSE GmbH ** (c) Copyright 1999,2000 Hewlett-Packard Company ** (c) Copyright 2000 Grant Grundler +** (c) Copyright 2006 Helge Deller ** ** 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 @@ -785,7 +786,7 @@ dino_bridge_init(struct dino_device *dino_dev, const char *name) if((io_addr & (1 << i)) == 0) continue; - start = (unsigned long)(signed int)(0xf0000000 | (i << 23)); + start = F_EXTEND(0xf0000000UL) | (i << 23); end = start + 8 * 1024 * 1024 - 1; DBG("DINO RANGE %d is at 0x%lx-0x%lx\n", count, @@ -996,7 +997,7 @@ static int __init dino_probe(struct parisc_device *dev) } dino_dev->hba.dev = dev; - dino_dev->hba.base_addr = ioremap(hpa, 4096); + dino_dev->hba.base_addr = ioremap_nocache(hpa, 4096); dino_dev->hba.lmmio_space_offset = 0; /* CPU addrs == bus addrs */ spin_lock_init(&dino_dev->dinosaur_pen); dino_dev->hba.iommu = ccio_get_iommu(dev); diff --git a/drivers/parisc/eisa.c b/drivers/parisc/eisa.c index 3d94d86c1c9f..9d3bd15bf53b 100644 --- a/drivers/parisc/eisa.c +++ b/drivers/parisc/eisa.c @@ -366,7 +366,7 @@ static int __devinit eisa_probe(struct parisc_device *dev) eisa_dev.eeprom_addr = MIRAGE_EEPROM_BASE_ADDR; } } - eisa_eeprom_addr = ioremap(eisa_dev.eeprom_addr, HPEE_MAX_LENGTH); + eisa_eeprom_addr = ioremap_nocache(eisa_dev.eeprom_addr, HPEE_MAX_LENGTH); result = eisa_enumerator(eisa_dev.eeprom_addr, &eisa_dev.hba.io_space, &eisa_dev.hba.lmmio_space); init_eisa_pic(); diff --git a/drivers/parisc/iosapic.c b/drivers/parisc/iosapic.c index 8d7a36392eb8..7a458d5bc751 100644 --- a/drivers/parisc/iosapic.c +++ b/drivers/parisc/iosapic.c @@ -879,7 +879,7 @@ void *iosapic_register(unsigned long hpa) return NULL; } - isi->addr = ioremap(hpa, 4096); + isi->addr = ioremap_nocache(hpa, 4096); isi->isi_hpa = hpa; isi->isi_version = iosapic_rd_version(isi); isi->isi_num_vectors = IOSAPIC_IRDT_MAX_ENTRY(isi->isi_version) + 1; diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c index e8a2a4a852f5..3fe4a77fa16a 100644 --- a/drivers/parisc/lba_pci.c +++ b/drivers/parisc/lba_pci.c @@ -1213,7 +1213,7 @@ lba_pat_resources(struct parisc_device *pa_dev, struct lba_device *lba_dev) ** Postable I/O port space is per PCI host adapter. ** base of 64MB PIOP region */ - lba_dev->iop_base = ioremap(p->start, 64 * 1024 * 1024); + lba_dev->iop_base = ioremap_nocache(p->start, 64 * 1024 * 1024); sprintf(lba_dev->hba.io_name, "PCI%02lx Ports", lba_dev->hba.bus_num.start); @@ -1525,7 +1525,7 @@ lba_driver_probe(struct parisc_device *dev) u32 func_class; void *tmp_obj; char *version; - void __iomem *addr = ioremap(dev->hpa.start, 4096); + void __iomem *addr = ioremap_nocache(dev->hpa.start, 4096); /* Read HW Rev First */ func_class = READ_REG32(addr + LBA_FCLASS); @@ -1619,7 +1619,7 @@ lba_driver_probe(struct parisc_device *dev) } else { if (!astro_iop_base) { /* Sprockets PDC uses NPIOP region */ - astro_iop_base = ioremap(LBA_PORT_BASE, 64 * 1024); + astro_iop_base = ioremap_nocache(LBA_PORT_BASE, 64 * 1024); pci_port = &lba_astro_port_ops; } @@ -1700,7 +1700,7 @@ void __init lba_init(void) */ void lba_set_iregs(struct parisc_device *lba, u32 ibase, u32 imask) { - void __iomem * base_addr = ioremap(lba->hpa.start, 4096); + void __iomem * base_addr = ioremap_nocache(lba->hpa.start, 4096); imask <<= 2; /* adjust for hints - 2 more bits */ diff --git a/drivers/parisc/pdc_stable.c b/drivers/parisc/pdc_stable.c index a28e17898fbd..4e53be9c03ab 100644 --- a/drivers/parisc/pdc_stable.c +++ b/drivers/parisc/pdc_stable.c @@ -4,9 +4,8 @@ * Copyright (C) 2005-2006 Thibaut VARENE * * 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. + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/drivers/parisc/sba_iommu.c b/drivers/parisc/sba_iommu.c index 0821747e44cf..42b32ff2fca6 100644 --- a/drivers/parisc/sba_iommu.c +++ b/drivers/parisc/sba_iommu.c @@ -1642,9 +1642,9 @@ sba_ioc_init(struct parisc_device *sba, struct ioc *ioc, int ioc_num) ** **************************************************************************/ -static void __iomem *ioc_remap(struct sba_device *sba_dev, int offset) +static void __iomem *ioc_remap(struct sba_device *sba_dev, unsigned int offset) { - return ioremap(sba_dev->dev->hpa.start + offset, SBA_FUNC_SIZE); + return ioremap_nocache(sba_dev->dev->hpa.start + offset, SBA_FUNC_SIZE); } static void sba_hw_init(struct sba_device *sba_dev) @@ -2040,7 +2040,7 @@ sba_driver_callback(struct parisc_device *dev) u32 func_class; int i; char *version; - void __iomem *sba_addr = ioremap(dev->hpa.start, SBA_FUNC_SIZE); + void __iomem *sba_addr = ioremap_nocache(dev->hpa.start, SBA_FUNC_SIZE); struct proc_dir_entry *info_entry, *bitmap_entry, *root; sba_dump_ranges(sba_addr); diff --git a/drivers/parisc/superio.c b/drivers/parisc/superio.c index ad6d3b28a3a6..719b863bc20e 100644 --- a/drivers/parisc/superio.c +++ b/drivers/parisc/superio.c @@ -12,6 +12,7 @@ * (C) Copyright 2001 John Marvin * (C) Copyright 2003 Grant Grundler * (C) Copyright 2005 Kyle McMartin + * (C) Copyright 2006 Helge Deller * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -388,43 +389,34 @@ int superio_fixup_irq(struct pci_dev *pcidev) return local_irq; } -static struct uart_port serial[] = { - { - .iotype = UPIO_PORT, - .line = 0, - .type = PORT_16550A, - .uartclk = 115200*16, - .fifosize = 16, - }, - { - .iotype = UPIO_PORT, - .line = 1, - .type = PORT_16550A, - .uartclk = 115200*16, - .fifosize = 16, - } -}; - static void __devinit superio_serial_init(void) { #ifdef CONFIG_SERIAL_8250 int retval; - - serial[0].iobase = sio_dev.sp1_base; - serial[0].irq = SP1_IRQ; - spin_lock_init(&serial[0].lock); + struct uart_port serial_port; - retval = early_serial_setup(&serial[0]); + memset(&serial_port, 0, sizeof(serial_port)); + serial_port.iotype = UPIO_PORT; + serial_port.type = PORT_16550A; + serial_port.uartclk = 115200*16; + serial_port.fifosize = 16; + spin_lock_init(&serial_port.lock); + + /* serial port #1 */ + serial_port.iobase = sio_dev.sp1_base; + serial_port.irq = SP1_IRQ; + serial_port.line = 0; + retval = early_serial_setup(&serial_port); if (retval < 0) { printk(KERN_WARNING PFX "Register Serial #0 failed.\n"); return; } - serial[1].iobase = sio_dev.sp2_base; - serial[1].irq = SP2_IRQ; - spin_lock_init(&serial[1].lock); - retval = early_serial_setup(&serial[1]); - + /* serial port #2 */ + serial_port.iobase = sio_dev.sp2_base; + serial_port.irq = SP2_IRQ; + serial_port.line = 1; + retval = early_serial_setup(&serial_port); if (retval < 0) printk(KERN_WARNING PFX "Register Serial #1 failed.\n"); #endif /* CONFIG_SERIAL_8250 */ diff --git a/drivers/parport/parport_cs.c b/drivers/parport/parport_cs.c index 158d92563259..b953d5907c05 100644 --- a/drivers/parport/parport_cs.c +++ b/drivers/parport/parport_cs.c @@ -81,15 +81,15 @@ static char *version = #define FORCE_EPP_MODE 0x08 typedef struct parport_info_t { - dev_link_t link; + struct pcmcia_device *p_dev; int ndev; dev_node_t node; struct parport *port; } parport_info_t; static void parport_detach(struct pcmcia_device *p_dev); -static void parport_config(dev_link_t *link); -static void parport_cs_release(dev_link_t *); +static int parport_config(struct pcmcia_device *link); +static void parport_cs_release(struct pcmcia_device *); /*====================================================================== @@ -99,10 +99,9 @@ static void parport_cs_release(dev_link_t *); ======================================================================*/ -static int parport_attach(struct pcmcia_device *p_dev) +static int parport_probe(struct pcmcia_device *link) { parport_info_t *info; - dev_link_t *link; DEBUG(0, "parport_attach()\n"); @@ -110,23 +109,17 @@ static int parport_attach(struct pcmcia_device *p_dev) info = kmalloc(sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; memset(info, 0, sizeof(*info)); - link = &info->link; link->priv = info; + link->priv = info; + info->p_dev = link; link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; link->io.Attributes2 = IO_DATA_PATH_WIDTH_8; link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - parport_config(link); - - return 0; + return parport_config(link); } /* parport_attach */ /*====================================================================== @@ -138,14 +131,11 @@ static int parport_attach(struct pcmcia_device *p_dev) ======================================================================*/ -static void parport_detach(struct pcmcia_device *p_dev) +static void parport_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); - DEBUG(0, "parport_detach(0x%p)\n", link); - if (link->state & DEV_CONFIG) - parport_cs_release(link); + parport_cs_release(link); kfree(link->priv); } /* parport_detach */ @@ -161,14 +151,12 @@ static void parport_detach(struct pcmcia_device *p_dev) #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) -void parport_config(dev_link_t *link) +static int parport_config(struct pcmcia_device *link) { - client_handle_t handle = link->handle; parport_info_t *info = link->priv; tuple_t tuple; u_short buf[128]; cisparse_t parse; - config_info_t conf; cistpl_cftable_entry_t *cfg = &parse.cftable_entry; cistpl_cftable_entry_t dflt = { 0 }; struct parport *p; @@ -180,24 +168,18 @@ void parport_config(dev_link_t *link) tuple.TupleOffset = 0; tuple.TupleDataMax = 255; tuple.Attributes = 0; tuple.DesiredTuple = CISTPL_CONFIG; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; - - /* Configure card */ - link->state |= DEV_CONFIG; - /* Not sure if this is right... look up the current Vcc */ - CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf)); - tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; tuple.Attributes = 0; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); while (1) { - if (pcmcia_get_tuple_data(handle, &tuple) != 0 || - pcmcia_parse_tuple(handle, &tuple, &parse) != 0) + if (pcmcia_get_tuple_data(link, &tuple) != 0 || + pcmcia_parse_tuple(link, &tuple, &parse) != 0) goto next_entry; if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) { @@ -212,7 +194,7 @@ void parport_config(dev_link_t *link) link->io.BasePort2 = io->win[1].base; link->io.NumPorts2 = io->win[1].len; } - if (pcmcia_request_io(link->handle, &link->io) != 0) + if (pcmcia_request_io(link, &link->io) != 0) goto next_entry; /* If we've got this far, we're done */ break; @@ -220,15 +202,12 @@ void parport_config(dev_link_t *link) next_entry: if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg; - CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple)); + CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple)); } - CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq)); - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); - release_region(link->io.BasePort1, link->io.NumPorts1); - if (link->io.NumPorts2) - release_region(link->io.BasePort2, link->io.NumPorts2); p = parport_pc_probe_port(link->io.BasePort1, link->io.BasePort2, link->irq.AssignedIRQ, PARPORT_DMA_NONE, NULL); @@ -247,17 +226,15 @@ void parport_config(dev_link_t *link) info->node.minor = p->number; info->port = p; strcpy(info->node.dev_name, p->name); - link->dev = &info->node; + link->dev_node = &info->node; + + return 0; - link->state &= ~DEV_CONFIG_PENDING; - return; - cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); failed: parport_cs_release(link); - link->state &= ~DEV_CONFIG_PENDING; - + return -ENODEV; } /* parport_config */ /*====================================================================== @@ -268,53 +245,21 @@ failed: ======================================================================*/ -void parport_cs_release(dev_link_t *link) +void parport_cs_release(struct pcmcia_device *link) { - parport_info_t *info = link->priv; - - DEBUG(0, "parport_release(0x%p)\n", link); + parport_info_t *info = link->priv; - if (info->ndev) { - struct parport *p = info->port; - parport_pc_unregister_port(p); - request_region(link->io.BasePort1, link->io.NumPorts1, - info->node.dev_name); - if (link->io.NumPorts2) - request_region(link->io.BasePort2, link->io.NumPorts2, - info->node.dev_name); - } - info->ndev = 0; - link->dev = NULL; - - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - - link->state &= ~DEV_CONFIG; + DEBUG(0, "parport_release(0x%p)\n", link); + if (info->ndev) { + struct parport *p = info->port; + parport_pc_unregister_port(p); + } + info->ndev = 0; + + pcmcia_disable_device(link); } /* parport_cs_release */ -static int parport_suspend(struct pcmcia_device *dev) -{ - dev_link_t *link = dev_to_instance(dev); - - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) - pcmcia_release_configuration(link->handle); - - return 0; -} - -static int parport_resume(struct pcmcia_device *dev) -{ - dev_link_t *link = dev_to_instance(dev); - - link->state &= ~DEV_SUSPEND; - if (DEV_OK(link)) - pcmcia_request_configuration(link->handle, &link->conf); - - return 0; -} static struct pcmcia_device_id parport_ids[] = { PCMCIA_DEVICE_FUNC_ID(3), @@ -328,11 +273,9 @@ static struct pcmcia_driver parport_cs_driver = { .drv = { .name = "parport_cs", }, - .probe = parport_attach, + .probe = parport_probe, .remove = parport_detach, .id_table = parport_ids, - .suspend = parport_suspend, - .resume = parport_resume, }; static int __init init_parport_cs(void) diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c index d121644646b9..98b83a85c60e 100644 --- a/drivers/parport/parport_serial.c +++ b/drivers/parport/parport_serial.c @@ -100,8 +100,6 @@ static struct pci_device_id parport_serial_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo }, { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo }, - { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9835, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo }, { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9845, PCI_ANY_ID, PCI_ANY_ID, 0, 0, netmos_9xx5_combo }, { PCI_VENDOR_ID_NETMOS, PCI_DEVICE_ID_NETMOS_9855, diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig index 1f4ad0e7836e..cba6c9eef28e 100644 --- a/drivers/pcmcia/Kconfig +++ b/drivers/pcmcia/Kconfig @@ -263,6 +263,13 @@ config OMAP_CF Say Y here to support the CompactFlash controller on OMAP. Note that this doesn't support "True IDE" mode. +config AT91_CF + tristate "AT91 CompactFlash Controller" + depends on PCMCIA && ARCH_AT91RM9200 + help + Say Y here to support the CompactFlash controller on AT91 chips. + Or choose M to compile the driver as a module named "at91_cf". + config PCCARD_NONSTATIC tristate diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile index bcecf5133b7e..4276965517f2 100644 --- a/drivers/pcmcia/Makefile +++ b/drivers/pcmcia/Makefile @@ -10,7 +10,7 @@ pcmcia_core-y += cs.o cistpl.o rsrc_mgr.o socket_sysfs.o pcmcia_core-$(CONFIG_CARDBUS) += cardbus.o obj-$(CONFIG_PCCARD) += pcmcia_core.o -pcmcia-y += ds.o pcmcia_compat.o pcmcia_resource.o +pcmcia-y += ds.o pcmcia_resource.o pcmcia-$(CONFIG_PCMCIA_IOCTL) += pcmcia_ioctl.o obj-$(CONFIG_PCMCIA) += pcmcia.o @@ -36,6 +36,7 @@ obj-$(CONFIG_PCMCIA_AU1X00) += au1x00_ss.o obj-$(CONFIG_PCMCIA_VRC4171) += vrc4171_card.o obj-$(CONFIG_PCMCIA_VRC4173) += vrc4173_cardu.o obj-$(CONFIG_OMAP_CF) += omap_cf.o +obj-$(CONFIG_AT91_CF) += at91_cf.o sa11xx_core-y += soc_common.o sa11xx_base.o pxa2xx_core-y += soc_common.o pxa2xx_base.o diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c new file mode 100644 index 000000000000..67cc5f7d0c90 --- /dev/null +++ b/drivers/pcmcia/at91_cf.c @@ -0,0 +1,365 @@ +/* + * at91_cf.c -- AT91 CompactFlash controller driver + * + * Copyright (C) 2005 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + + +#define CF_SIZE 0x30000000 /* CS5+CS6: unavailable */ + +/* + * A0..A10 work in each range; A23 indicates I/O space; A25 is CFRNW; + * some other bit in {A24,A22..A11} is nREG to flag memory access + * (vs attributes). So more than 2KB/region would just be waste. + */ +#define CF_ATTR_PHYS (AT91_CF_BASE) +#define CF_IO_PHYS (AT91_CF_BASE + (1 << 23)) +#define CF_MEM_PHYS (AT91_CF_BASE + 0x017ff800) + +/*--------------------------------------------------------------------------*/ + +static const char driver_name[] = "at91_cf"; + +struct at91_cf_socket { + struct pcmcia_socket socket; + + unsigned present:1; + + struct platform_device *pdev; + struct at91_cf_data *board; +}; + +#define SZ_2K (2 * SZ_1K) + +static inline int at91_cf_present(struct at91_cf_socket *cf) +{ + return !at91_get_gpio_value(cf->board->det_pin); +} + +/*--------------------------------------------------------------------------*/ + +static int at91_cf_ss_init(struct pcmcia_socket *s) +{ + return 0; +} + +static irqreturn_t at91_cf_irq(int irq, void *_cf, struct pt_regs *r) +{ + struct at91_cf_socket *cf = (struct at91_cf_socket *) _cf; + + if (irq == cf->board->det_pin) { + unsigned present = at91_cf_present(cf); + + /* kick pccard as needed */ + if (present != cf->present) { + cf->present = present; + pr_debug("%s: card %s\n", driver_name, present ? "present" : "gone"); + pcmcia_parse_events(&cf->socket, SS_DETECT); + } + } + + return IRQ_HANDLED; +} + +static int at91_cf_get_status(struct pcmcia_socket *s, u_int *sp) +{ + struct at91_cf_socket *cf; + + if (!sp) + return -EINVAL; + + cf = container_of(s, struct at91_cf_socket, socket); + + /* NOTE: we assume 3VCARD, not XVCARD... */ + if (at91_cf_present(cf)) { + int rdy = cf->board->irq_pin; /* RDY/nIRQ */ + int vcc = cf->board->vcc_pin; + + *sp = SS_DETECT | SS_3VCARD; + if (!rdy || at91_get_gpio_value(rdy)) + *sp |= SS_READY; + if (!vcc || at91_get_gpio_value(vcc)) + *sp |= SS_POWERON; + } else + *sp = 0; + + return 0; +} + +static int at91_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s) +{ + struct at91_cf_socket *cf; + + cf = container_of(sock, struct at91_cf_socket, socket); + + /* switch Vcc if needed and possible */ + if (cf->board->vcc_pin) { + switch (s->Vcc) { + case 0: + at91_set_gpio_value(cf->board->vcc_pin, 0); + break; + case 33: + at91_set_gpio_value(cf->board->vcc_pin, 1); + break; + default: + return -EINVAL; + } + } + + /* toggle reset if needed */ + at91_set_gpio_value(cf->board->rst_pin, s->flags & SS_RESET); + + pr_debug("%s: Vcc %d, io_irq %d, flags %04x csc %04x\n", + driver_name, s->Vcc, s->io_irq, s->flags, s->csc_mask); + + return 0; +} + +static int at91_cf_ss_suspend(struct pcmcia_socket *s) +{ + return at91_cf_set_socket(s, &dead_socket); +} + +/* we already mapped the I/O region */ +static int at91_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io) +{ + struct at91_cf_socket *cf; + u32 csr; + + cf = container_of(s, struct at91_cf_socket, socket); + io->flags &= (MAP_ACTIVE | MAP_16BIT | MAP_AUTOSZ); + + /* + * Use 16 bit accesses unless/until we need 8-bit i/o space. + * Always set CSR4 ... PCMCIA won't always unmap things. + */ + csr = at91_sys_read(AT91_SMC_CSR(4)) & ~AT91_SMC_DBW; + + /* + * NOTE: this CF controller ignores IOIS16, so we can't really do + * MAP_AUTOSZ. The 16bit mode allows single byte access on either + * D0-D7 (even addr) or D8-D15 (odd), so it's close enough for many + * purposes (and handles ide-cs). + * + * The 8bit mode is needed for odd byte access on D0-D7. It seems + * some cards only like that way to get at the odd byte, despite + * CF 3.0 spec table 35 also giving the D8-D15 option. + */ + if (!(io->flags & (MAP_16BIT|MAP_AUTOSZ))) { + csr |= AT91_SMC_DBW_8; + pr_debug("%s: 8bit i/o bus\n", driver_name); + } else { + csr |= AT91_SMC_DBW_16; + pr_debug("%s: 16bit i/o bus\n", driver_name); + } + at91_sys_write(AT91_SMC_CSR(4), csr); + + io->start = cf->socket.io_offset; + io->stop = io->start + SZ_2K - 1; + + return 0; +} + +/* pcmcia layer maps/unmaps mem regions */ +static int at91_cf_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *map) +{ + struct at91_cf_socket *cf; + + if (map->card_start) + return -EINVAL; + + cf = container_of(s, struct at91_cf_socket, socket); + + map->flags &= MAP_ACTIVE|MAP_ATTRIB|MAP_16BIT; + if (map->flags & MAP_ATTRIB) + map->static_start = CF_ATTR_PHYS; + else + map->static_start = CF_MEM_PHYS; + + return 0; +} + +static struct pccard_operations at91_cf_ops = { + .init = at91_cf_ss_init, + .suspend = at91_cf_ss_suspend, + .get_status = at91_cf_get_status, + .set_socket = at91_cf_set_socket, + .set_io_map = at91_cf_set_io_map, + .set_mem_map = at91_cf_set_mem_map, +}; + +/*--------------------------------------------------------------------------*/ + +static int __init at91_cf_probe(struct device *dev) +{ + struct at91_cf_socket *cf; + struct at91_cf_data *board = dev->platform_data; + struct platform_device *pdev = to_platform_device(dev); + unsigned int csa; + int status; + + if (!board || !board->det_pin || !board->rst_pin) + return -ENODEV; + + cf = kcalloc(1, sizeof *cf, GFP_KERNEL); + if (!cf) + return -ENOMEM; + + cf->board = board; + cf->pdev = pdev; + dev_set_drvdata(dev, cf); + + /* CF takes over CS4, CS5, CS6 */ + csa = at91_sys_read(AT91_EBI_CSA); + at91_sys_write(AT91_EBI_CSA, csa | AT91_EBI_CS4A_SMC_COMPACTFLASH); + + /* force poweron defaults for these pins ... */ + (void) at91_set_A_periph(AT91_PIN_PC9, 0); /* A25/CFRNW */ + (void) at91_set_A_periph(AT91_PIN_PC10, 0); /* NCS4/CFCS */ + (void) at91_set_A_periph(AT91_PIN_PC11, 0); /* NCS5/CFCE1 */ + (void) at91_set_A_periph(AT91_PIN_PC12, 0); /* NCS6/CFCE2 */ + + /* nWAIT is _not_ a default setting */ + (void) at91_set_A_periph(AT91_PIN_PC6, 1); /* nWAIT */ + + /* + * Static memory controller timing adjustments. + * REVISIT: these timings are in terms of MCK cycles, so + * when MCK changes (cpufreq etc) so must these values... + */ + at91_sys_write(AT91_SMC_CSR(4), AT91_SMC_ACSS_STD | AT91_SMC_DBW_16 | AT91_SMC_BAT | AT91_SMC_WSEN + | AT91_SMC_NWS_(32) /* wait states */ + | AT91_SMC_RWSETUP_(6) /* setup time */ + | AT91_SMC_RWHOLD_(4) /* hold time */ + ); + + /* must be a GPIO; ergo must trigger on both edges */ + status = request_irq(board->det_pin, at91_cf_irq, + SA_SAMPLE_RANDOM, driver_name, cf); + if (status < 0) + goto fail0; + + /* + * The card driver will request this irq later as needed. + * but it causes lots of "irqNN: nobody cared" messages + * unless we report that we handle everything (sigh). + * (Note: DK board doesn't wire the IRQ pin...) + */ + if (board->irq_pin) { + status = request_irq(board->irq_pin, at91_cf_irq, + SA_SHIRQ, driver_name, cf); + if (status < 0) + goto fail0a; + cf->socket.pci_irq = board->irq_pin; + } + else + cf->socket.pci_irq = NR_IRQS + 1; + + /* pcmcia layer only remaps "real" memory not iospace */ + cf->socket.io_offset = (unsigned long) ioremap(CF_IO_PHYS, SZ_2K); + if (!cf->socket.io_offset) + goto fail1; + + /* reserve CS4, CS5, and CS6 regions; but use just CS4 */ + if (!request_mem_region(AT91_CF_BASE, CF_SIZE, driver_name)) + goto fail1; + + pr_info("%s: irqs det #%d, io #%d\n", driver_name, + board->det_pin, board->irq_pin); + + cf->socket.owner = THIS_MODULE; + cf->socket.dev.dev = dev; + cf->socket.ops = &at91_cf_ops; + cf->socket.resource_ops = &pccard_static_ops; + cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP + | SS_CAP_MEM_ALIGN; + cf->socket.map_size = SZ_2K; + cf->socket.io[0].NumPorts = SZ_2K; + + status = pcmcia_register_socket(&cf->socket); + if (status < 0) + goto fail2; + + return 0; + +fail2: + iounmap((void __iomem *) cf->socket.io_offset); + release_mem_region(AT91_CF_BASE, CF_SIZE); +fail1: + if (board->irq_pin) + free_irq(board->irq_pin, cf); +fail0a: + free_irq(board->det_pin, cf); +fail0: + at91_sys_write(AT91_EBI_CSA, csa); + kfree(cf); + return status; +} + +static int __exit at91_cf_remove(struct device *dev) +{ + struct at91_cf_socket *cf = dev_get_drvdata(dev); + unsigned int csa; + + pcmcia_unregister_socket(&cf->socket); + free_irq(cf->board->irq_pin, cf); + free_irq(cf->board->det_pin, cf); + iounmap((void __iomem *) cf->socket.io_offset); + release_mem_region(AT91_CF_BASE, CF_SIZE); + + csa = at91_sys_read(AT91_EBI_CSA); + at91_sys_write(AT91_EBI_CSA, csa & ~AT91_EBI_CS4A); + + kfree(cf); + return 0; +} + +static struct device_driver at91_cf_driver = { + .name = (char *) driver_name, + .bus = &platform_bus_type, + .probe = at91_cf_probe, + .remove = __exit_p(at91_cf_remove), + .suspend = pcmcia_socket_dev_suspend, + .resume = pcmcia_socket_dev_resume, +}; + +/*--------------------------------------------------------------------------*/ + +static int __init at91_cf_init(void) +{ + return driver_register(&at91_cf_driver); +} +module_init(at91_cf_init); + +static void __exit at91_cf_exit(void) +{ + driver_unregister(&at91_cf_driver); +} +module_exit(at91_cf_exit); + +MODULE_DESCRIPTION("AT91 Compact Flash Driver"); +MODULE_AUTHOR("David Brownell"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c index 120fa8da6392..912c03e5eb0a 100644 --- a/drivers/pcmcia/cistpl.c +++ b/drivers/pcmcia/cistpl.c @@ -12,7 +12,6 @@ * (C) 1999 David A. Hinds */ -#include #include #include #include diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c index 613f2f1fbfdd..3162998579c1 100644 --- a/drivers/pcmcia/cs.c +++ b/drivers/pcmcia/cs.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -111,9 +110,9 @@ int pcmcia_socket_dev_suspend(struct device *dev, pm_message_t state) list_for_each_entry(socket, &pcmcia_socket_list, socket_list) { if (socket->dev.dev != dev) continue; - down(&socket->skt_sem); + mutex_lock(&socket->skt_mutex); socket_suspend(socket); - up(&socket->skt_sem); + mutex_unlock(&socket->skt_mutex); } up_read(&pcmcia_socket_list_rwsem); @@ -129,9 +128,9 @@ int pcmcia_socket_dev_resume(struct device *dev) list_for_each_entry(socket, &pcmcia_socket_list, socket_list) { if (socket->dev.dev != dev) continue; - down(&socket->skt_sem); + mutex_lock(&socket->skt_mutex); socket_resume(socket); - up(&socket->skt_sem); + mutex_unlock(&socket->skt_mutex); } up_read(&pcmcia_socket_list_rwsem); @@ -237,7 +236,7 @@ int pcmcia_register_socket(struct pcmcia_socket *socket) init_completion(&socket->socket_released); init_completion(&socket->thread_done); init_waitqueue_head(&socket->thread_wait); - init_MUTEX(&socket->skt_sem); + mutex_init(&socket->skt_mutex); spin_lock_init(&socket->thread_lock); ret = kernel_thread(pccardd, socket, CLONE_KERNEL); @@ -406,8 +405,6 @@ static void socket_shutdown(struct pcmcia_socket *s) cb_free(s); #endif s->functions = 0; - kfree(s->config); - s->config = NULL; s->ops->get_status(s, &status); if (status & SS_POWERON) { @@ -664,7 +661,7 @@ static int pccardd(void *__skt) spin_unlock_irqrestore(&skt->thread_lock, flags); if (events) { - down(&skt->skt_sem); + mutex_lock(&skt->skt_mutex); if (events & SS_DETECT) socket_detect_change(skt); if (events & SS_BATDEAD) @@ -673,7 +670,7 @@ static int pccardd(void *__skt) send_event(skt, CS_EVENT_BATTERY_LOW, CS_EVENT_PRI_LOW); if (events & SS_READY) send_event(skt, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW); - up(&skt->skt_sem); + mutex_unlock(&skt->skt_mutex); continue; } @@ -717,8 +714,8 @@ int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c) { int ret = 0; - /* s->skt_sem also protects s->callback */ - down(&s->skt_sem); + /* s->skt_mutex also protects s->callback */ + mutex_lock(&s->skt_mutex); if (c) { /* registration */ @@ -734,7 +731,7 @@ int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c) } else s->callback = NULL; err: - up(&s->skt_sem); + mutex_unlock(&s->skt_mutex); return ret; } @@ -752,7 +749,7 @@ int pccard_reset_card(struct pcmcia_socket *skt) cs_dbg(skt, 1, "resetting socket\n"); - down(&skt->skt_sem); + mutex_lock(&skt->skt_mutex); do { if (!(skt->state & SOCKET_PRESENT)) { ret = CS_NO_CARD; @@ -781,7 +778,7 @@ int pccard_reset_card(struct pcmcia_socket *skt) ret = CS_SUCCESS; } while (0); - up(&skt->skt_sem); + mutex_unlock(&skt->skt_mutex); return ret; } /* reset_card */ @@ -797,7 +794,7 @@ int pcmcia_suspend_card(struct pcmcia_socket *skt) cs_dbg(skt, 1, "suspending socket\n"); - down(&skt->skt_sem); + mutex_lock(&skt->skt_mutex); do { if (!(skt->state & SOCKET_PRESENT)) { ret = CS_NO_CARD; @@ -814,7 +811,7 @@ int pcmcia_suspend_card(struct pcmcia_socket *skt) } ret = socket_suspend(skt); } while (0); - up(&skt->skt_sem); + mutex_unlock(&skt->skt_mutex); return ret; } /* suspend_card */ @@ -827,7 +824,7 @@ int pcmcia_resume_card(struct pcmcia_socket *skt) cs_dbg(skt, 1, "waking up socket\n"); - down(&skt->skt_sem); + mutex_lock(&skt->skt_mutex); do { if (!(skt->state & SOCKET_PRESENT)) { ret = CS_NO_CARD; @@ -841,7 +838,7 @@ int pcmcia_resume_card(struct pcmcia_socket *skt) if (!ret && skt->callback) skt->callback->resume(skt); } while (0); - up(&skt->skt_sem); + mutex_unlock(&skt->skt_mutex); return ret; } /* resume_card */ @@ -855,7 +852,7 @@ int pcmcia_eject_card(struct pcmcia_socket *skt) cs_dbg(skt, 1, "user eject request\n"); - down(&skt->skt_sem); + mutex_lock(&skt->skt_mutex); do { if (!(skt->state & SOCKET_PRESENT)) { ret = -ENODEV; @@ -871,7 +868,7 @@ int pcmcia_eject_card(struct pcmcia_socket *skt) socket_remove(skt); ret = 0; } while (0); - up(&skt->skt_sem); + mutex_unlock(&skt->skt_mutex); return ret; } /* eject_card */ @@ -884,7 +881,7 @@ int pcmcia_insert_card(struct pcmcia_socket *skt) cs_dbg(skt, 1, "user insert request\n"); - down(&skt->skt_sem); + mutex_lock(&skt->skt_mutex); do { if (skt->state & SOCKET_PRESENT) { ret = -EBUSY; @@ -896,7 +893,7 @@ int pcmcia_insert_card(struct pcmcia_socket *skt) } ret = 0; } while (0); - up(&skt->skt_sem); + mutex_unlock(&skt->skt_mutex); return ret; } /* insert_card */ diff --git a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h index 7b37eba35bf1..d6164cd583fd 100644 --- a/drivers/pcmcia/cs_internal.h +++ b/drivers/pcmcia/cs_internal.h @@ -15,7 +15,7 @@ #ifndef _LINUX_CS_INTERNAL_H #define _LINUX_CS_INTERNAL_H -#include +#include /* Flags in client state */ #define CLIENT_CONFIG_LOCKED 0x0001 @@ -23,7 +23,7 @@ #define CLIENT_IO_REQ 0x0004 #define CLIENT_UNBOUND 0x0008 #define CLIENT_STALE 0x0010 -#define CLIENT_WIN_REQ(i) (0x20<<(i)) +#define CLIENT_WIN_REQ(i) (0x1<<(i)) #define CLIENT_CARDBUS 0x8000 #define REGION_MAGIC 0xE3C9 @@ -31,7 +31,7 @@ typedef struct region_t { u_short region_magic; u_short state; dev_info_t dev_info; - client_handle_t mtd; + struct pcmcia_device *mtd; u_int MediaID; region_info_t info; } region_t; @@ -40,12 +40,12 @@ typedef struct region_t { /* Each card function gets one of these guys */ typedef struct config_t { + struct kref ref; u_int state; u_int Attributes; u_int IntType; u_int ConfigBase; u_char Status, Pin, Copy, Option, ExtStatus; - u_int Present; u_int CardValues; io_req_t io; struct { @@ -95,12 +95,6 @@ static inline void cs_socket_put(struct pcmcia_socket *skt) } } -#define CHECK_SOCKET(s) \ - (((s) >= sockets) || (socket_table[s]->ops == NULL)) - -#define SOCKET(h) (h->socket) -#define CONFIG(h) (&SOCKET(h)->config[(h)->func]) - /* In cardbus.c */ int cb_alloc(struct pcmcia_socket *s); void cb_free(struct pcmcia_socket *s); @@ -133,10 +127,9 @@ extern struct class_interface pccard_sysfs_interface; extern struct rw_semaphore pcmcia_socket_list_rwsem; extern struct list_head pcmcia_socket_list; int pcmcia_get_window(struct pcmcia_socket *s, window_handle_t *handle, int idx, win_req_t *req); -int pccard_get_configuration_info(struct pcmcia_socket *s, unsigned int function, config_info_t *config); +int pccard_get_configuration_info(struct pcmcia_socket *s, struct pcmcia_device *p_dev, config_info_t *config); int pccard_reset_card(struct pcmcia_socket *skt); -int pccard_get_status(struct pcmcia_socket *s, unsigned int function, cs_status_t *status); -int pccard_access_configuration_register(struct pcmcia_socket *s, unsigned int function, conf_reg_t *reg); +int pccard_get_status(struct pcmcia_socket *s, struct pcmcia_device *p_dev, cs_status_t *status); struct pcmcia_callback{ diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index bb96ce1db08c..ae10d1eed65e 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -10,10 +10,9 @@ * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. * * (C) 1999 David A. Hinds - * (C) 2003 - 2005 Dominik Brodowski + * (C) 2003 - 2006 Dominik Brodowski */ -#include #include #include #include @@ -23,6 +22,7 @@ #include #include #include +#include #define IN_CARD_SERVICES #include @@ -343,12 +343,19 @@ void pcmcia_put_dev(struct pcmcia_device *p_dev) put_device(&p_dev->dev); } +static void pcmcia_release_function(struct kref *ref) +{ + struct config_t *c = container_of(ref, struct config_t, ref); + kfree(c); +} + static void pcmcia_release_dev(struct device *dev) { struct pcmcia_device *p_dev = to_pcmcia_dev(dev); ds_dbg(1, "releasing dev %p\n", p_dev); pcmcia_put_socket(p_dev->socket); kfree(p_dev->devname); + kref_put(&p_dev->function_config->ref, pcmcia_release_function); kfree(p_dev); } @@ -377,29 +384,12 @@ static int pcmcia_device_probe(struct device * dev) p_drv = to_pcmcia_drv(dev->driver); s = p_dev->socket; - if ((!p_drv->probe) || (!try_module_get(p_drv->owner))) { + if ((!p_drv->probe) || (!p_dev->function_config) || + (!try_module_get(p_drv->owner))) { ret = -EINVAL; goto put_dev; } - p_dev->state &= ~CLIENT_UNBOUND; - - /* set up the device configuration, if it hasn't been done before */ - if (!s->functions) { - cistpl_longlink_mfc_t mfc; - if (pccard_read_tuple(s, p_dev->func, CISTPL_LONGLINK_MFC, - &mfc) == CS_SUCCESS) - s->functions = mfc.nfn; - else - s->functions = 1; - s->config = kzalloc(sizeof(config_t) * s->functions, - GFP_KERNEL); - if (!s->config) { - ret = -ENOMEM; - goto put_module; - } - } - ret = p_drv->probe(p_dev); if (ret) goto put_module; @@ -425,15 +415,61 @@ static int pcmcia_device_probe(struct device * dev) } +/* + * Removes a PCMCIA card from the device tree and socket list. + */ +static void pcmcia_card_remove(struct pcmcia_socket *s, struct pcmcia_device *leftover) +{ + struct pcmcia_device *p_dev; + struct pcmcia_device *tmp; + unsigned long flags; + + ds_dbg(2, "unbind_request(%d)\n", s->sock); + + + if (!leftover) + s->device_count = 0; + else + s->device_count = 1; + + /* unregister all pcmcia_devices registered with this socket, except leftover */ + list_for_each_entry_safe(p_dev, tmp, &s->devices_list, socket_device_list) { + if (p_dev == leftover) + continue; + + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + list_del(&p_dev->socket_device_list); + p_dev->_removed=1; + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + + device_unregister(&p_dev->dev); + } + + return; +} + + static int pcmcia_device_remove(struct device * dev) { struct pcmcia_device *p_dev; struct pcmcia_driver *p_drv; + struct pcmcia_device_id *did; int i; - /* detach the "instance" */ p_dev = to_pcmcia_dev(dev); p_drv = to_pcmcia_drv(dev->driver); + + /* If we're removing the primary module driving a + * pseudo multi-function card, we need to unbind + * all devices + */ + did = (struct pcmcia_device_id *) p_dev->dev.driver_data; + if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) && + (p_dev->socket->device_count != 0) && + (p_dev->device_no == 0)) + pcmcia_card_remove(p_dev->socket, p_dev); + + /* detach the "instance" */ if (!p_drv) return 0; @@ -441,17 +477,16 @@ static int pcmcia_device_remove(struct device * dev) p_drv->remove(p_dev); /* check for proper unloading */ - if (p_dev->state & (CLIENT_IRQ_REQ|CLIENT_IO_REQ|CLIENT_CONFIG_LOCKED)) + if (p_dev->_irq || p_dev->_io || p_dev->_locked) printk(KERN_INFO "pcmcia: driver %s did not release config properly\n", p_drv->drv.name); for (i = 0; i < MAX_WIN; i++) - if (p_dev->state & CLIENT_WIN_REQ(i)) + if (p_dev->_win & CLIENT_WIN_REQ(i)) printk(KERN_INFO "pcmcia: driver %s did not release windows properly\n", p_drv->drv.name); /* references from pcmcia_probe_device */ - p_dev->state = CLIENT_UNBOUND; pcmcia_put_dev(p_dev); module_put(p_drv->owner); @@ -459,37 +494,6 @@ static int pcmcia_device_remove(struct device * dev) } -/* - * Removes a PCMCIA card from the device tree and socket list. - */ -static void pcmcia_card_remove(struct pcmcia_socket *s) -{ - struct pcmcia_device *p_dev; - unsigned long flags; - - ds_dbg(2, "unbind_request(%d)\n", s->sock); - - s->device_count = 0; - - for (;;) { - /* unregister all pcmcia_devices registered with this socket*/ - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); - if (list_empty(&s->devices_list)) { - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); - return; - } - p_dev = list_entry((&s->devices_list)->next, struct pcmcia_device, socket_device_list); - list_del(&p_dev->socket_device_list); - p_dev->state |= CLIENT_STALE; - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); - - device_unregister(&p_dev->dev); - } - - return; -} /* unbind_request */ - - /* * pcmcia_device_query -- determine information about a pcmcia device */ @@ -546,7 +550,7 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev) tmp = vers1->str + vers1->ofs[i]; length = strlen(tmp) + 1; - if ((length < 3) || (length > 255)) + if ((length < 2) || (length > 255)) continue; p_dev->prod_id[i] = kmalloc(sizeof(char) * length, @@ -571,11 +575,11 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev) * won't work, this doesn't matter much at the moment: the driver core doesn't * support it either. */ -static DECLARE_MUTEX(device_add_lock); +static DEFINE_MUTEX(device_add_lock); struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int function) { - struct pcmcia_device *p_dev; + struct pcmcia_device *p_dev, *tmp_dev; unsigned long flags; int bus_id_len; @@ -583,7 +587,7 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f if (!s) return NULL; - down(&device_add_lock); + mutex_lock(&device_add_lock); /* max of 2 devices per card */ if (s->device_count == 2) @@ -596,6 +600,8 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f p_dev->socket = s; p_dev->device_no = (s->device_count++); p_dev->func = function; + if (s->functions <= function) + s->functions = function + 1; p_dev->dev.bus = &pcmcia_bus_type; p_dev->dev.parent = s->dev.dev; @@ -608,36 +614,55 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f sprintf (p_dev->devname, "pcmcia%s", p_dev->dev.bus_id); /* compat */ - p_dev->state = CLIENT_UNBOUND; + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + + /* + * p_dev->function_config must be the same for all card functions. + * Note that this is serialized by the device_add_lock, so that + * only one such struct will be created. + */ + list_for_each_entry(tmp_dev, &s->devices_list, socket_device_list) + if (p_dev->func == tmp_dev->func) { + p_dev->function_config = tmp_dev->function_config; + kref_get(&p_dev->function_config->ref); + } /* Add to the list in pcmcia_bus_socket */ - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); list_add_tail(&p_dev->socket_device_list, &s->devices_list); + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + if (!p_dev->function_config) { + p_dev->function_config = kzalloc(sizeof(struct config_t), + GFP_KERNEL); + if (!p_dev->function_config) + goto err_unreg; + kref_init(&p_dev->function_config->ref); + } + printk(KERN_NOTICE "pcmcia: registering new device %s\n", p_dev->devname); pcmcia_device_query(p_dev); - if (device_register(&p_dev->dev)) { - spin_lock_irqsave(&pcmcia_dev_list_lock, flags); - list_del(&p_dev->socket_device_list); - spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + if (device_register(&p_dev->dev)) + goto err_unreg; - goto err_free; - } - - up(&device_add_lock); + mutex_unlock(&device_add_lock); return p_dev; + err_unreg: + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + list_del(&p_dev->socket_device_list); + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + err_free: kfree(p_dev->devname); kfree(p_dev); s->device_count--; err_put: - up(&device_add_lock); + mutex_unlock(&device_add_lock); pcmcia_put_socket(s); return NULL; @@ -696,7 +721,7 @@ static void pcmcia_bus_rescan(struct pcmcia_socket *skt) int no_devices=0; unsigned long flags; - /* must be called with skt_sem held */ + /* must be called with skt_mutex held */ spin_lock_irqsave(&pcmcia_dev_list_lock, flags); if (list_empty(&skt->devices_list)) no_devices=1; @@ -819,9 +844,11 @@ static int pcmcia_bus_match(struct device * dev, struct device_driver * drv) { struct pcmcia_driver * p_drv = to_pcmcia_drv(drv); struct pcmcia_device_id *did = p_drv->id_table; +#ifdef CONFIG_PCMCIA_IOCTL /* matching by cardmgr */ if (p_dev->cardmgr == p_drv) return 1; +#endif while (did && did->match_flags) { if (pcmcia_devmatch(p_dev, did)) @@ -927,7 +954,7 @@ static ssize_t pcmcia_show_pm_state(struct device *dev, struct device_attribute { struct pcmcia_device *p_dev = to_pcmcia_dev(dev); - if (p_dev->dev.power.power_state.event != PM_EVENT_ON) + if (p_dev->suspended) return sprintf(buf, "off\n"); else return sprintf(buf, "on\n"); @@ -942,11 +969,9 @@ static ssize_t pcmcia_store_pm_state(struct device *dev, struct device_attribute if (!count) return -EINVAL; - if ((p_dev->dev.power.power_state.event == PM_EVENT_ON) && - (!strncmp(buf, "off", 3))) + if ((!p_dev->suspended) && !strncmp(buf, "off", 3)) ret = dpm_runtime_suspend(dev, PMSG_SUSPEND); - else if ((p_dev->dev.power.power_state.event != PM_EVENT_ON) && - (!strncmp(buf, "on", 2))) + else if (p_dev->suspended && !strncmp(buf, "on", 2)) dpm_runtime_resume(dev); return ret ? ret : count; @@ -982,9 +1007,9 @@ static ssize_t pcmcia_store_allow_func_id_match(struct device *dev, if (!count) return -EINVAL; - down(&p_dev->socket->skt_sem); + mutex_lock(&p_dev->socket->skt_mutex); p_dev->allow_func_id_match = 1; - up(&p_dev->socket->skt_sem); + mutex_unlock(&p_dev->socket->skt_mutex); bus_rescan_devices(&pcmcia_bus_type); @@ -1012,14 +1037,27 @@ static int pcmcia_dev_suspend(struct device * dev, pm_message_t state) { struct pcmcia_device *p_dev = to_pcmcia_dev(dev); struct pcmcia_driver *p_drv = NULL; + int ret = 0; if (dev->driver) p_drv = to_pcmcia_drv(dev->driver); - if (p_drv && p_drv->suspend) - return p_drv->suspend(p_dev); + if (!p_drv) + goto out; - return 0; + if (p_drv->suspend) { + ret = p_drv->suspend(p_dev); + if (ret) + goto out; + } + + if (p_dev->device_no == p_dev->func) + pcmcia_release_configuration(p_dev); + + out: + if (!ret) + p_dev->suspended = 1; + return ret; } @@ -1027,14 +1065,27 @@ static int pcmcia_dev_resume(struct device * dev) { struct pcmcia_device *p_dev = to_pcmcia_dev(dev); struct pcmcia_driver *p_drv = NULL; + int ret = 0; if (dev->driver) p_drv = to_pcmcia_drv(dev->driver); - if (p_drv && p_drv->resume) - return p_drv->resume(p_dev); + if (!p_drv) + goto out; - return 0; + if (p_dev->device_no == p_dev->func) { + ret = pcmcia_request_configuration(p_dev, &p_dev->conf); + if (ret) + goto out; + } + + if (p_drv->resume) + ret = p_drv->resume(p_dev); + + out: + if (!ret) + p_dev->suspended = 0; + return ret; } @@ -1100,7 +1151,7 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) switch (event) { case CS_EVENT_CARD_REMOVAL: s->pcmcia_state.present = 0; - pcmcia_card_remove(skt); + pcmcia_card_remove(skt, NULL); handle_event(skt, event); break; @@ -1128,6 +1179,32 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority) } /* ds_event */ +struct pcmcia_device * pcmcia_dev_present(struct pcmcia_device *_p_dev) +{ + struct pcmcia_device *p_dev; + struct pcmcia_device *ret = NULL; + + p_dev = pcmcia_get_dev(_p_dev); + if (!p_dev) + return NULL; + + if (!p_dev->socket->pcmcia_state.present) + goto out; + + if (p_dev->_removed) + goto out; + + if (p_dev->suspended) + goto out; + + ret = p_dev; + out: + pcmcia_put_dev(p_dev); + return ret; +} +EXPORT_SYMBOL(pcmcia_dev_present); + + static struct pcmcia_callback pcmcia_bus_callback = { .owner = THIS_MODULE, .event = ds_event, diff --git a/drivers/pcmcia/ds_internal.h b/drivers/pcmcia/ds_internal.h index d359bd25a51c..3a2b25e6ed73 100644 --- a/drivers/pcmcia/ds_internal.h +++ b/drivers/pcmcia/ds_internal.h @@ -8,6 +8,8 @@ extern void pcmcia_put_dev(struct pcmcia_device *p_dev); struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int function); +extern int pcmcia_release_configuration(struct pcmcia_device *p_dev); + #ifdef CONFIG_PCMCIA_IOCTL extern void __init pcmcia_setup_ioctl(void); extern void __exit pcmcia_cleanup_ioctl(void); @@ -15,7 +17,7 @@ extern void handle_event(struct pcmcia_socket *s, event_t event); extern int handle_request(struct pcmcia_socket *s, event_t event); #else static inline void __init pcmcia_setup_ioctl(void) { return; } -static inline void __init pcmcia_cleanup_ioctl(void) { return; } +static inline void __exit pcmcia_cleanup_ioctl(void) { return; } static inline void handle_event(struct pcmcia_socket *s, event_t event) { return; } static inline int handle_request(struct pcmcia_socket *s, event_t event) { return CS_SUCCESS; } #endif diff --git a/drivers/pcmcia/i82092.c b/drivers/pcmcia/i82092.c index 7979c85df3dc..d5f03a338c6c 100644 --- a/drivers/pcmcia/i82092.c +++ b/drivers/pcmcia/i82092.c @@ -10,7 +10,6 @@ */ #include -#include #include #include #include diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c index 35a92d1e4945..bd0308e89815 100644 --- a/drivers/pcmcia/i82365.c +++ b/drivers/pcmcia/i82365.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/pcmcia/pcmcia_compat.c b/drivers/pcmcia/pcmcia_compat.c deleted file mode 100644 index ebb161c4f819..000000000000 --- a/drivers/pcmcia/pcmcia_compat.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * PCMCIA 16-bit compatibility functions - * - * The initial developer of the original code is David A. Hinds - * . Portions created by David A. Hinds - * are Copyright (C) 1999 David A. Hinds. All Rights Reserved. - * - * Copyright (C) 2004 Dominik Brodowski - * - * 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 - -#define IN_CARD_SERVICES -#include -#include -#include -#include -#include -#include - -#include "cs_internal.h" - -int pcmcia_get_first_tuple(struct pcmcia_device *p_dev, tuple_t *tuple) -{ - return pccard_get_first_tuple(p_dev->socket, p_dev->func, tuple); -} -EXPORT_SYMBOL(pcmcia_get_first_tuple); - -int pcmcia_get_next_tuple(struct pcmcia_device *p_dev, tuple_t *tuple) -{ - return pccard_get_next_tuple(p_dev->socket, p_dev->func, tuple); -} -EXPORT_SYMBOL(pcmcia_get_next_tuple); - -int pcmcia_get_tuple_data(struct pcmcia_device *p_dev, tuple_t *tuple) -{ - return pccard_get_tuple_data(p_dev->socket, tuple); -} -EXPORT_SYMBOL(pcmcia_get_tuple_data); - -int pcmcia_parse_tuple(struct pcmcia_device *p_dev, tuple_t *tuple, cisparse_t *parse) -{ - return pccard_parse_tuple(tuple, parse); -} -EXPORT_SYMBOL(pcmcia_parse_tuple); - -int pcmcia_validate_cis(struct pcmcia_device *p_dev, cisinfo_t *info) -{ - return pccard_validate_cis(p_dev->socket, p_dev->func, info); -} -EXPORT_SYMBOL(pcmcia_validate_cis); - - -int pcmcia_reset_card(struct pcmcia_device *p_dev, client_req_t *req) -{ - return pccard_reset_card(p_dev->socket); -} -EXPORT_SYMBOL(pcmcia_reset_card); diff --git a/drivers/pcmcia/pcmcia_ioctl.c b/drivers/pcmcia/pcmcia_ioctl.c index 80969f7e7a0b..c53db7ceda5e 100644 --- a/drivers/pcmcia/pcmcia_ioctl.c +++ b/drivers/pcmcia/pcmcia_ioctl.c @@ -18,7 +18,6 @@ */ -#include #include #include #include @@ -70,10 +69,26 @@ extern int ds_pc_debug; #define ds_dbg(lvl, fmt, arg...) do { } while (0) #endif +static struct pcmcia_device *get_pcmcia_device(struct pcmcia_socket *s, + unsigned int function) +{ + struct pcmcia_device *p_dev = NULL; + unsigned long flags; + + spin_lock_irqsave(&pcmcia_dev_list_lock, flags); + list_for_each_entry(p_dev, &s->devices_list, socket_device_list) { + if (p_dev->func == function) { + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + return pcmcia_get_dev(p_dev); + } + } + spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); + return NULL; +} /* backwards-compatible accessing of driver --- by name! */ -static struct pcmcia_driver * get_pcmcia_driver (dev_info_t *dev_info) +static struct pcmcia_driver *get_pcmcia_driver(dev_info_t *dev_info) { struct device_driver *drv; struct pcmcia_driver *p_drv; @@ -214,7 +229,7 @@ static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info) * by userspace before, we need to * return the "instance". */ spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); - bind_info->instance = p_dev->instance; + bind_info->instance = p_dev; ret = -EBUSY; goto err_put_module; } else { @@ -253,9 +268,9 @@ rescan: /* * Prevent this racing with a card insertion. */ - down(&s->skt_sem); + mutex_lock(&s->skt_mutex); bus_rescan_devices(&pcmcia_bus_type); - up(&s->skt_sem); + mutex_unlock(&s->skt_mutex); /* check whether the driver indeed matched. I don't care if this * is racy or not, because it can only happen on cardmgr access @@ -289,6 +304,7 @@ static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int { dev_node_t *node; struct pcmcia_device *p_dev; + struct pcmcia_driver *p_drv; unsigned long flags; int ret = 0; @@ -343,16 +359,16 @@ static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int found: spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); - if ((!p_dev->instance) || - (p_dev->instance->state & DEV_CONFIG_PENDING)) { + p_drv = to_pcmcia_drv(p_dev->dev.driver); + if (p_drv && !p_dev->_locked) { ret = -EAGAIN; goto err_put; } if (first) - node = p_dev->instance->dev; + node = p_dev->dev_node; else - for (node = p_dev->instance->dev; node; node = node->next) + for (node = p_dev->dev_node; node; node = node->next) if (node == bind_info->next) break; if (!node) { @@ -583,14 +599,16 @@ static int ds_ioctl(struct inode * inode, struct file * file, if (buf->config.Function && (buf->config.Function >= s->functions)) ret = CS_BAD_ARGS; - else - ret = pccard_get_configuration_info(s, - buf->config.Function, &buf->config); + else { + struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->config.Function); + ret = pccard_get_configuration_info(s, p_dev, &buf->config); + pcmcia_put_dev(p_dev); + } break; case DS_GET_FIRST_TUPLE: - down(&s->skt_sem); + mutex_lock(&s->skt_mutex); pcmcia_validate_mem(s); - up(&s->skt_sem); + mutex_unlock(&s->skt_mutex); ret = pccard_get_first_tuple(s, BIND_FN_ALL, &buf->tuple); break; case DS_GET_NEXT_TUPLE: @@ -609,16 +627,19 @@ static int ds_ioctl(struct inode * inode, struct file * file, ret = pccard_reset_card(s); break; case DS_GET_STATUS: - if (buf->status.Function && - (buf->status.Function >= s->functions)) - ret = CS_BAD_ARGS; - else - ret = pccard_get_status(s, buf->status.Function, &buf->status); - break; + if (buf->status.Function && + (buf->status.Function >= s->functions)) + ret = CS_BAD_ARGS; + else { + struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->status.Function); + ret = pccard_get_status(s, p_dev, &buf->status); + pcmcia_put_dev(p_dev); + } + break; case DS_VALIDATE_CIS: - down(&s->skt_sem); + mutex_lock(&s->skt_mutex); pcmcia_validate_mem(s); - up(&s->skt_sem); + mutex_unlock(&s->skt_mutex); ret = pccard_validate_cis(s, BIND_FN_ALL, &buf->cisinfo); break; case DS_SUSPEND_CARD: @@ -638,12 +659,16 @@ static int ds_ioctl(struct inode * inode, struct file * file, err = -EPERM; goto free_out; } - if (buf->conf_reg.Function && - (buf->conf_reg.Function >= s->functions)) - ret = CS_BAD_ARGS; - else - ret = pccard_access_configuration_register(s, - buf->conf_reg.Function, &buf->conf_reg); + + ret = CS_BAD_ARGS; + + if (!(buf->conf_reg.Function && + (buf->conf_reg.Function >= s->functions))) { + struct pcmcia_device *p_dev = get_pcmcia_device(s, buf->conf_reg.Function); + if (p_dev) + ret = pcmcia_access_configuration_register(p_dev, &buf->conf_reg); + pcmcia_put_dev(p_dev); + } break; case DS_GET_FIRST_REGION: case DS_GET_NEXT_REGION: diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c index 89022ad5b520..45063b4e5b78 100644 --- a/drivers/pcmcia/pcmcia_resource.c +++ b/drivers/pcmcia/pcmcia_resource.c @@ -14,7 +14,6 @@ * */ -#include #include #include #include @@ -89,7 +88,7 @@ static int alloc_io_space(struct pcmcia_socket *s, u_int attr, ioaddr_t *base, } if ((s->features & SS_CAP_STATIC_MAP) && s->io_offset) { *base = s->io_offset | (*base & 0x0fff); - s->io[0].Attributes = attr; + s->io[0].res->flags = (s->io[0].res->flags & ~IORESOURCE_BITS) | (attr & IORESOURCE_BITS); return 0; } /* Check for an already-allocated window that must conflict with @@ -97,38 +96,36 @@ static int alloc_io_space(struct pcmcia_socket *s, u_int attr, ioaddr_t *base, * potential conflicts, just the most obvious ones. */ for (i = 0; i < MAX_IO_WIN; i++) - if ((s->io[i].NumPorts != 0) && - ((s->io[i].BasePort & (align-1)) == *base)) + if ((s->io[i].res) && + ((s->io[i].res->start & (align-1)) == *base)) return 1; for (i = 0; i < MAX_IO_WIN; i++) { - if (s->io[i].NumPorts == 0) { + if (!s->io[i].res) { s->io[i].res = pcmcia_find_io_region(*base, num, align, s); if (s->io[i].res) { - s->io[i].Attributes = attr; - s->io[i].BasePort = *base = s->io[i].res->start; - s->io[i].NumPorts = s->io[i].InUse = num; + *base = s->io[i].res->start; + s->io[i].res->flags = (s->io[i].res->flags & ~IORESOURCE_BITS) | (attr & IORESOURCE_BITS); + s->io[i].InUse = num; break; } else return 1; - } else if (s->io[i].Attributes != attr) + } else if ((s->io[i].res->flags & IORESOURCE_BITS) != (attr & IORESOURCE_BITS)) continue; /* Try to extend top of window */ - try = s->io[i].BasePort + s->io[i].NumPorts; + try = s->io[i].res->end + 1; if ((*base == 0) || (*base == try)) if (pcmcia_adjust_io_region(s->io[i].res, s->io[i].res->start, s->io[i].res->end + num, s) == 0) { *base = try; - s->io[i].NumPorts += num; s->io[i].InUse += num; break; } /* Try to extend bottom of window */ - try = s->io[i].BasePort - num; + try = s->io[i].res->start - num; if ((*base == 0) || (*base == try)) if (pcmcia_adjust_io_region(s->io[i].res, s->io[i].res->start - num, s->io[i].res->end, s) == 0) { - s->io[i].BasePort = *base = try; - s->io[i].NumPorts += num; + *base = try; s->io[i].InUse += num; break; } @@ -143,12 +140,13 @@ static void release_io_space(struct pcmcia_socket *s, ioaddr_t base, int i; for (i = 0; i < MAX_IO_WIN; i++) { - if ((s->io[i].BasePort <= base) && - (s->io[i].BasePort+s->io[i].NumPorts >= base+num)) { + if (!s->io[i].res) + continue; + if ((s->io[i].res->start <= base) && + (s->io[i].res->end >= base+num-1)) { s->io[i].InUse -= num; /* Free the window if no one else is using it */ if (s->io[i].InUse == 0) { - s->io[i].NumPorts = 0; release_resource(s->io[i].res); kfree(s->io[i].res); s->io[i].res = NULL; @@ -165,21 +163,19 @@ static void release_io_space(struct pcmcia_socket *s, ioaddr_t base, * this and the tuple reading services. */ -int pccard_access_configuration_register(struct pcmcia_socket *s, - unsigned int function, +int pcmcia_access_configuration_register(struct pcmcia_device *p_dev, conf_reg_t *reg) { + struct pcmcia_socket *s; config_t *c; int addr; u_char val; - if (!s || !s->config) + if (!p_dev || !p_dev->function_config) return CS_NO_CARD; - c = &s->config[function]; - - if (c == NULL) - return CS_NO_CARD; + s = p_dev->socket; + c = p_dev->function_config; if (!(c->state & CONFIG_LOCKED)) return CS_CONFIGURATION_LOCKED; @@ -200,20 +196,12 @@ int pccard_access_configuration_register(struct pcmcia_socket *s, break; } return CS_SUCCESS; -} /* pccard_access_configuration_register */ - -int pcmcia_access_configuration_register(struct pcmcia_device *p_dev, - conf_reg_t *reg) -{ - return pccard_access_configuration_register(p_dev->socket, - p_dev->func, reg); -} +} /* pcmcia_access_configuration_register */ EXPORT_SYMBOL(pcmcia_access_configuration_register); - int pccard_get_configuration_info(struct pcmcia_socket *s, - unsigned int function, + struct pcmcia_device *p_dev, config_info_t *config) { config_t *c; @@ -221,7 +209,7 @@ int pccard_get_configuration_info(struct pcmcia_socket *s, if (!(s->state & SOCKET_PRESENT)) return CS_NO_CARD; - config->Function = function; + config->Function = p_dev->func; #ifdef CONFIG_CARDBUS if (s->state & SOCKET_CARDBUS) { @@ -235,14 +223,14 @@ int pccard_get_configuration_info(struct pcmcia_socket *s, config->AssignedIRQ = s->irq.AssignedIRQ; if (config->AssignedIRQ) config->Attributes |= CONF_ENABLE_IRQ; - config->BasePort1 = s->io[0].BasePort; - config->NumPorts1 = s->io[0].NumPorts; + config->BasePort1 = s->io[0].res->start; + config->NumPorts1 = s->io[0].res->end - config->BasePort1 + 1; } return CS_SUCCESS; } #endif - c = (s->config != NULL) ? &s->config[function] : NULL; + c = (p_dev) ? p_dev->function_config : NULL; if ((c == NULL) || !(c->state & CONFIG_LOCKED)) { config->Attributes = 0; @@ -271,7 +259,7 @@ int pccard_get_configuration_info(struct pcmcia_socket *s, int pcmcia_get_configuration_info(struct pcmcia_device *p_dev, config_info_t *config) { - return pccard_get_configuration_info(p_dev->socket, p_dev->func, + return pccard_get_configuration_info(p_dev->socket, p_dev, config); } EXPORT_SYMBOL(pcmcia_get_configuration_info); @@ -317,7 +305,7 @@ EXPORT_SYMBOL(pcmcia_get_window); * SocketState yet: I haven't seen any point for it. */ -int pccard_get_status(struct pcmcia_socket *s, unsigned int function, +int pccard_get_status(struct pcmcia_socket *s, struct pcmcia_device *p_dev, cs_status_t *status) { config_t *c; @@ -334,11 +322,12 @@ int pccard_get_status(struct pcmcia_socket *s, unsigned int function, if (!(s->state & SOCKET_PRESENT)) return CS_NO_CARD; - c = (s->config != NULL) ? &s->config[function] : NULL; + c = (p_dev) ? p_dev->function_config : NULL; + if ((c != NULL) && (c->state & CONFIG_LOCKED) && (c->IntType & (INT_MEMORY_AND_IO | INT_ZOOMED_VIDEO))) { u_char reg; - if (c->Present & PRESENT_PIN_REPLACE) { + if (c->CardValues & PRESENT_PIN_REPLACE) { pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_PRR)>>1, 1, ®); status->CardState |= (reg & PRR_WP_STATUS) ? CS_EVENT_WRITE_PROTECT : 0; @@ -352,7 +341,7 @@ int pccard_get_status(struct pcmcia_socket *s, unsigned int function, /* No PRR? Then assume we're always ready */ status->CardState |= CS_EVENT_READY_CHANGE; } - if (c->Present & PRESENT_EXT_STATUS) { + if (c->CardValues & PRESENT_EXT_STATUS) { pcmcia_read_cis_mem(s, 1, (c->ConfigBase+CISREG_ESR)>>1, 1, ®); status->CardState |= (reg & ESR_REQ_ATTN) ? CS_EVENT_REQUEST_ATTENTION : 0; @@ -370,11 +359,9 @@ int pccard_get_status(struct pcmcia_socket *s, unsigned int function, return CS_SUCCESS; } /* pccard_get_status */ -int pcmcia_get_status(client_handle_t handle, cs_status_t *status) +int pcmcia_get_status(struct pcmcia_device *p_dev, cs_status_t *status) { - struct pcmcia_socket *s; - s = SOCKET(handle); - return pccard_get_status(s, handle->func, status); + return pccard_get_status(p_dev->socket, p_dev, status); } EXPORT_SYMBOL(pcmcia_get_status); @@ -422,7 +409,8 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev, config_t *c; s = p_dev->socket; - c = CONFIG(p_dev); + c = p_dev->function_config; + if (!(s->state & SOCKET_PRESENT)) return CS_NO_CARD; if (!(c->state & CONFIG_LOCKED)) @@ -454,6 +442,28 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev, (mod->Attributes & CONF_VPP2_CHANGE_VALID)) return CS_BAD_VPP; + if (mod->Attributes & CONF_IO_CHANGE_WIDTH) { + pccard_io_map io_off = { 0, 0, 0, 0, 1 }; + pccard_io_map io_on; + int i; + + io_on.speed = io_speed; + for (i = 0; i < MAX_IO_WIN; i++) { + if (!s->io[i].res) + continue; + io_off.map = i; + io_on.map = i; + + io_on.flags = MAP_ACTIVE | IO_DATA_PATH_WIDTH_8; + io_on.start = s->io[i].res->start; + io_on.stop = s->io[i].res->end; + + s->ops->set_io_map(s, &io_off); + mdelay(40); + s->ops->set_io_map(s, &io_on); + } + } + return CS_SUCCESS; } /* modify_configuration */ EXPORT_SYMBOL(pcmcia_modify_configuration); @@ -463,23 +473,23 @@ int pcmcia_release_configuration(struct pcmcia_device *p_dev) { pccard_io_map io = { 0, 0, 0, 0, 1 }; struct pcmcia_socket *s = p_dev->socket; + config_t *c = p_dev->function_config; int i; - if (!(p_dev->state & CLIENT_CONFIG_LOCKED)) - return CS_BAD_HANDLE; - p_dev->state &= ~CLIENT_CONFIG_LOCKED; - - if (!(p_dev->state & CLIENT_STALE)) { - config_t *c = CONFIG(p_dev); + if (p_dev->_locked) { + p_dev->_locked = 0; if (--(s->lock_count) == 0) { s->socket.flags = SS_OUTPUT_ENA; /* Is this correct? */ s->socket.Vpp = 0; s->socket.io_irq = 0; s->ops->set_socket(s, &s->socket); } + } + if (c->state & CONFIG_LOCKED) { + c->state &= ~CONFIG_LOCKED; if (c->state & CONFIG_IO_REQ) for (i = 0; i < MAX_IO_WIN; i++) { - if (s->io[i].NumPorts == 0) + if (!s->io[i].res) continue; s->io[i].Config--; if (s->io[i].Config != 0) @@ -487,12 +497,10 @@ int pcmcia_release_configuration(struct pcmcia_device *p_dev) io.map = i; s->ops->set_io_map(s, &io); } - c->state &= ~CONFIG_LOCKED; } return CS_SUCCESS; } /* pcmcia_release_configuration */ -EXPORT_SYMBOL(pcmcia_release_configuration); /** pcmcia_release_io @@ -503,25 +511,23 @@ EXPORT_SYMBOL(pcmcia_release_configuration); * don't bother checking the port ranges against the current socket * values. */ -int pcmcia_release_io(struct pcmcia_device *p_dev, io_req_t *req) +static int pcmcia_release_io(struct pcmcia_device *p_dev, io_req_t *req) { struct pcmcia_socket *s = p_dev->socket; + config_t *c = p_dev->function_config; - if (!(p_dev->state & CLIENT_IO_REQ)) + if (!p_dev->_io ) return CS_BAD_HANDLE; - p_dev->state &= ~CLIENT_IO_REQ; - if (!(p_dev->state & CLIENT_STALE)) { - config_t *c = CONFIG(p_dev); - if (c->state & CONFIG_LOCKED) - return CS_CONFIGURATION_LOCKED; - if ((c->io.BasePort1 != req->BasePort1) || - (c->io.NumPorts1 != req->NumPorts1) || - (c->io.BasePort2 != req->BasePort2) || - (c->io.NumPorts2 != req->NumPorts2)) - return CS_BAD_ARGS; - c->state &= ~CONFIG_IO_REQ; - } + p_dev->_io = 0; + + if ((c->io.BasePort1 != req->BasePort1) || + (c->io.NumPorts1 != req->NumPorts1) || + (c->io.BasePort2 != req->BasePort2) || + (c->io.NumPorts2 != req->NumPorts2)) + return CS_BAD_ARGS; + + c->state &= ~CONFIG_IO_REQ; release_io_space(s, req->BasePort1, req->NumPorts1); if (req->NumPorts2) @@ -529,28 +535,26 @@ int pcmcia_release_io(struct pcmcia_device *p_dev, io_req_t *req) return CS_SUCCESS; } /* pcmcia_release_io */ -EXPORT_SYMBOL(pcmcia_release_io); -int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req) +static int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req) { struct pcmcia_socket *s = p_dev->socket; - if (!(p_dev->state & CLIENT_IRQ_REQ)) - return CS_BAD_HANDLE; - p_dev->state &= ~CLIENT_IRQ_REQ; + config_t *c= p_dev->function_config; - if (!(p_dev->state & CLIENT_STALE)) { - config_t *c = CONFIG(p_dev); - if (c->state & CONFIG_LOCKED) - return CS_CONFIGURATION_LOCKED; - if (c->irq.Attributes != req->Attributes) - return CS_BAD_ATTRIBUTE; - if (s->irq.AssignedIRQ != req->AssignedIRQ) - return CS_BAD_IRQ; - if (--s->irq.Config == 0) { - c->state &= ~CONFIG_IRQ_REQ; - s->irq.AssignedIRQ = 0; - } + if (!p_dev->_irq) + return CS_BAD_HANDLE; + p_dev->_irq = 0; + + if (c->state & CONFIG_LOCKED) + return CS_CONFIGURATION_LOCKED; + if (c->irq.Attributes != req->Attributes) + return CS_BAD_ATTRIBUTE; + if (s->irq.AssignedIRQ != req->AssignedIRQ) + return CS_BAD_IRQ; + if (--s->irq.Config == 0) { + c->state &= ~CONFIG_IRQ_REQ; + s->irq.AssignedIRQ = 0; } if (req->Attributes & IRQ_HANDLE_PRESENT) { @@ -563,7 +567,6 @@ int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req) return CS_SUCCESS; } /* pcmcia_release_irq */ -EXPORT_SYMBOL(pcmcia_release_irq); int pcmcia_release_window(window_handle_t win) @@ -573,7 +576,7 @@ int pcmcia_release_window(window_handle_t win) if ((win == NULL) || (win->magic != WINDOW_MAGIC)) return CS_BAD_HANDLE; s = win->sock; - if (!(win->handle->state & CLIENT_WIN_REQ(win->index))) + if (!(win->handle->_win & CLIENT_WIN_REQ(win->index))) return CS_BAD_HANDLE; /* Shut down memory window */ @@ -587,7 +590,7 @@ int pcmcia_release_window(window_handle_t win) kfree(win->ctl.res); win->ctl.res = NULL; } - win->handle->state &= ~CLIENT_WIN_REQ(win->index); + win->handle->_win &= ~CLIENT_WIN_REQ(win->index); win->magic = 0; @@ -610,16 +613,12 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev, if (req->IntType & INT_CARDBUS) return CS_UNSUPPORTED_MODE; - c = CONFIG(p_dev); + c = p_dev->function_config; if (c->state & CONFIG_LOCKED) return CS_CONFIGURATION_LOCKED; /* Do power control. We don't allow changes in Vcc. */ - if (s->socket.Vcc != req->Vcc) - return CS_BAD_VCC; - if (req->Vpp1 != req->Vpp2) - return CS_BAD_VPP; - s->socket.Vpp = req->Vpp1; + s->socket.Vpp = req->Vpp; if (s->ops->set_socket(s, &s->socket)) return CS_BAD_VPP; @@ -643,7 +642,7 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev, /* Set up CIS configuration registers */ base = c->ConfigBase = req->ConfigBase; - c->Present = c->CardValues = req->Present; + c->CardValues = req->Present; if (req->Present & PRESENT_COPY) { c->Copy = req->Copy; pcmcia_write_cis_mem(s, 1, (base + CISREG_SCR)>>1, 1, &c->Copy); @@ -690,10 +689,10 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev, if (c->state & CONFIG_IO_REQ) { iomap.speed = io_speed; for (i = 0; i < MAX_IO_WIN; i++) - if (s->io[i].NumPorts != 0) { + if (s->io[i].res) { iomap.map = i; iomap.flags = MAP_ACTIVE; - switch (s->io[i].Attributes & IO_DATA_PATH_WIDTH) { + switch (s->io[i].res->flags & IO_DATA_PATH_WIDTH) { case IO_DATA_PATH_WIDTH_16: iomap.flags |= MAP_16BIT; break; case IO_DATA_PATH_WIDTH_AUTO: @@ -701,15 +700,15 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev, default: break; } - iomap.start = s->io[i].BasePort; - iomap.stop = iomap.start + s->io[i].NumPorts - 1; + iomap.start = s->io[i].res->start; + iomap.stop = s->io[i].res->end; s->ops->set_io_map(s, &iomap); s->io[i].Config++; } } c->state |= CONFIG_LOCKED; - p_dev->state |= CLIENT_CONFIG_LOCKED; + p_dev->_locked = 1; return CS_SUCCESS; } /* pcmcia_request_configuration */ EXPORT_SYMBOL(pcmcia_request_configuration); @@ -730,7 +729,7 @@ int pcmcia_request_io(struct pcmcia_device *p_dev, io_req_t *req) if (!req) return CS_UNSUPPORTED_MODE; - c = CONFIG(p_dev); + c = p_dev->function_config; if (c->state & CONFIG_LOCKED) return CS_CONFIGURATION_LOCKED; if (c->state & CONFIG_IO_REQ) @@ -755,7 +754,7 @@ int pcmcia_request_io(struct pcmcia_device *p_dev, io_req_t *req) c->io = *req; c->state |= CONFIG_IO_REQ; - p_dev->state |= CLIENT_IO_REQ; + p_dev->_io = 1; return CS_SUCCESS; } /* pcmcia_request_io */ EXPORT_SYMBOL(pcmcia_request_io); @@ -786,7 +785,7 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req) if (!(s->state & SOCKET_PRESENT)) return CS_NO_CARD; - c = CONFIG(p_dev); + c = p_dev->function_config; if (c->state & CONFIG_LOCKED) return CS_CONFIGURATION_LOCKED; if (c->state & CONFIG_IRQ_REQ) @@ -851,7 +850,7 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req) s->irq.Config++; c->state |= CONFIG_IRQ_REQ; - p_dev->state |= CLIENT_IRQ_REQ; + p_dev->_irq = 1; #ifdef CONFIG_PCMCIA_PROBE pcmcia_used_irq[irq]++; @@ -911,7 +910,7 @@ int pcmcia_request_window(struct pcmcia_device **p_dev, win_req_t *req, window_h if (!win->ctl.res) return CS_IN_USE; } - (*p_dev)->state |= CLIENT_WIN_REQ(w); + (*p_dev)->_win |= CLIENT_WIN_REQ(w); /* Configure the socket controller */ win->ctl.map = w+1; @@ -941,3 +940,14 @@ int pcmcia_request_window(struct pcmcia_device **p_dev, win_req_t *req, window_h return CS_SUCCESS; } /* pcmcia_request_window */ EXPORT_SYMBOL(pcmcia_request_window); + +void pcmcia_disable_device(struct pcmcia_device *p_dev) { + pcmcia_release_configuration(p_dev); + pcmcia_release_io(p_dev, &p_dev->io); + pcmcia_release_irq(p_dev, &p_dev->irq); + if (&p_dev->win) + pcmcia_release_window(p_dev->win); + + p_dev->dev_node = NULL; +} +EXPORT_SYMBOL(pcmcia_disable_device); diff --git a/drivers/pcmcia/pd6729.c b/drivers/pcmcia/pd6729.c index f2789afb22b2..16d1ea7b0a18 100644 --- a/drivers/pcmcia/pd6729.c +++ b/drivers/pcmcia/pd6729.c @@ -8,7 +8,6 @@ */ #include -#include #include #include #include diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c index 514609369836..81dfc2cac2b4 100644 --- a/drivers/pcmcia/rsrc_mgr.c +++ b/drivers/pcmcia/rsrc_mgr.c @@ -12,7 +12,6 @@ * (C) 1999 David A. Hinds */ -#include #include #include @@ -22,6 +21,8 @@ #include "cs_internal.h" +#ifdef CONFIG_PCMCIA_IOCTL + #ifdef CONFIG_PCMCIA_PROBE static int adjust_irq(struct pcmcia_socket *s, adjust_t *adj) @@ -98,6 +99,8 @@ int pcmcia_adjust_resource_info(adjust_t *adj) } EXPORT_SYMBOL(pcmcia_adjust_resource_info); +#endif + int pcmcia_validate_mem(struct pcmcia_socket *s) { if (s->resource_ops->validate_mem) diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index 5301ac60358f..0f8b157c9717 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c @@ -12,7 +12,6 @@ * (C) 1999 David A. Hinds */ -#include #include #include #include @@ -61,7 +60,7 @@ struct socket_data { unsigned int rsrc_mem_probe; }; -static DECLARE_MUTEX(rsrc_sem); +static DEFINE_MUTEX(rsrc_mutex); #define MEM_PROBE_LOW (1 << 0) #define MEM_PROBE_HIGH (1 << 1) @@ -484,7 +483,7 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) /* - * Locking note: Must be called with skt_sem held! + * Locking note: Must be called with skt_mutex held! */ static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s) { @@ -495,7 +494,7 @@ static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s) if (!probe_mem) return 0; - down(&rsrc_sem); + mutex_lock(&rsrc_mutex); if (s->features & SS_CAP_PAGE_REGS) probe_mask = MEM_PROBE_HIGH; @@ -507,7 +506,7 @@ static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s) s_data->rsrc_mem_probe |= probe_mask; } - up(&rsrc_sem); + mutex_unlock(&rsrc_mutex); return ret; } @@ -585,7 +584,7 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star struct socket_data *s_data = s->resource_data; int ret = -ENOMEM; - down(&rsrc_sem); + mutex_lock(&rsrc_mutex); for (m = s_data->io_db.next; m != &s_data->io_db; m = m->next) { unsigned long start = m->base; unsigned long end = m->base + m->num - 1; @@ -596,7 +595,7 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star ret = adjust_resource(res, r_start, r_end - r_start + 1); break; } - up(&rsrc_sem); + mutex_unlock(&rsrc_mutex); return ret; } @@ -630,7 +629,7 @@ static struct resource *nonstatic_find_io_region(unsigned long base, int num, data.offset = base & data.mask; data.map = &s_data->io_db; - down(&rsrc_sem); + mutex_lock(&rsrc_mutex); #ifdef CONFIG_PCI if (s->cb_dev) { ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1, @@ -639,7 +638,7 @@ static struct resource *nonstatic_find_io_region(unsigned long base, int num, #endif ret = allocate_resource(&ioport_resource, res, num, min, ~0UL, 1, pcmcia_align, &data); - up(&rsrc_sem); + mutex_unlock(&rsrc_mutex); if (ret != 0) { kfree(res); @@ -672,7 +671,7 @@ static struct resource * nonstatic_find_mem_region(u_long base, u_long num, min = 0x100000UL + base; } - down(&rsrc_sem); + mutex_lock(&rsrc_mutex); #ifdef CONFIG_PCI if (s->cb_dev) { ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, @@ -682,7 +681,7 @@ static struct resource * nonstatic_find_mem_region(u_long base, u_long num, #endif ret = allocate_resource(&iomem_resource, res, num, min, max, 1, pcmcia_align, &data); - up(&rsrc_sem); + mutex_unlock(&rsrc_mutex); if (ret == 0 || low) break; low = 1; @@ -705,7 +704,7 @@ static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned if (end < start) return -EINVAL; - down(&rsrc_sem); + mutex_lock(&rsrc_mutex); switch (action) { case ADD_MANAGED_RESOURCE: ret = add_interval(&data->mem_db, start, size); @@ -723,7 +722,7 @@ static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned default: ret = -EINVAL; } - up(&rsrc_sem); + mutex_unlock(&rsrc_mutex); return ret; } @@ -741,7 +740,7 @@ static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long if (end > IO_SPACE_LIMIT) return -EINVAL; - down(&rsrc_sem); + mutex_lock(&rsrc_mutex); switch (action) { case ADD_MANAGED_RESOURCE: if (add_interval(&data->io_db, start, size) != 0) { @@ -760,7 +759,7 @@ static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long ret = -EINVAL; break; } - up(&rsrc_sem); + mutex_unlock(&rsrc_mutex); return ret; } @@ -867,7 +866,7 @@ static void nonstatic_release_resource_db(struct pcmcia_socket *s) struct socket_data *data = s->resource_data; struct resource_map *p, *q; - down(&rsrc_sem); + mutex_lock(&rsrc_mutex); for (p = data->mem_db.next; p != &data->mem_db; p = q) { q = p->next; kfree(p); @@ -876,7 +875,7 @@ static void nonstatic_release_resource_db(struct pcmcia_socket *s) q = p->next; kfree(p); } - up(&rsrc_sem); + mutex_unlock(&rsrc_mutex); } @@ -901,7 +900,7 @@ static ssize_t show_io_db(struct class_device *class_dev, char *buf) struct resource_map *p; ssize_t ret = 0; - down(&rsrc_sem); + mutex_lock(&rsrc_mutex); data = s->resource_data; for (p = data->io_db.next; p != &data->io_db; p = p->next) { @@ -913,7 +912,7 @@ static ssize_t show_io_db(struct class_device *class_dev, char *buf) ((unsigned long) p->base + p->num - 1)); } - up(&rsrc_sem); + mutex_unlock(&rsrc_mutex); return (ret); } @@ -953,7 +952,7 @@ static ssize_t show_mem_db(struct class_device *class_dev, char *buf) struct resource_map *p; ssize_t ret = 0; - down(&rsrc_sem); + mutex_lock(&rsrc_mutex); data = s->resource_data; for (p = data->mem_db.next; p != &data->mem_db; p = p->next) { @@ -965,7 +964,7 @@ static ssize_t show_mem_db(struct class_device *class_dev, char *buf) ((unsigned long) p->base + p->num - 1)); } - up(&rsrc_sem); + mutex_unlock(&rsrc_mutex); return (ret); } diff --git a/drivers/pcmcia/sa1100_cerf.c b/drivers/pcmcia/sa1100_cerf.c index 2b3c2895b43d..eb89928f2338 100644 --- a/drivers/pcmcia/sa1100_cerf.c +++ b/drivers/pcmcia/sa1100_cerf.c @@ -5,7 +5,6 @@ * Based off the Assabet. * */ -#include #include #include #include diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c index 5ab1cdef7c48..c5d7476da471 100644 --- a/drivers/pcmcia/socket_sysfs.c +++ b/drivers/pcmcia/socket_sysfs.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -25,6 +24,7 @@ #include #include #include +#include #include #include @@ -183,7 +183,7 @@ static ssize_t pccard_store_resource(struct class_device *dev, const char *buf, s->resource_setup_done = 1; spin_unlock_irqrestore(&s->lock, flags); - down(&s->skt_sem); + mutex_lock(&s->skt_mutex); if ((s->callback) && (s->state & SOCKET_PRESENT) && !(s->state & SOCKET_CARDBUS)) { @@ -192,7 +192,7 @@ static ssize_t pccard_store_resource(struct class_device *dev, const char *buf, module_put(s->callback->owner); } } - up(&s->skt_sem); + mutex_unlock(&s->skt_mutex); return count; } @@ -322,7 +322,7 @@ static ssize_t pccard_store_cis(struct kobject *kobj, char *buf, loff_t off, siz kfree(cis); if (!ret) { - down(&s->skt_sem); + mutex_lock(&s->skt_mutex); if ((s->callback) && (s->state & SOCKET_PRESENT) && !(s->state & SOCKET_CARDBUS)) { if (try_module_get(s->callback->owner)) { @@ -330,7 +330,7 @@ static ssize_t pccard_store_cis(struct kobject *kobj, char *buf, loff_t off, siz module_put(s->callback->owner); } } - up(&s->skt_sem); + mutex_unlock(&s->skt_mutex); } diff --git a/drivers/pcmcia/ti113x.h b/drivers/pcmcia/ti113x.h index d5b4ff74462e..7a3d1b8e16b9 100644 --- a/drivers/pcmcia/ti113x.h +++ b/drivers/pcmcia/ti113x.h @@ -30,7 +30,6 @@ #ifndef _LINUX_TI113X_H #define _LINUX_TI113X_H -#include /* Register definitions for TI 113X PCI-to-CardBus bridges */ diff --git a/drivers/pcmcia/vrc4171_card.c b/drivers/pcmcia/vrc4171_card.c index 0574efd7828a..459e6e1946fd 100644 --- a/drivers/pcmcia/vrc4171_card.c +++ b/drivers/pcmcia/vrc4171_card.c @@ -634,7 +634,7 @@ static void vrc4171_remove_sockets(void) static int __devinit vrc4171_card_setup(char *options) { if (options == NULL || *options == '\0') - return 0; + return 1; if (strncmp(options, "irq:", 4) == 0) { int irq; @@ -644,7 +644,7 @@ static int __devinit vrc4171_card_setup(char *options) vrc4171_irq = irq; if (*options != ',') - return 0; + return 1; options++; } @@ -663,10 +663,10 @@ static int __devinit vrc4171_card_setup(char *options) } if (*options != ',') - return 0; + return 1; options++; } else - return 0; + return 1; } @@ -688,7 +688,7 @@ static int __devinit vrc4171_card_setup(char *options) } if (*options != ',') - return 0; + return 1; options++; if (strncmp(options, "memnoprobe", 10) == 0) @@ -700,7 +700,7 @@ static int __devinit vrc4171_card_setup(char *options) } } - return 0; + return 1; } __setup("vrc4171_card=", vrc4171_card_setup); diff --git a/drivers/pcmcia/vrc4173_cardu.c b/drivers/pcmcia/vrc4173_cardu.c index 57f38dba0a48..6004196f7cc1 100644 --- a/drivers/pcmcia/vrc4173_cardu.c +++ b/drivers/pcmcia/vrc4173_cardu.c @@ -516,7 +516,7 @@ static int __devinit vrc4173_cardu_probe(struct pci_dev *dev, static int __devinit vrc4173_cardu_setup(char *options) { if (options == NULL || *options == '\0') - return 0; + return 1; if (strncmp(options, "cardu1:", 7) == 0) { options += 7; @@ -527,9 +527,9 @@ static int __devinit vrc4173_cardu_setup(char *options) } if (*options != ',') - return 0; + return 1; } else - return 0; + return 1; } if (strncmp(options, "cardu2:", 7) == 0) { @@ -538,7 +538,7 @@ static int __devinit vrc4173_cardu_setup(char *options) cardu_sockets[CARDU2].noprobe = 1; } - return 0; + return 1; } __setup("vrc4173_cardu=", vrc4173_cardu_setup); diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 929dd8090578..65d090dbef46 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -147,6 +147,16 @@ config RTC_DRV_SA1100 To compile this driver as a module, choose M here: the module will be called rtc-sa1100. +config RTC_DRV_VR41XX + tristate "NEC VR41XX" + depends on RTC_CLASS && CPU_VR41XX + help + If you say Y here you will get access to the real time clock + built into your NEC VR41XX CPU. + + To compile this driver as a module, choose M here: the + module will be called rtc-vr41xx. + config RTC_DRV_TEST tristate "Test driver/device" depends on RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 8d4c7fe88d58..a9ca0f171686 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -19,3 +19,4 @@ obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o +obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 8533936d50d8..413c7d54ea10 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -96,6 +96,8 @@ exit_idr: idr_remove(&rtc_idr, id); exit: + dev_err(dev, "rtc core: unable to register %s, err = %d\n", + name, err); return ERR_PTR(err); } EXPORT_SYMBOL_GPL(rtc_device_register); diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c index 358695a416f3..9be81fd4737c 100644 --- a/drivers/rtc/rtc-ds1672.c +++ b/drivers/rtc/rtc-ds1672.c @@ -1,6 +1,8 @@ /* * An rtc/i2c driver for the Dallas DS1672 - * Copyright 2005 Alessandro Zummo + * Copyright 2005-06 Tower Technologies + * + * Author: Alessandro Zummo * * 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 @@ -11,7 +13,7 @@ #include #include -#define DRV_VERSION "0.2" +#define DRV_VERSION "0.3" /* Addresses to scan: none. This chip cannot be detected. */ static unsigned short normal_i2c[] = { I2C_CLIENT_END }; @@ -25,6 +27,7 @@ I2C_CLIENT_INSMOD; #define DS1672_REG_CONTROL 4 #define DS1672_REG_TRICKLE 5 +#define DS1672_REG_CONTROL_EOSC 0x80 /* Prototypes */ static int ds1672_probe(struct i2c_adapter *adapter, int address, int kind); @@ -53,8 +56,7 @@ static int ds1672_get_datetime(struct i2c_client *client, struct rtc_time *tm) dev_dbg(&client->dev, "%s: raw read data - counters=%02x,%02x,%02x,%02x\n" - __FUNCTION__, - buf[0], buf[1], buf[2], buf[3]); + __FUNCTION__, buf[0], buf[1], buf[2], buf[3]); time = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; @@ -62,8 +64,7 @@ static int ds1672_get_datetime(struct i2c_client *client, struct rtc_time *tm) dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, " "mday=%d, mon=%d, year=%d, wday=%d\n", - __FUNCTION__, - tm->tm_sec, tm->tm_min, tm->tm_hour, + __FUNCTION__, tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); return 0; @@ -72,16 +73,17 @@ static int ds1672_get_datetime(struct i2c_client *client, struct rtc_time *tm) static int ds1672_set_mmss(struct i2c_client *client, unsigned long secs) { int xfer; - unsigned char buf[5]; + unsigned char buf[6]; buf[0] = DS1672_REG_CNT_BASE; buf[1] = secs & 0x000000FF; buf[2] = (secs & 0x0000FF00) >> 8; buf[3] = (secs & 0x00FF0000) >> 16; buf[4] = (secs & 0xFF000000) >> 24; + buf[5] = 0; /* set control reg to enable counting */ - xfer = i2c_master_send(client, buf, 5); - if (xfer != 5) { + xfer = i2c_master_send(client, buf, 6); + if (xfer != 6) { dev_err(&client->dev, "%s: send: %d\n", __FUNCTION__, xfer); return -EIO; } @@ -120,6 +122,40 @@ static int ds1672_rtc_set_mmss(struct device *dev, unsigned long secs) return ds1672_set_mmss(to_i2c_client(dev), secs); } +static int ds1672_get_control(struct i2c_client *client, u8 *status) +{ + unsigned char addr = DS1672_REG_CONTROL; + + struct i2c_msg msgs[] = { + { client->addr, 0, 1, &addr }, /* setup read ptr */ + { client->addr, I2C_M_RD, 1, status }, /* read control */ + }; + + /* read control register */ + if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) { + dev_err(&client->dev, "%s: read error\n", __FUNCTION__); + return -EIO; + } + + return 0; +} + +/* following are the sysfs callback functions */ +static ssize_t show_control(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 control; + int err; + + err = ds1672_get_control(client, &control); + if (err) + return err; + + return sprintf(buf, "%s\n", (control & DS1672_REG_CONTROL_EOSC) + ? "disabled" : "enabled"); +} +static DEVICE_ATTR(control, S_IRUGO, show_control, NULL); + static struct rtc_class_ops ds1672_rtc_ops = { .read_time = ds1672_rtc_read_time, .set_time = ds1672_rtc_set_time, @@ -128,7 +164,6 @@ static struct rtc_class_ops ds1672_rtc_ops = { static int ds1672_attach(struct i2c_adapter *adapter) { - dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); return i2c_probe(adapter, &addr_data, ds1672_probe); } @@ -137,8 +172,6 @@ static int ds1672_detach(struct i2c_client *client) int err; struct rtc_device *rtc = i2c_get_clientdata(client); - dev_dbg(&client->dev, "%s\n", __FUNCTION__); - if (rtc) rtc_device_unregister(rtc); @@ -162,6 +195,7 @@ static struct i2c_driver ds1672_driver = { static int ds1672_probe(struct i2c_adapter *adapter, int address, int kind) { int err = 0; + u8 control; struct i2c_client *client; struct rtc_device *rtc; @@ -195,13 +229,23 @@ static int ds1672_probe(struct i2c_adapter *adapter, int address, int kind) if (IS_ERR(rtc)) { err = PTR_ERR(rtc); - dev_err(&client->dev, - "unable to register the class device\n"); goto exit_detach; } i2c_set_clientdata(client, rtc); + /* read control register */ + err = ds1672_get_control(client, &control); + if (err) + goto exit_detach; + + if (control & DS1672_REG_CONTROL_EOSC) + dev_warn(&client->dev, "Oscillator not enabled. " + "Set time to enable.\n"); + + /* Register sysfs hooks */ + device_create_file(&client->dev, &dev_attr_control); + return 0; exit_detach: diff --git a/drivers/rtc/rtc-ep93xx.c b/drivers/rtc/rtc-ep93xx.c index 0dd80ea686a9..e1a1169e4664 100644 --- a/drivers/rtc/rtc-ep93xx.c +++ b/drivers/rtc/rtc-ep93xx.c @@ -67,7 +67,6 @@ static int ep93xx_rtc_proc(struct device *dev, struct seq_file *seq) ep93xx_get_swcomp(dev, &preload, &delete); - seq_printf(seq, "24hr\t\t: yes\n"); seq_printf(seq, "preload\t\t: %d\n", preload); seq_printf(seq, "delete\t\t: %d\n", delete); @@ -110,7 +109,6 @@ static int __devinit ep93xx_rtc_probe(struct platform_device *dev) &dev->dev, &ep93xx_rtc_ops, THIS_MODULE); if (IS_ERR(rtc)) { - dev_err(&dev->dev, "unable to register\n"); return PTR_ERR(rtc); } diff --git a/drivers/rtc/rtc-m48t86.c b/drivers/rtc/rtc-m48t86.c index db445c872b1b..f6e7ee04f3dc 100644 --- a/drivers/rtc/rtc-m48t86.c +++ b/drivers/rtc/rtc-m48t86.c @@ -23,7 +23,7 @@ #define M48T86_REG_SECALRM 0x01 #define M48T86_REG_MIN 0x02 #define M48T86_REG_MINALRM 0x03 -#define M48T86_REG_HOUR 0x04 +#define M48T86_REG_HOUR 0x04 #define M48T86_REG_HOURALRM 0x05 #define M48T86_REG_DOW 0x06 /* 1 = sunday */ #define M48T86_REG_DOM 0x07 @@ -127,9 +127,6 @@ static int m48t86_rtc_proc(struct device *dev, struct seq_file *seq) reg = ops->readb(M48T86_REG_B); - seq_printf(seq, "24hr\t\t: %s\n", - (reg & M48T86_REG_B_H24) ? "yes" : "no"); - seq_printf(seq, "mode\t\t: %s\n", (reg & M48T86_REG_B_DM) ? "binary" : "bcd"); @@ -154,10 +151,8 @@ static int __devinit m48t86_rtc_probe(struct platform_device *dev) struct rtc_device *rtc = rtc_device_register("m48t86", &dev->dev, &m48t86_rtc_ops, THIS_MODULE); - if (IS_ERR(rtc)) { - dev_err(&dev->dev, "unable to register\n"); + if (IS_ERR(rtc)) return PTR_ERR(rtc); - } platform_set_drvdata(dev, rtc); diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index d857d45bdbe8..ba9a583b7b68 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -227,14 +227,7 @@ static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm) return pcf8563_set_datetime(to_i2c_client(dev), tm); } -static int pcf8563_rtc_proc(struct device *dev, struct seq_file *seq) -{ - seq_printf(seq, "24hr\t\t: yes\n"); - return 0; -} - static struct rtc_class_ops pcf8563_rtc_ops = { - .proc = pcf8563_rtc_proc, .read_time = pcf8563_rtc_read_time, .set_time = pcf8563_rtc_set_time, }; @@ -297,8 +290,6 @@ static int pcf8563_probe(struct i2c_adapter *adapter, int address, int kind) if (IS_ERR(rtc)) { err = PTR_ERR(rtc); - dev_err(&client->dev, - "unable to register the class device\n"); goto exit_detach; } @@ -321,8 +312,6 @@ static int pcf8563_detach(struct i2c_client *client) int err; struct rtc_device *rtc = i2c_get_clientdata(client); - dev_dbg(&client->dev, "%s\n", __FUNCTION__); - if (rtc) rtc_device_unregister(rtc); diff --git a/drivers/rtc/rtc-proc.c b/drivers/rtc/rtc-proc.c index 90b8a97a0919..cef5f5a3bbf9 100644 --- a/drivers/rtc/rtc-proc.c +++ b/drivers/rtc/rtc-proc.c @@ -71,6 +71,8 @@ static int rtc_proc_show(struct seq_file *seq, void *offset) alrm.pending ? "yes" : "no"); } + seq_printf(seq, "24hr\t\t: yes\n"); + if (ops->proc) ops->proc(class_dev->dev, seq); diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c index 396c8681f66c..7553d797603f 100644 --- a/drivers/rtc/rtc-rs5c372.c +++ b/drivers/rtc/rtc-rs5c372.c @@ -151,9 +151,8 @@ static int rs5c372_rtc_proc(struct device *dev, struct seq_file *seq) { int err, osc, trim; - seq_printf(seq, "24hr\t\t: yes\n"); - - if ((err = rs5c372_get_trim(to_i2c_client(dev), &osc, &trim)) == 0) { + err = rs5c372_get_trim(to_i2c_client(dev), &osc, &trim); + if (err == 0) { seq_printf(seq, "%d.%03d KHz\n", osc / 1000, osc % 1000); seq_printf(seq, "trim\t: %d\n", trim); } @@ -170,30 +169,31 @@ static struct rtc_class_ops rs5c372_rtc_ops = { static ssize_t rs5c372_sysfs_show_trim(struct device *dev, struct device_attribute *attr, char *buf) { - int trim; + int err, trim; - if (rs5c372_get_trim(to_i2c_client(dev), NULL, &trim) == 0) - return sprintf(buf, "0x%2x\n", trim); + err = rs5c372_get_trim(to_i2c_client(dev), NULL, &trim); + if (err) + return err; - return 0; + return sprintf(buf, "0x%2x\n", trim); } static DEVICE_ATTR(trim, S_IRUGO, rs5c372_sysfs_show_trim, NULL); static ssize_t rs5c372_sysfs_show_osc(struct device *dev, struct device_attribute *attr, char *buf) { - int osc; + int err, osc; - if (rs5c372_get_trim(to_i2c_client(dev), &osc, NULL) == 0) - return sprintf(buf, "%d.%03d KHz\n", osc / 1000, osc % 1000); + err = rs5c372_get_trim(to_i2c_client(dev), &osc, NULL); + if (err) + return err; - return 0; + return sprintf(buf, "%d.%03d KHz\n", osc / 1000, osc % 1000); } static DEVICE_ATTR(osc, S_IRUGO, rs5c372_sysfs_show_osc, NULL); static int rs5c372_attach(struct i2c_adapter *adapter) { - dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); return i2c_probe(adapter, &addr_data, rs5c372_probe); } @@ -233,8 +233,6 @@ static int rs5c372_probe(struct i2c_adapter *adapter, int address, int kind) if (IS_ERR(rtc)) { err = PTR_ERR(rtc); - dev_err(&client->dev, - "unable to register the class device\n"); goto exit_detach; } @@ -260,8 +258,6 @@ static int rs5c372_detach(struct i2c_client *client) int err; struct rtc_device *rtc = i2c_get_clientdata(client); - dev_dbg(&client->dev, "%s\n", __FUNCTION__); - if (rtc) rtc_device_unregister(rtc); diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c index 83b2bb480a16..a23ec54989f6 100644 --- a/drivers/rtc/rtc-sa1100.c +++ b/drivers/rtc/rtc-sa1100.c @@ -160,19 +160,19 @@ static int sa1100_rtc_open(struct device *dev) ret = request_irq(IRQ_RTC1Hz, sa1100_rtc_interrupt, SA_INTERRUPT, "rtc 1Hz", dev); if (ret) { - printk(KERN_ERR "rtc: IRQ%d already in use.\n", IRQ_RTC1Hz); + dev_err(dev, "IRQ %d already in use.\n", IRQ_RTC1Hz); goto fail_ui; } ret = request_irq(IRQ_RTCAlrm, sa1100_rtc_interrupt, SA_INTERRUPT, "rtc Alrm", dev); if (ret) { - printk(KERN_ERR "rtc: IRQ%d already in use.\n", IRQ_RTCAlrm); + dev_err(dev, "IRQ %d already in use.\n", IRQ_RTCAlrm); goto fail_ai; } ret = request_irq(IRQ_OST1, timer1_interrupt, SA_INTERRUPT, "rtc timer", dev); if (ret) { - printk(KERN_ERR "rtc: IRQ%d already in use.\n", IRQ_OST1); + dev_err(dev, "IRQ %d already in use.\n", IRQ_OST1); goto fail_pi; } return 0; @@ -332,7 +332,7 @@ static int sa1100_rtc_probe(struct platform_device *pdev) */ if (RTTR == 0) { RTTR = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16); - printk(KERN_WARNING "rtc: warning: initializing default clock divider/trim value\n"); + dev_warn(&pdev->dev, "warning: initializing default clock divider/trim value\n"); /* The current RTC value probably doesn't make sense either */ RCNR = 0; } @@ -340,15 +340,11 @@ static int sa1100_rtc_probe(struct platform_device *pdev) rtc = rtc_device_register(pdev->name, &pdev->dev, &sa1100_rtc_ops, THIS_MODULE); - if (IS_ERR(rtc)) { - dev_err(&pdev->dev, "Unable to register the RTC device\n"); + if (IS_ERR(rtc)) return PTR_ERR(rtc); - } platform_set_drvdata(pdev, rtc); - dev_info(&pdev->dev, "SA11xx/PXA2xx RTC Registered\n"); - return 0; } diff --git a/drivers/rtc/rtc-test.c b/drivers/rtc/rtc-test.c index 43d107487820..e1f7e8e86daf 100644 --- a/drivers/rtc/rtc-test.c +++ b/drivers/rtc/rtc-test.c @@ -49,7 +49,6 @@ static int test_rtc_proc(struct device *dev, struct seq_file *seq) { struct platform_device *plat_dev = to_platform_device(dev); - seq_printf(seq, "24hr\t\t: yes\n"); seq_printf(seq, "test\t\t: yes\n"); seq_printf(seq, "id\t\t: %d\n", plat_dev->id); @@ -120,8 +119,6 @@ static int test_probe(struct platform_device *plat_dev) &test_rtc_ops, THIS_MODULE); if (IS_ERR(rtc)) { err = PTR_ERR(rtc); - dev_err(&plat_dev->dev, - "unable to register the class device\n"); return err; } device_create_file(&plat_dev->dev, &dev_attr_irq); diff --git a/drivers/char/vr41xx_rtc.c b/drivers/rtc/rtc-vr41xx.c similarity index 57% rename from drivers/char/vr41xx_rtc.c rename to drivers/rtc/rtc-vr41xx.c index b109d9a502d6..4d49fd501198 100644 --- a/drivers/char/vr41xx_rtc.c +++ b/drivers/rtc/rtc-vr41xx.c @@ -1,7 +1,7 @@ /* - * Driver for NEC VR4100 series Real Time Clock unit. + * Driver for NEC VR4100 series Real Time Clock unit. * - * Copyright (C) 2003-2005 Yoichi Yuasa + * Copyright (C) 2003-2006 Yoichi Yuasa * * 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 @@ -17,23 +17,18 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include #include #include #include #include -#include -#include #include -#include +#include #include #include #include -#include #include #include -#include #include #include @@ -99,27 +94,11 @@ static void __iomem *rtc2_base; static unsigned long epoch = 1970; /* Jan 1 1970 00:00:00 */ -static spinlock_t rtc_task_lock; -static wait_queue_head_t rtc_wait; -static unsigned long rtc_irq_data; -static struct fasync_struct *rtc_async_queue; -static rtc_task_t *rtc_callback; +static spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; static char rtc_name[] = "RTC"; static unsigned long periodic_frequency; static unsigned long periodic_count; -typedef enum { - RTC_RELEASE, - RTC_OPEN, -} rtc_status_t; - -static rtc_status_t rtc_status; - -typedef enum { - FUNCTION_RTC_IOCTL, - FUNCTION_RTC_CONTROL, -} rtc_callfrom_t; - struct resource rtc_resource[2] = { { .name = rtc_name, .flags = IORESOURCE_MEM, }, @@ -129,7 +108,9 @@ struct resource rtc_resource[2] = { static inline unsigned long read_elapsed_second(void) { + unsigned long first_low, first_mid, first_high; + unsigned long second_low, second_mid, second_high; do { @@ -156,9 +137,70 @@ static inline void write_elapsed_second(unsigned long sec) spin_unlock_irq(&rtc_lock); } -static void set_alarm(struct rtc_time *time) +static void vr41xx_rtc_release(struct device *dev) +{ + + spin_lock_irq(&rtc_lock); + + rtc1_write(ECMPLREG, 0); + rtc1_write(ECMPMREG, 0); + rtc1_write(ECMPHREG, 0); + rtc1_write(RTCL1LREG, 0); + rtc1_write(RTCL1HREG, 0); + + spin_unlock_irq(&rtc_lock); + + disable_irq(ELAPSEDTIME_IRQ); + disable_irq(RTCLONG1_IRQ); +} + +static int vr41xx_rtc_read_time(struct device *dev, struct rtc_time *time) +{ + unsigned long epoch_sec, elapsed_sec; + + epoch_sec = mktime(epoch, 1, 1, 0, 0, 0); + elapsed_sec = read_elapsed_second(); + + rtc_time_to_tm(epoch_sec + elapsed_sec, time); + + return 0; +} + +static int vr41xx_rtc_set_time(struct device *dev, struct rtc_time *time) +{ + unsigned long epoch_sec, current_sec; + + epoch_sec = mktime(epoch, 1, 1, 0, 0, 0); + current_sec = mktime(time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, + time->tm_hour, time->tm_min, time->tm_sec); + + write_elapsed_second(current_sec - epoch_sec); + + return 0; +} + +static int vr41xx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + unsigned long low, mid, high; + struct rtc_time *time = &wkalrm->time; + + spin_lock_irq(&rtc_lock); + + low = rtc1_read(ECMPLREG); + mid = rtc1_read(ECMPMREG); + high = rtc1_read(ECMPHREG); + + spin_unlock_irq(&rtc_lock); + + rtc_time_to_tm((high << 17) | (mid << 1) | (low >> 15), time); + + return 0; +} + +static int vr41xx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) { unsigned long alarm_sec; + struct rtc_time *time = &wkalrm->time; alarm_sec = mktime(time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec); @@ -170,111 +212,12 @@ static void set_alarm(struct rtc_time *time) rtc1_write(ECMPHREG, (uint16_t)(alarm_sec >> 17)); spin_unlock_irq(&rtc_lock); -} - -static void read_alarm(struct rtc_time *time) -{ - unsigned long low, mid, high; - - spin_lock_irq(&rtc_lock); - - low = rtc1_read(ECMPLREG); - mid = rtc1_read(ECMPMREG); - high = rtc1_read(ECMPHREG); - - spin_unlock_irq(&rtc_lock); - - to_tm((high << 17) | (mid << 1) | (low >> 15), time); - time->tm_year -= 1900; -} - -static void read_time(struct rtc_time *time) -{ - unsigned long epoch_sec, elapsed_sec; - - epoch_sec = mktime(epoch, 1, 1, 0, 0, 0); - elapsed_sec = read_elapsed_second(); - - to_tm(epoch_sec + elapsed_sec, time); - time->tm_year -= 1900; -} - -static void set_time(struct rtc_time *time) -{ - unsigned long epoch_sec, current_sec; - - epoch_sec = mktime(epoch, 1, 1, 0, 0, 0); - current_sec = mktime(time->tm_year + 1900, time->tm_mon + 1, time->tm_mday, - time->tm_hour, time->tm_min, time->tm_sec); - - write_elapsed_second(current_sec - epoch_sec); -} - -static ssize_t rtc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long irq_data; - int retval = 0; - - if (count != sizeof(unsigned int) && count != sizeof(unsigned long)) - return -EINVAL; - - add_wait_queue(&rtc_wait, &wait); - - do { - __set_current_state(TASK_INTERRUPTIBLE); - - spin_lock_irq(&rtc_lock); - irq_data = rtc_irq_data; - rtc_irq_data = 0; - spin_unlock_irq(&rtc_lock); - - if (irq_data != 0) - break; - - if (file->f_flags & O_NONBLOCK) { - retval = -EAGAIN; - break; - } - - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - } while (1); - - if (retval == 0) { - if (count == sizeof(unsigned int)) { - retval = put_user(irq_data, (unsigned int __user *)buf); - if (retval == 0) - retval = sizeof(unsigned int); - } else { - retval = put_user(irq_data, (unsigned long __user *)buf); - if (retval == 0) - retval = sizeof(unsigned long); - } - - } - - __set_current_state(TASK_RUNNING); - remove_wait_queue(&rtc_wait, &wait); - - return retval; -} - -static unsigned int rtc_poll(struct file *file, struct poll_table_struct *table) -{ - poll_wait(file, &rtc_wait, table); - - if (rtc_irq_data != 0) - return POLLIN | POLLRDNORM; return 0; } -static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, rtc_callfrom_t from) +static int vr41xx_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) { - struct rtc_time time; unsigned long count; switch (cmd) { @@ -290,33 +233,6 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, rtc_callfrom_t from case RTC_PIE_OFF: disable_irq(RTCLONG1_IRQ); break; - case RTC_ALM_SET: - if (copy_from_user(&time, (struct rtc_time __user *)arg, - sizeof(struct rtc_time))) - return -EFAULT; - - set_alarm(&time); - break; - case RTC_ALM_READ: - memset(&time, 0, sizeof(struct rtc_time)); - read_alarm(&time); - break; - case RTC_RD_TIME: - memset(&time, 0, sizeof(struct rtc_time)); - read_time(&time); - if (copy_to_user((void __user *)arg, &time, sizeof(struct rtc_time))) - return -EFAULT; - break; - case RTC_SET_TIME: - if (capable(CAP_SYS_TIME) == 0) - return -EACCES; - - if (copy_from_user(&time, (struct rtc_time __user *)arg, - sizeof(struct rtc_time))) - return -EFAULT; - - set_time(&time); - break; case RTC_IRQP_READ: return put_user(periodic_frequency, (unsigned long __user *)arg); break; @@ -324,8 +240,7 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, rtc_callfrom_t from if (arg > MAX_PERIODIC_RATE) return -EINVAL; - if (from == FUNCTION_RTC_IOCTL && arg > MAX_USER_PERIODIC_RATE && - capable(CAP_SYS_RESOURCE) == 0) + if (arg > MAX_USER_PERIODIC_RATE && capable(CAP_SYS_RESOURCE) == 0) return -EACCES; periodic_frequency = arg; @@ -361,205 +276,46 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, rtc_callfrom_t from return 0; } -static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - return rtc_do_ioctl(cmd, arg, FUNCTION_RTC_IOCTL); -} - -static int rtc_open(struct inode *inode, struct file *file) -{ - spin_lock_irq(&rtc_lock); - - if (rtc_status == RTC_OPEN) { - spin_unlock_irq(&rtc_lock); - return -EBUSY; - } - - rtc_status = RTC_OPEN; - rtc_irq_data = 0; - - spin_unlock_irq(&rtc_lock); - - return 0; -} - -static int rtc_release(struct inode *inode, struct file *file) -{ - if (file->f_flags & FASYNC) - (void)fasync_helper(-1, file, 0, &rtc_async_queue); - - spin_lock_irq(&rtc_lock); - - rtc1_write(ECMPLREG, 0); - rtc1_write(ECMPMREG, 0); - rtc1_write(ECMPHREG, 0); - rtc1_write(RTCL1LREG, 0); - rtc1_write(RTCL1HREG, 0); - - rtc_status = RTC_RELEASE; - - spin_unlock_irq(&rtc_lock); - - disable_irq(ELAPSEDTIME_IRQ); - disable_irq(RTCLONG1_IRQ); - - return 0; -} - -static int rtc_fasync(int fd, struct file *file, int on) -{ - return fasync_helper(fd, file, on, &rtc_async_queue); -} - -static struct file_operations rtc_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = rtc_read, - .poll = rtc_poll, - .ioctl = rtc_ioctl, - .open = rtc_open, - .release = rtc_release, - .fasync = rtc_fasync, -}; - static irqreturn_t elapsedtime_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - spin_lock(&rtc_lock); + struct platform_device *pdev = (struct platform_device *)dev_id; + struct rtc_device *rtc = platform_get_drvdata(pdev); + rtc2_write(RTCINTREG, ELAPSEDTIME_INT); - rtc_irq_data += 0x100; - rtc_irq_data &= ~0xff; - rtc_irq_data |= RTC_AF; - spin_unlock(&rtc_lock); - - spin_lock(&rtc_lock); - if (rtc_callback) - rtc_callback->func(rtc_callback->private_data); - spin_unlock(&rtc_lock); - - wake_up_interruptible(&rtc_wait); - - kill_fasync(&rtc_async_queue, SIGIO, POLL_IN); + rtc_update_irq(&rtc->class_dev, 1, RTC_AF); return IRQ_HANDLED; } static irqreturn_t rtclong1_interrupt(int irq, void *dev_id, struct pt_regs *regs) { + struct platform_device *pdev = (struct platform_device *)dev_id; + struct rtc_device *rtc = platform_get_drvdata(pdev); unsigned long count = periodic_count; - spin_lock(&rtc_lock); rtc2_write(RTCINTREG, RTCLONG1_INT); rtc1_write(RTCL1LREG, count); rtc1_write(RTCL1HREG, count >> 16); - rtc_irq_data += 0x100; - rtc_irq_data &= ~0xff; - rtc_irq_data |= RTC_PF; - spin_unlock(&rtc_lock); - - spin_lock(&rtc_task_lock); - if (rtc_callback) - rtc_callback->func(rtc_callback->private_data); - spin_unlock(&rtc_task_lock); - - wake_up_interruptible(&rtc_wait); - - kill_fasync(&rtc_async_queue, SIGIO, POLL_IN); + rtc_update_irq(&rtc->class_dev, 1, RTC_PF); return IRQ_HANDLED; } -int rtc_register(rtc_task_t *task) -{ - if (task == NULL || task->func == NULL) - return -EINVAL; - - spin_lock_irq(&rtc_lock); - if (rtc_status == RTC_OPEN) { - spin_unlock_irq(&rtc_lock); - return -EBUSY; - } - - spin_lock(&rtc_task_lock); - if (rtc_callback != NULL) { - spin_unlock(&rtc_task_lock); - spin_unlock_irq(&rtc_task_lock); - return -EBUSY; - } - - rtc_callback = task; - spin_unlock(&rtc_task_lock); - - rtc_status = RTC_OPEN; - - spin_unlock_irq(&rtc_lock); - - return 0; -} - -EXPORT_SYMBOL_GPL(rtc_register); - -int rtc_unregister(rtc_task_t *task) -{ - spin_lock_irq(&rtc_task_lock); - if (task == NULL || rtc_callback != task) { - spin_unlock_irq(&rtc_task_lock); - return -ENXIO; - } - - spin_lock(&rtc_lock); - - rtc1_write(ECMPLREG, 0); - rtc1_write(ECMPMREG, 0); - rtc1_write(ECMPHREG, 0); - rtc1_write(RTCL1LREG, 0); - rtc1_write(RTCL1HREG, 0); - - rtc_status = RTC_RELEASE; - - spin_unlock(&rtc_lock); - - rtc_callback = NULL; - - spin_unlock_irq(&rtc_task_lock); - - disable_irq(ELAPSEDTIME_IRQ); - disable_irq(RTCLONG1_IRQ); - - return 0; -} - -EXPORT_SYMBOL_GPL(rtc_unregister); - -int rtc_control(rtc_task_t *task, unsigned int cmd, unsigned long arg) -{ - int retval = 0; - - spin_lock_irq(&rtc_task_lock); - - if (rtc_callback != task) - retval = -ENXIO; - else - rtc_do_ioctl(cmd, arg, FUNCTION_RTC_CONTROL); - - spin_unlock_irq(&rtc_task_lock); - - return retval; -} - -EXPORT_SYMBOL_GPL(rtc_control); - -static struct miscdevice rtc_miscdevice = { - .minor = RTC_MINOR, - .name = rtc_name, - .fops = &rtc_fops, +static struct rtc_class_ops vr41xx_rtc_ops = { + .release = vr41xx_rtc_release, + .ioctl = vr41xx_rtc_ioctl, + .read_time = vr41xx_rtc_read_time, + .set_time = vr41xx_rtc_set_time, + .read_alarm = vr41xx_rtc_read_alarm, + .set_alarm = vr41xx_rtc_set_alarm, }; static int __devinit rtc_probe(struct platform_device *pdev) { + struct rtc_device *rtc; unsigned int irq; int retval; @@ -577,13 +333,13 @@ static int __devinit rtc_probe(struct platform_device *pdev) return -EBUSY; } - retval = misc_register(&rtc_miscdevice); - if (retval < 0) { + rtc = rtc_device_register(rtc_name, &pdev->dev, &vr41xx_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) { iounmap(rtc1_base); iounmap(rtc2_base); rtc1_base = NULL; rtc2_base = NULL; - return retval; + return PTR_ERR(rtc); } spin_lock_irq(&rtc_lock); @@ -594,24 +350,20 @@ static int __devinit rtc_probe(struct platform_device *pdev) rtc1_write(RTCL1LREG, 0); rtc1_write(RTCL1HREG, 0); - rtc_status = RTC_RELEASE; - rtc_irq_data = 0; - spin_unlock_irq(&rtc_lock); - init_waitqueue_head(&rtc_wait); - irq = ELAPSEDTIME_IRQ; retval = request_irq(irq, elapsedtime_interrupt, SA_INTERRUPT, - "elapsed_time", NULL); + "elapsed_time", pdev); if (retval == 0) { irq = RTCLONG1_IRQ; retval = request_irq(irq, rtclong1_interrupt, SA_INTERRUPT, - "rtclong1", NULL); + "rtclong1", pdev); } if (retval < 0) { printk(KERN_ERR "rtc: IRQ%d is busy\n", irq); + rtc_device_unregister(rtc); if (irq == RTCLONG1_IRQ) free_irq(ELAPSEDTIME_IRQ, NULL); iounmap(rtc1_base); @@ -621,23 +373,25 @@ static int __devinit rtc_probe(struct platform_device *pdev) return retval; } + platform_set_drvdata(pdev, rtc); + disable_irq(ELAPSEDTIME_IRQ); disable_irq(RTCLONG1_IRQ); - spin_lock_init(&rtc_task_lock); - printk(KERN_INFO "rtc: Real Time Clock of NEC VR4100 series\n"); return 0; } -static int __devexit rtc_remove(struct platform_device *dev) +static int __devexit rtc_remove(struct platform_device *pdev) { - int retval; + struct rtc_device *rtc; - retval = misc_deregister(&rtc_miscdevice); - if (retval < 0) - return retval; + rtc = platform_get_drvdata(pdev); + if (rtc != NULL) + rtc_device_unregister(rtc); + + platform_set_drvdata(pdev, NULL); free_irq(ELAPSEDTIME_IRQ, NULL); free_irq(RTCLONG1_IRQ, NULL); @@ -651,7 +405,7 @@ static int __devexit rtc_remove(struct platform_device *dev) static struct platform_device *rtc_platform_device; -static struct platform_driver rtc_device_driver = { +static struct platform_driver rtc_platform_driver = { .probe = rtc_probe, .remove = __devexit_p(rtc_remove), .driver = { @@ -686,7 +440,7 @@ static int __init vr41xx_rtc_init(void) } rtc_platform_device = platform_device_alloc("RTC", -1); - if (!rtc_platform_device) + if (rtc_platform_device == NULL) return -ENOMEM; retval = platform_device_add_resources(rtc_platform_device, @@ -700,7 +454,7 @@ static int __init vr41xx_rtc_init(void) return retval; } - retval = platform_driver_register(&rtc_device_driver); + retval = platform_driver_register(&rtc_platform_driver); if (retval < 0) platform_device_unregister(rtc_platform_device); @@ -709,7 +463,7 @@ static int __init vr41xx_rtc_init(void) static void __exit vr41xx_rtc_exit(void) { - platform_driver_unregister(&rtc_device_driver); + platform_driver_unregister(&rtc_platform_driver); platform_device_unregister(rtc_platform_device); } diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c index 621d17afc0d9..788b6d1f8f2f 100644 --- a/drivers/rtc/rtc-x1205.c +++ b/drivers/rtc/rtc-x1205.c @@ -19,7 +19,7 @@ #include #include -#define DRV_VERSION "1.0.6" +#define DRV_VERSION "1.0.7" /* Addresses to scan: none. This chip is located at * 0x6f and uses a two bytes register addressing. @@ -451,8 +451,6 @@ static int x1205_rtc_proc(struct device *dev, struct seq_file *seq) { int err, dtrim, atrim; - seq_printf(seq, "24hr\t\t: yes\n"); - if ((err = x1205_get_dtrim(to_i2c_client(dev), &dtrim)) == 0) seq_printf(seq, "digital_trim\t: %d ppm\n", dtrim); @@ -473,30 +471,31 @@ static struct rtc_class_ops x1205_rtc_ops = { static ssize_t x1205_sysfs_show_atrim(struct device *dev, struct device_attribute *attr, char *buf) { - int atrim; + int err, atrim; - if (x1205_get_atrim(to_i2c_client(dev), &atrim) == 0) - return sprintf(buf, "%d.%02d pF\n", - atrim / 1000, atrim % 1000); - return 0; + err = x1205_get_atrim(to_i2c_client(dev), &atrim); + if (err) + return err; + + return sprintf(buf, "%d.%02d pF\n", atrim / 1000, atrim % 1000); } static DEVICE_ATTR(atrim, S_IRUGO, x1205_sysfs_show_atrim, NULL); static ssize_t x1205_sysfs_show_dtrim(struct device *dev, struct device_attribute *attr, char *buf) { - int dtrim; + int err, dtrim; - if (x1205_get_dtrim(to_i2c_client(dev), &dtrim) == 0) - return sprintf(buf, "%d ppm\n", dtrim); + err = x1205_get_dtrim(to_i2c_client(dev), &dtrim); + if (err) + return err; - return 0; + return sprintf(buf, "%d ppm\n", dtrim); } static DEVICE_ATTR(dtrim, S_IRUGO, x1205_sysfs_show_dtrim, NULL); static int x1205_attach(struct i2c_adapter *adapter) { - dev_dbg(&adapter->dev, "%s\n", __FUNCTION__); return i2c_probe(adapter, &addr_data, x1205_probe); } @@ -545,8 +544,6 @@ static int x1205_probe(struct i2c_adapter *adapter, int address, int kind) if (IS_ERR(rtc)) { err = PTR_ERR(rtc); - dev_err(&client->dev, - "unable to register the class device\n"); goto exit_detach; } @@ -585,8 +582,6 @@ static int x1205_detach(struct i2c_client *client) int err; struct rtc_device *rtc = i2c_get_clientdata(client); - dev_dbg(&client->dev, "%s\n", __FUNCTION__); - if (rtc) rtc_device_unregister(rtc); diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 0a9f12c4e911..a3bfebcf31ef 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1257,25 +1257,28 @@ __dasd_start_head(struct dasd_device * device) if (list_empty(&device->ccw_queue)) return; cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list); - /* check FAILFAST */ + if (cqr->status != DASD_CQR_QUEUED) + return; + /* Non-temporary stop condition will trigger fail fast */ if (device->stopped & ~DASD_STOPPED_PENDING && test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && (!dasd_eer_enabled(device))) { cqr->status = DASD_CQR_FAILED; dasd_schedule_bh(device); + return; } - if ((cqr->status == DASD_CQR_QUEUED) && - (!device->stopped)) { - /* try to start the first I/O that can be started */ - rc = device->discipline->start_IO(cqr); - if (rc == 0) - dasd_set_timer(device, cqr->expires); - else if (rc == -EACCES) { - dasd_schedule_bh(device); - } else - /* Hmpf, try again in 1/2 sec */ - dasd_set_timer(device, 50); - } + /* Don't try to start requests if device is stopped */ + if (device->stopped) + return; + + rc = device->discipline->start_IO(cqr); + if (rc == 0) + dasd_set_timer(device, cqr->expires); + else if (rc == -EACCES) { + dasd_schedule_bh(device); + } else + /* Hmpf, try again in 1/2 sec */ + dasd_set_timer(device, 50); } /* @@ -1968,7 +1971,7 @@ int dasd_generic_set_offline (struct ccw_device *cdev) { struct dasd_device *device; - int max_count; + int max_count, open_count; device = dasd_device_from_cdev(cdev); if (IS_ERR(device)) @@ -1985,10 +1988,16 @@ dasd_generic_set_offline (struct ccw_device *cdev) * in the other openers. */ max_count = device->bdev ? 0 : -1; - if (atomic_read(&device->open_count) > max_count) { - printk (KERN_WARNING "Can't offline dasd device with open" - " count = %i.\n", - atomic_read(&device->open_count)); + open_count = (int) atomic_read(&device->open_count); + if (open_count > max_count) { + if (open_count > 0) + printk (KERN_WARNING "Can't offline dasd device with " + "open count = %i.\n", + open_count); + else + printk (KERN_WARNING "%s", + "Can't offline dasd device due to internal " + "use\n"); clear_bit(DASD_FLAG_OFFLINE, &device->flags); dasd_put_device(device); return -EBUSY; diff --git a/drivers/s390/block/dasd_erp.c b/drivers/s390/block/dasd_erp.c index 8fd71ab02ef0..b842377cb0c6 100644 --- a/drivers/s390/block/dasd_erp.c +++ b/drivers/s390/block/dasd_erp.c @@ -32,9 +32,8 @@ dasd_alloc_erp_request(char *magic, int cplength, int datasize, int size; /* Sanity checks */ - if ( magic == NULL || datasize > PAGE_SIZE || - (cplength*sizeof(struct ccw1)) > PAGE_SIZE) - BUG(); + BUG_ON( magic == NULL || datasize > PAGE_SIZE || + (cplength*sizeof(struct ccw1)) > PAGE_SIZE); size = (sizeof(struct dasd_ccw_req) + 7L) & -8L; if (cplength > 0) @@ -125,8 +124,7 @@ dasd_default_erp_postaction(struct dasd_ccw_req * cqr) struct dasd_device *device; int success; - if (cqr->refers == NULL || cqr->function == NULL) - BUG(); + BUG_ON(cqr->refers == NULL || cqr->function == NULL); device = cqr->device; success = cqr->status == DASD_CQR_DONE; diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c index 1aa3c261718a..ad23aede356c 100644 --- a/drivers/s390/block/dasd_proc.c +++ b/drivers/s390/block/dasd_proc.c @@ -294,23 +294,40 @@ out_error: #endif /* CONFIG_DASD_PROFILE */ } +/* + * Create dasd proc-fs entries. + * In case creation failed, cleanup and return -ENOENT. + */ int dasd_proc_init(void) { dasd_proc_root_entry = proc_mkdir("dasd", &proc_root); + if (!dasd_proc_root_entry) + goto out_nodasd; dasd_proc_root_entry->owner = THIS_MODULE; dasd_devices_entry = create_proc_entry("devices", S_IFREG | S_IRUGO | S_IWUSR, dasd_proc_root_entry); + if (!dasd_devices_entry) + goto out_nodevices; dasd_devices_entry->proc_fops = &dasd_devices_file_ops; dasd_devices_entry->owner = THIS_MODULE; dasd_statistics_entry = create_proc_entry("statistics", S_IFREG | S_IRUGO | S_IWUSR, dasd_proc_root_entry); + if (!dasd_statistics_entry) + goto out_nostatistics; dasd_statistics_entry->read_proc = dasd_statistics_read; dasd_statistics_entry->write_proc = dasd_statistics_write; dasd_statistics_entry->owner = THIS_MODULE; return 0; + + out_nostatistics: + remove_proc_entry("devices", dasd_proc_root_entry); + out_nodevices: + remove_proc_entry("dasd", &proc_root); + out_nodasd: + return -ENOENT; } void diff --git a/drivers/s390/char/keyboard.c b/drivers/s390/char/keyboard.c index 6badd8403409..d4d2ff0a9da2 100644 --- a/drivers/s390/char/keyboard.c +++ b/drivers/s390/char/keyboard.c @@ -54,7 +54,7 @@ kbd_alloc(void) { if (!kbd) goto out; kbd->key_maps = kzalloc(sizeof(key_maps), GFP_KERNEL); - if (!key_maps) + if (!kbd->key_maps) goto out_kbd; for (i = 0; i < ARRAY_SIZE(key_maps); i++) { if (key_maps[i]) { diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c index ac10dfb20a62..91e93c78f57a 100644 --- a/drivers/s390/char/sclp_rw.c +++ b/drivers/s390/char/sclp_rw.c @@ -24,7 +24,7 @@ /* * The room for the SCCB (only for writing) is not equal to a pages size - * (as it is specified as the maximum size in the the SCLP ducumentation) + * (as it is specified as the maximum size in the the SCLP documentation) * because of the additional data structure described above. */ #define MAX_SCCB_ROOM (PAGE_SIZE - sizeof(struct sclp_buffer)) diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c index 5ced2725d6c7..b70d92690242 100644 --- a/drivers/s390/char/tape_block.c +++ b/drivers/s390/char/tape_block.c @@ -198,9 +198,7 @@ tapeblock_request_fn(request_queue_t *queue) device = (struct tape_device *) queue->queuedata; DBF_LH(6, "tapeblock_request_fn(device=%p)\n", device); - if (device == NULL) - BUG(); - + BUG_ON(device == NULL); tapeblock_trigger_requeue(device); } @@ -307,8 +305,7 @@ tapeblock_revalidate_disk(struct gendisk *disk) int rc; device = (struct tape_device *) disk->private_data; - if (!device) - BUG(); + BUG_ON(!device); if (!device->blk_data.medium_changed) return 0; @@ -435,16 +432,14 @@ tapeblock_ioctl( ) { int rc; int minor; - struct gendisk *disk = inode->i_bdev->bd_disk; - struct tape_device *device = disk->private_data; + struct gendisk *disk; + struct tape_device *device; rc = 0; disk = inode->i_bdev->bd_disk; - if (!disk) - BUG(); + BUG_ON(!disk); device = disk->private_data; - if (!device) - BUG(); + BUG_ON(!device); minor = iminor(inode); DBF_LH(6, "tapeblock_ioctl(0x%0x)\n", command); diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index 389ee2c0f443..e6e4086d3224 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c @@ -210,18 +210,14 @@ tape_state_set(struct tape_device *device, enum tape_state newstate) return; } DBF_EVENT(4, "ts. dev: %x\n", device->first_minor); - if (device->tape_state < TO_SIZE && device->tape_state >= 0) - str = tape_state_verbose[device->tape_state]; - else - str = "UNKNOWN TS"; - DBF_EVENT(4, "old ts: %s\n", str); - if (device->tape_state < TO_SIZE && device->tape_state >=0 ) + DBF_EVENT(4, "old ts:\t\n"); + if (device->tape_state < TS_SIZE && device->tape_state >=0 ) str = tape_state_verbose[device->tape_state]; else str = "UNKNOWN TS"; DBF_EVENT(4, "%s\n", str); DBF_EVENT(4, "new ts:\t\n"); - if (newstate < TO_SIZE && newstate >= 0) + if (newstate < TS_SIZE && newstate >= 0) str = tape_state_verbose[newstate]; else str = "UNKNOWN TS"; diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c index cb8e2e672b68..0960bef7b199 100644 --- a/drivers/s390/cio/blacklist.c +++ b/drivers/s390/cio/blacklist.c @@ -414,11 +414,11 @@ cio_ignore_proc_init (void) entry = create_proc_entry ("cio_ignore", S_IFREG | S_IRUGO | S_IWUSR, &proc_root); if (!entry) - return 0; + return -ENOENT; entry->proc_fops = &cio_ignore_proc_fops; - return 1; + return 0; } __initcall (cio_ignore_proc_init); diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index cbb86fa5f293..5b20d8c9c025 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -67,7 +67,7 @@ cio_debug_init (void) goto out_unregister; debug_register_view (cio_debug_msg_id, &debug_sprintf_view); debug_set_level (cio_debug_msg_id, 2); - cio_debug_trace_id = debug_register ("cio_trace", 16, 4, 8); + cio_debug_trace_id = debug_register ("cio_trace", 16, 4, 16); if (!cio_debug_trace_id) goto out_unregister; debug_register_view (cio_debug_trace_id, &debug_hex_ascii_view); diff --git a/drivers/s390/cio/cio_debug.h b/drivers/s390/cio/cio_debug.h index 6af8b27d366b..f88844adae1b 100644 --- a/drivers/s390/cio/cio_debug.h +++ b/drivers/s390/cio/cio_debug.h @@ -3,6 +3,11 @@ #include +/* for use of debug feature */ +extern debug_info_t *cio_debug_msg_id; +extern debug_info_t *cio_debug_trace_id; +extern debug_info_t *cio_debug_crw_id; + #define CIO_TRACE_EVENT(imp, txt) do { \ debug_text_event(cio_debug_trace_id, imp, txt); \ } while (0) @@ -15,18 +20,19 @@ debug_sprintf_event(cio_debug_crw_id, imp , ##args); \ } while (0) -#define CIO_HEX_EVENT(imp, args...) do { \ - debug_event(cio_debug_trace_id, imp, ##args); \ - } while (0) +static inline void +CIO_HEX_EVENT(int level, void *data, int length) +{ + while (length > 0) { + debug_event(cio_debug_trace_id, level, data, length); + length -= cio_debug_trace_id->buf_size; + data += cio_debug_trace_id->buf_size; + } +} #define CIO_DEBUG(printk_level,event_level,msg...) ({ \ if (cio_show_msg) printk(printk_level msg); \ CIO_MSG_EVENT (event_level, msg); \ }) -/* for use of debug feature */ -extern debug_info_t *cio_debug_msg_id; -extern debug_info_t *cio_debug_trace_id; -extern debug_info_t *cio_debug_crw_id; - #endif diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index edcf05d5d568..5d6b7a57b02f 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -675,9 +675,8 @@ lcs_ready_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer) int index, rc; LCS_DBF_TEXT(5, trace, "rdybuff"); - if (buffer->state != BUF_STATE_LOCKED && - buffer->state != BUF_STATE_PROCESSED) - BUG(); + BUG_ON(buffer->state != BUF_STATE_LOCKED && + buffer->state != BUF_STATE_PROCESSED); spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); buffer->state = BUF_STATE_READY; index = buffer - channel->iob; @@ -701,8 +700,7 @@ __lcs_processed_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer) int index, prev, next; LCS_DBF_TEXT(5, trace, "prcsbuff"); - if (buffer->state != BUF_STATE_READY) - BUG(); + BUG_ON(buffer->state != BUF_STATE_READY); buffer->state = BUF_STATE_PROCESSED; index = buffer - channel->iob; prev = (index - 1) & (LCS_NUM_BUFFS - 1); @@ -734,9 +732,8 @@ lcs_release_buffer(struct lcs_channel *channel, struct lcs_buffer *buffer) unsigned long flags; LCS_DBF_TEXT(5, trace, "relbuff"); - if (buffer->state != BUF_STATE_LOCKED && - buffer->state != BUF_STATE_PROCESSED) - BUG(); + BUG_ON(buffer->state != BUF_STATE_LOCKED && + buffer->state != BUF_STATE_PROCESSED); spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags); buffer->state = BUF_STATE_EMPTY; spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags); diff --git a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c index 25f678d0780b..e8e41e6eb42a 100644 --- a/drivers/scsi/3w-xxxx.c +++ b/drivers/scsi/3w-xxxx.c @@ -1508,10 +1508,12 @@ static void tw_transfer_internal(TW_Device_Extension *tw_dev, int request_id, struct scsi_cmnd *cmd = tw_dev->srb[request_id]; void *buf; unsigned int transfer_len; + unsigned long flags = 0; if (cmd->use_sg) { struct scatterlist *sg = (struct scatterlist *)cmd->request_buffer; + local_irq_save(flags); buf = kmap_atomic(sg->page, KM_IRQ0) + sg->offset; transfer_len = min(sg->length, len); } else { @@ -1526,6 +1528,7 @@ static void tw_transfer_internal(TW_Device_Extension *tw_dev, int request_id, sg = (struct scatterlist *)cmd->request_buffer; kunmap_atomic(buf - sg->offset, KM_IRQ0); + local_irq_restore(flags); } } diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index e513c3158ad9..503f189dab3b 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -165,7 +165,7 @@ ncr53c8xx-flags-$(CONFIG_SCSI_ZALON) \ CFLAGS_ncr53c8xx.o := $(ncr53c8xx-flags-y) $(ncr53c8xx-flags-m) zalon7xx-objs := zalon.o ncr53c8xx.o NCR_Q720_mod-objs := NCR_Q720.o ncr53c8xx.o -libata-objs := libata-core.o libata-scsi.o libata-bmdma.o +libata-objs := libata-core.o libata-scsi.o libata-bmdma.o libata-eh.o oktagon_esp_mod-objs := oktagon_esp.o oktagon_io.o # Files generated that shall be removed upon make clean diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index ffba65656a83..1b8429cb3c96 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -48,7 +48,7 @@ #include #define DRV_NAME "ahci" -#define DRV_VERSION "1.2" +#define DRV_VERSION "1.3" enum { @@ -207,7 +207,6 @@ static struct scsi_host_template ahci_sht = { .name = DRV_NAME, .ioctl = ata_scsi_ioctl, .queuecommand = ata_scsi_queuecmd, - .eh_strategy_handler = ata_scsi_error, .can_queue = ATA_DEF_QUEUE, .this_id = ATA_SHT_THIS_ID, .sg_tablesize = AHCI_MAX_SG, @@ -293,6 +292,10 @@ static const struct pci_device_id ahci_pci_tbl[] = { board_ahci }, /* JMicron JMB360 */ { 0x197b, 0x2363, PCI_ANY_ID, PCI_ANY_ID, 0, 0, board_ahci }, /* JMicron JMB363 */ + { PCI_VENDOR_ID_ATI, 0x4380, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ATI SB600 non-raid */ + { PCI_VENDOR_ID_ATI, 0x4381, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ATI SB600 raid */ { } /* terminate list */ }; @@ -513,25 +516,7 @@ static void ahci_fill_cmd_slot(struct ahci_port_priv *pp, u32 opts) pp->cmd_slot[0].tbl_addr_hi = cpu_to_le32((pp->cmd_tbl_dma >> 16) >> 16); } -static int ahci_poll_register(void __iomem *reg, u32 mask, u32 val, - unsigned long interval_msec, - unsigned long timeout_msec) -{ - unsigned long timeout; - u32 tmp; - - timeout = jiffies + (timeout_msec * HZ) / 1000; - do { - tmp = readl(reg); - if ((tmp & mask) == val) - return 0; - msleep(interval_msec); - } while (time_before(jiffies, timeout)); - - return -1; -} - -static int ahci_softreset(struct ata_port *ap, int verbose, unsigned int *class) +static int ahci_softreset(struct ata_port *ap, unsigned int *class) { struct ahci_host_priv *hpriv = ap->host_set->private_data; struct ahci_port_priv *pp = ap->private_data; @@ -540,11 +525,18 @@ static int ahci_softreset(struct ata_port *ap, int verbose, unsigned int *class) const u32 cmd_fis_len = 5; /* five dwords */ const char *reason = NULL; struct ata_taskfile tf; + u32 tmp; u8 *fis; int rc; DPRINTK("ENTER\n"); + if (!sata_dev_present(ap)) { + DPRINTK("PHY reports no device\n"); + *class = ATA_DEV_NONE; + return 0; + } + /* prepare for SRST (AHCI-1.1 10.4.1) */ rc = ahci_stop_engine(ap); if (rc) { @@ -555,8 +547,6 @@ static int ahci_softreset(struct ata_port *ap, int verbose, unsigned int *class) /* check BUSY/DRQ, perform Command List Override if necessary */ ahci_tf_read(ap, &tf); if (tf.command & (ATA_BUSY | ATA_DRQ)) { - u32 tmp; - if (!(hpriv->cap & HOST_CAP_CLO)) { rc = -EIO; reason = "port busy but no CLO"; @@ -566,10 +556,10 @@ static int ahci_softreset(struct ata_port *ap, int verbose, unsigned int *class) tmp = readl(port_mmio + PORT_CMD); tmp |= PORT_CMD_CLO; writel(tmp, port_mmio + PORT_CMD); - readl(port_mmio + PORT_CMD); /* flush */ - if (ahci_poll_register(port_mmio + PORT_CMD, PORT_CMD_CLO, 0x0, - 1, 500)) { + tmp = ata_wait_register(port_mmio + PORT_CMD, + PORT_CMD_CLO, PORT_CMD_CLO, 1, 500); + if (tmp & PORT_CMD_CLO) { rc = -EIO; reason = "CLO failed"; goto fail_restart; @@ -590,9 +580,9 @@ static int ahci_softreset(struct ata_port *ap, int verbose, unsigned int *class) fis[1] &= ~(1 << 7); /* turn off Command FIS bit */ writel(1, port_mmio + PORT_CMD_ISSUE); - readl(port_mmio + PORT_CMD_ISSUE); /* flush */ - if (ahci_poll_register(port_mmio + PORT_CMD_ISSUE, 0x1, 0x0, 1, 500)) { + tmp = ata_wait_register(port_mmio + PORT_CMD_ISSUE, 0x1, 0x1, 1, 500); + if (tmp & 0x1) { rc = -EIO; reason = "1st FIS failed"; goto fail; @@ -637,22 +627,19 @@ static int ahci_softreset(struct ata_port *ap, int verbose, unsigned int *class) fail_restart: ahci_start_engine(ap); fail: - if (verbose) - printk(KERN_ERR "ata%u: softreset failed (%s)\n", - ap->id, reason); - else - DPRINTK("EXIT, rc=%d reason=\"%s\"\n", rc, reason); + printk(KERN_ERR "ata%u: softreset failed (%s)\n", + ap->id, reason); return rc; } -static int ahci_hardreset(struct ata_port *ap, int verbose, unsigned int *class) +static int ahci_hardreset(struct ata_port *ap, unsigned int *class) { int rc; DPRINTK("ENTER\n"); ahci_stop_engine(ap); - rc = sata_std_hardreset(ap, verbose, class); + rc = sata_std_hardreset(ap, class); ahci_start_engine(ap); if (rc == 0) diff --git a/drivers/scsi/aic7xxx/Kconfig.aic7xxx b/drivers/scsi/aic7xxx/Kconfig.aic7xxx index 6c2c395554ff..5517da5855f0 100644 --- a/drivers/scsi/aic7xxx/Kconfig.aic7xxx +++ b/drivers/scsi/aic7xxx/Kconfig.aic7xxx @@ -86,7 +86,7 @@ config AIC7XXX_DEBUG_MASK default "0" help Bit mask of debug options that is only valid if the - CONFIG_AIC7XXX_DEBUG_ENBLE option is enabled. The bits in this mask + CONFIG_AIC7XXX_DEBUG_ENABLE option is enabled. The bits in this mask are defined in the drivers/scsi/aic7xxx/aic7xxx.h - search for the variable ahc_debug in that file to find them. diff --git a/drivers/scsi/ata_piix.c b/drivers/scsi/ata_piix.c index 2d5be84d8bd4..62dabf74188e 100644 --- a/drivers/scsi/ata_piix.c +++ b/drivers/scsi/ata_piix.c @@ -93,7 +93,7 @@ #include #define DRV_NAME "ata_piix" -#define DRV_VERSION "1.05" +#define DRV_VERSION "1.10" enum { PIIX_IOCFG = 0x54, /* IDE I/O configuration register */ @@ -209,7 +209,6 @@ static struct scsi_host_template piix_sht = { .name = DRV_NAME, .ioctl = ata_scsi_ioctl, .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, @@ -301,7 +300,7 @@ static struct piix_map_db ich6_map_db = { .mask = 0x3, .map = { /* PM PS SM SS MAP */ - { P0, P1, P2, P3 }, /* 00b */ + { P0, P2, P1, P3 }, /* 00b */ { IDE, IDE, P1, P3 }, /* 01b */ { P0, P2, IDE, IDE }, /* 10b */ { RV, RV, RV, RV }, @@ -312,7 +311,7 @@ static struct piix_map_db ich6m_map_db = { .mask = 0x3, .map = { /* PM PS SM SS MAP */ - { P0, P1, P2, P3 }, /* 00b */ + { P0, P2, RV, RV }, /* 00b */ { RV, RV, RV, RV }, { P0, P2, IDE, IDE }, /* 10b */ { RV, RV, RV, RV }, diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index ef57f253031c..dfcb96f3e60c 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -294,18 +294,6 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) if (sht->unchecked_isa_dma && privsize) gfp_mask |= __GFP_DMA; - /* Check to see if this host has any error handling facilities */ - if (!sht->eh_strategy_handler && !sht->eh_abort_handler && - !sht->eh_device_reset_handler && !sht->eh_bus_reset_handler && - !sht->eh_host_reset_handler) { - printk(KERN_ERR "ERROR: SCSI host `%s' has no error handling\n" - "ERROR: This is not a safe way to run your " - "SCSI host\n" - "ERROR: The error handling must be added to " - "this driver\n", sht->proc_name); - dump_stack(); - } - shost = kzalloc(sizeof(struct Scsi_Host) + privsize, gfp_mask); if (!shost) return NULL; diff --git a/drivers/scsi/ibmmca.c b/drivers/scsi/ibmmca.c index 3a8462e8d063..24eb59e143a9 100644 --- a/drivers/scsi/ibmmca.c +++ b/drivers/scsi/ibmmca.c @@ -2488,7 +2488,7 @@ static int option_setup(char *str) } ints[0] = i - 1; internal_ibmmca_scsi_setup(cur, ints); - return 0; + return 1; } __setup("ibmmcascsi=", option_setup); diff --git a/drivers/scsi/ibmvscsi/rpa_vscsi.c b/drivers/scsi/ibmvscsi/rpa_vscsi.c index f47dd87c05e7..892e8ed63091 100644 --- a/drivers/scsi/ibmvscsi/rpa_vscsi.c +++ b/drivers/scsi/ibmvscsi/rpa_vscsi.c @@ -80,7 +80,7 @@ void ibmvscsi_release_crq_queue(struct crq_queue *queue, tasklet_kill(&hostdata->srp_task); do { rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); - } while ((rc == H_Busy) || (H_isLongBusy(rc))); + } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); dma_unmap_single(hostdata->dev, queue->msg_token, queue->size * sizeof(*queue->msgs), DMA_BIDIRECTIONAL); @@ -230,7 +230,7 @@ int ibmvscsi_init_crq_queue(struct crq_queue *queue, rc = plpar_hcall_norets(H_REG_CRQ, vdev->unit_address, queue->msg_token, PAGE_SIZE); - if (rc == H_Resource) + if (rc == H_RESOURCE) /* maybe kexecing and resource is busy. try a reset */ rc = ibmvscsi_reset_crq_queue(queue, hostdata); @@ -269,7 +269,7 @@ int ibmvscsi_init_crq_queue(struct crq_queue *queue, req_irq_failed: do { rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); - } while ((rc == H_Busy) || (H_isLongBusy(rc))); + } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); reg_crq_failed: dma_unmap_single(hostdata->dev, queue->msg_token, @@ -295,7 +295,7 @@ int ibmvscsi_reenable_crq_queue(struct crq_queue *queue, /* Re-enable the CRQ */ do { rc = plpar_hcall_norets(H_ENABLE_CRQ, vdev->unit_address); - } while ((rc == H_InProgress) || (rc == H_Busy) || (H_isLongBusy(rc))); + } while ((rc == H_IN_PROGRESS) || (rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); if (rc) printk(KERN_ERR "ibmvscsi: Error %d enabling adapter\n", rc); @@ -317,7 +317,7 @@ int ibmvscsi_reset_crq_queue(struct crq_queue *queue, /* Close the CRQ */ do { rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); - } while ((rc == H_Busy) || (H_isLongBusy(rc))); + } while ((rc == H_BUSY) || (H_IS_LONG_BUSY(rc))); /* Clean out the queue */ memset(queue->msgs, 0x00, PAGE_SIZE); diff --git a/drivers/scsi/lasi700.c b/drivers/scsi/lasi700.c index 459a4daebece..eb7bd310cc82 100644 --- a/drivers/scsi/lasi700.c +++ b/drivers/scsi/lasi700.c @@ -112,7 +112,7 @@ lasi700_probe(struct parisc_device *dev) hostdata->dev = &dev->dev; dma_set_mask(&dev->dev, DMA_32BIT_MASK); - hostdata->base = ioremap(base, 0x100); + hostdata->base = ioremap_nocache(base, 0x100); hostdata->differential = 0; if (dev->id.sversion == LASI_700_SVERSION) { diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 21b0ed583b8a..509178c3700c 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -65,7 +65,6 @@ static unsigned int ata_dev_init_params(struct ata_port *ap, struct ata_device *dev, u16 heads, u16 sectors); -static void ata_set_mode(struct ata_port *ap); static unsigned int ata_dev_set_xfermode(struct ata_port *ap, struct ata_device *dev); static void ata_dev_xfermask(struct ata_port *ap, struct ata_device *dev); @@ -77,6 +76,10 @@ int atapi_enabled = 1; module_param(atapi_enabled, int, 0444); MODULE_PARM_DESC(atapi_enabled, "Enable discovery of ATAPI devices (0=off, 1=on)"); +int atapi_dmadir = 0; +module_param(atapi_dmadir, int, 0444); +MODULE_PARM_DESC(atapi_dmadir, "Enable ATAPI DMADIR bridge support (0=off, 1=on)"); + int libata_fua = 0; module_param_named(fua, libata_fua, int, 0444); MODULE_PARM_DESC(fua, "FUA support (0=off, 1=on)"); @@ -278,7 +281,7 @@ static void ata_unpack_xfermask(unsigned int xfer_mask, } static const struct ata_xfer_ent { - unsigned int shift, bits; + int shift, bits; u8 base; } ata_xfer_tbl[] = { { ATA_SHIFT_PIO, ATA_BITS_PIO, XFER_PIO_0 }, @@ -397,9 +400,21 @@ static const char *ata_mode_string(unsigned int xfer_mask) return ""; } -static void ata_dev_disable(struct ata_port *ap, struct ata_device *dev) +static const char *sata_spd_string(unsigned int spd) { - if (ata_dev_present(dev)) { + static const char * const spd_str[] = { + "1.5 Gbps", + "3.0 Gbps", + }; + + if (spd == 0 || (spd - 1) >= ARRAY_SIZE(spd_str)) + return ""; + return spd_str[spd - 1]; +} + +void ata_dev_disable(struct ata_port *ap, struct ata_device *dev) +{ + if (ata_dev_enabled(dev)) { printk(KERN_WARNING "ata%u: dev %u disabled\n", ap->id, dev->devno); dev->class++; @@ -949,6 +964,7 @@ void ata_qc_complete_internal(struct ata_queued_cmd *qc) * @ap: Port to which the command is sent * @dev: Device to which the command is sent * @tf: Taskfile registers for the command and the result + * @cdb: CDB for packet command * @dma_dir: Data tranfer direction of the command * @buf: Data buffer of the command * @buflen: Length of data buffer @@ -963,10 +979,9 @@ void ata_qc_complete_internal(struct ata_queued_cmd *qc) * None. Should be called with kernel context, might sleep. */ -static unsigned -ata_exec_internal(struct ata_port *ap, struct ata_device *dev, - struct ata_taskfile *tf, - int dma_dir, void *buf, unsigned int buflen) +unsigned ata_exec_internal(struct ata_port *ap, struct ata_device *dev, + struct ata_taskfile *tf, const u8 *cdb, + int dma_dir, void *buf, unsigned int buflen) { u8 command = tf->command; struct ata_queued_cmd *qc; @@ -980,6 +995,8 @@ ata_exec_internal(struct ata_port *ap, struct ata_device *dev, BUG_ON(qc == NULL); qc->tf = *tf; + if (cdb) + memcpy(qc->cdb, cdb, ATAPI_CDB_LEN); qc->dma_dir = dma_dir; if (dma_dir != DMA_NONE) { ata_sg_init_one(qc, buf, buflen); @@ -989,9 +1006,7 @@ ata_exec_internal(struct ata_port *ap, struct ata_device *dev, qc->private_data = &wait; qc->complete_fn = ata_qc_complete_internal; - qc->err_mask = ata_qc_issue(qc); - if (qc->err_mask) - ata_qc_complete(qc); + ata_qc_issue(qc); spin_unlock_irqrestore(&ap->host_set->lock, flags); @@ -1032,7 +1047,7 @@ ata_exec_internal(struct ata_port *ap, struct ata_device *dev, * * Kill the following code as soon as those drivers are fixed. */ - if (ap->flags & ATA_FLAG_PORT_DISABLED) { + if (ap->flags & ATA_FLAG_DISABLED) { err_mask |= AC_ERR_SYSTEM; ata_port_probe(ap); } @@ -1131,7 +1146,7 @@ static int ata_dev_read_id(struct ata_port *ap, struct ata_device *dev, tf.protocol = ATA_PROT_PIO; - err_mask = ata_exec_internal(ap, dev, &tf, DMA_FROM_DEVICE, + err_mask = ata_exec_internal(ap, dev, &tf, NULL, DMA_FROM_DEVICE, id, sizeof(id[0]) * ATA_ID_WORDS); if (err_mask) { rc = -EIO; @@ -1212,7 +1227,7 @@ static int ata_dev_configure(struct ata_port *ap, struct ata_device *dev, unsigned int xfer_mask; int i, rc; - if (!ata_dev_present(dev)) { + if (!ata_dev_enabled(dev)) { DPRINTK("ENTER/EXIT (host %u, dev %u) -- nodev\n", ap->id, dev->devno); return 0; @@ -1228,7 +1243,7 @@ static int ata_dev_configure(struct ata_port *ap, struct ata_device *dev, id[84], id[85], id[86], id[87], id[88]); /* initialize to-be-configured parameters */ - dev->flags = 0; + dev->flags &= ~ATA_DFLAG_CFG_MASK; dev->max_sectors = 0; dev->cdb_len = 0; dev->n_sectors = 0; @@ -1351,16 +1366,24 @@ err_out_nosup: * PCI/etc. bus probe sem. * * RETURNS: - * Zero on success, non-zero on error. + * Zero on success, negative errno otherwise. */ static int ata_bus_probe(struct ata_port *ap) { unsigned int classes[ATA_MAX_DEVICES]; - unsigned int i, rc, found = 0; + int tries[ATA_MAX_DEVICES]; + int i, rc, down_xfermask; + struct ata_device *dev; ata_port_probe(ap); + for (i = 0; i < ATA_MAX_DEVICES; i++) + tries[i] = ATA_PROBE_MAX_TRIES; + + retry: + down_xfermask = 0; + /* reset and determine device classes */ for (i = 0; i < ATA_MAX_DEVICES; i++) classes[i] = ATA_DEV_UNKNOWN; @@ -1374,7 +1397,7 @@ static int ata_bus_probe(struct ata_port *ap) } else { ap->ops->phy_reset(ap); - if (!(ap->flags & ATA_FLAG_PORT_DISABLED)) + if (!(ap->flags & ATA_FLAG_DISABLED)) for (i = 0; i < ATA_MAX_DEVICES; i++) classes[i] = ap->device[i].class; @@ -1387,43 +1410,76 @@ static int ata_bus_probe(struct ata_port *ap) /* read IDENTIFY page and configure devices */ for (i = 0; i < ATA_MAX_DEVICES; i++) { - struct ata_device *dev = &ap->device[i]; + dev = &ap->device[i]; - dev->class = classes[i]; + if (tries[i]) + dev->class = classes[i]; - if (!ata_dev_present(dev)) + if (!ata_dev_enabled(dev)) continue; - WARN_ON(dev->id != NULL); - if (ata_dev_read_id(ap, dev, &dev->class, 1, &dev->id)) { - dev->class = ATA_DEV_NONE; - continue; - } + kfree(dev->id); + dev->id = NULL; + rc = ata_dev_read_id(ap, dev, &dev->class, 1, &dev->id); + if (rc) + goto fail; - if (ata_dev_configure(ap, dev, 1)) { - ata_dev_disable(ap, dev); - continue; - } - - found = 1; + rc = ata_dev_configure(ap, dev, 1); + if (rc) + goto fail; } - if (!found) - goto err_out_disable; + /* configure transfer mode */ + if (ap->ops->set_mode) { + /* FIXME: make ->set_mode handle no device case and + * return error code and failing device on failure as + * ata_set_mode() does. + */ + for (i = 0; i < ATA_MAX_DEVICES; i++) + if (ata_dev_enabled(&ap->device[i])) { + ap->ops->set_mode(ap); + break; + } + rc = 0; + } else + rc = ata_set_mode(ap, &dev); - if (ap->ops->set_mode) - ap->ops->set_mode(ap); - else - ata_set_mode(ap); + if (rc) { + down_xfermask = 1; + goto fail; + } - if (ap->flags & ATA_FLAG_PORT_DISABLED) - goto err_out_disable; + for (i = 0; i < ATA_MAX_DEVICES; i++) + if (ata_dev_enabled(&ap->device[i])) + return 0; - return 0; - -err_out_disable: + /* no device present, disable port */ + ata_port_disable(ap); ap->ops->port_disable(ap); - return -1; + return -ENODEV; + + fail: + switch (rc) { + case -EINVAL: + case -ENODEV: + tries[dev->devno] = 0; + break; + case -EIO: + ata_down_sata_spd_limit(ap); + /* fall through */ + default: + tries[dev->devno]--; + if (down_xfermask && + ata_down_xfermask_limit(ap, dev, tries[dev->devno] == 1)) + tries[dev->devno] = 0; + } + + if (!tries[dev->devno]) { + ata_down_xfermask_limit(ap, dev, 1); + ata_dev_disable(ap, dev); + } + + goto retry; } /** @@ -1439,7 +1495,7 @@ err_out_disable: void ata_port_probe(struct ata_port *ap) { - ap->flags &= ~ATA_FLAG_PORT_DISABLED; + ap->flags &= ~ATA_FLAG_DISABLED; } /** @@ -1453,27 +1509,23 @@ void ata_port_probe(struct ata_port *ap) */ static void sata_print_link_status(struct ata_port *ap) { - u32 sstatus, tmp; - const char *speed; + u32 sstatus, scontrol, tmp; if (!ap->ops->scr_read) return; sstatus = scr_read(ap, SCR_STATUS); + scontrol = scr_read(ap, SCR_CONTROL); if (sata_dev_present(ap)) { tmp = (sstatus >> 4) & 0xf; - if (tmp & (1 << 0)) - speed = "1.5"; - else if (tmp & (1 << 1)) - speed = "3.0"; - else - speed = ""; - printk(KERN_INFO "ata%u: SATA link up %s Gbps (SStatus %X)\n", - ap->id, speed, sstatus); + printk(KERN_INFO + "ata%u: SATA link up %s (SStatus %X SControl %X)\n", + ap->id, sata_spd_string(tmp), sstatus, scontrol); } else { - printk(KERN_INFO "ata%u: SATA link down (SStatus %X)\n", - ap->id, sstatus); + printk(KERN_INFO + "ata%u: SATA link down (SStatus %X SControl %X)\n", + ap->id, sstatus, scontrol); } } @@ -1520,7 +1572,7 @@ void __sata_phy_reset(struct ata_port *ap) else ata_port_disable(ap); - if (ap->flags & ATA_FLAG_PORT_DISABLED) + if (ap->flags & ATA_FLAG_DISABLED) return; if (ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT)) { @@ -1545,7 +1597,7 @@ void __sata_phy_reset(struct ata_port *ap) void sata_phy_reset(struct ata_port *ap) { __sata_phy_reset(ap); - if (ap->flags & ATA_FLAG_PORT_DISABLED) + if (ap->flags & ATA_FLAG_DISABLED) return; ata_bus_reset(ap); } @@ -1562,7 +1614,7 @@ void sata_phy_reset(struct ata_port *ap) struct ata_device *ata_dev_pair(struct ata_port *ap, struct ata_device *adev) { struct ata_device *pair = &ap->device[1 - adev->devno]; - if (!ata_dev_present(pair)) + if (!ata_dev_enabled(pair)) return NULL; return pair; } @@ -1584,7 +1636,121 @@ 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; + ap->flags |= ATA_FLAG_DISABLED; +} + +/** + * ata_down_sata_spd_limit - adjust SATA spd limit downward + * @ap: Port to adjust SATA spd limit for + * + * Adjust SATA spd limit of @ap downward. Note that this + * function only adjusts the limit. The change must be applied + * using ata_set_sata_spd(). + * + * LOCKING: + * Inherited from caller. + * + * RETURNS: + * 0 on success, negative errno on failure + */ +int ata_down_sata_spd_limit(struct ata_port *ap) +{ + u32 spd, mask; + int highbit; + + if (ap->cbl != ATA_CBL_SATA || !ap->ops->scr_read) + return -EOPNOTSUPP; + + mask = ap->sata_spd_limit; + if (mask <= 1) + return -EINVAL; + highbit = fls(mask) - 1; + mask &= ~(1 << highbit); + + spd = (scr_read(ap, SCR_STATUS) >> 4) & 0xf; + if (spd <= 1) + return -EINVAL; + spd--; + mask &= (1 << spd) - 1; + if (!mask) + return -EINVAL; + + ap->sata_spd_limit = mask; + + printk(KERN_WARNING "ata%u: limiting SATA link speed to %s\n", + ap->id, sata_spd_string(fls(mask))); + + return 0; +} + +static int __ata_set_sata_spd_needed(struct ata_port *ap, u32 *scontrol) +{ + u32 spd, limit; + + if (ap->sata_spd_limit == UINT_MAX) + limit = 0; + else + limit = fls(ap->sata_spd_limit); + + spd = (*scontrol >> 4) & 0xf; + *scontrol = (*scontrol & ~0xf0) | ((limit & 0xf) << 4); + + return spd != limit; +} + +/** + * ata_set_sata_spd_needed - is SATA spd configuration needed + * @ap: Port in question + * + * Test whether the spd limit in SControl matches + * @ap->sata_spd_limit. This function is used to determine + * whether hardreset is necessary to apply SATA spd + * configuration. + * + * LOCKING: + * Inherited from caller. + * + * RETURNS: + * 1 if SATA spd configuration is needed, 0 otherwise. + */ +int ata_set_sata_spd_needed(struct ata_port *ap) +{ + u32 scontrol; + + if (ap->cbl != ATA_CBL_SATA || !ap->ops->scr_read) + return 0; + + scontrol = scr_read(ap, SCR_CONTROL); + + return __ata_set_sata_spd_needed(ap, &scontrol); +} + +/** + * ata_set_sata_spd - set SATA spd according to spd limit + * @ap: Port to set SATA spd for + * + * Set SATA spd of @ap according to sata_spd_limit. + * + * LOCKING: + * Inherited from caller. + * + * RETURNS: + * 0 if spd doesn't need to be changed, 1 if spd has been + * changed. -EOPNOTSUPP if SCR registers are inaccessible. + */ +int ata_set_sata_spd(struct ata_port *ap) +{ + u32 scontrol; + + if (ap->cbl != ATA_CBL_SATA || !ap->ops->scr_read) + return -EOPNOTSUPP; + + scontrol = scr_read(ap, SCR_CONTROL); + if (!__ata_set_sata_spd_needed(ap, &scontrol)) + return 0; + + scr_write(ap, SCR_CONTROL, scontrol); + return 1; } /* @@ -1735,11 +1901,62 @@ int ata_timing_compute(struct ata_device *adev, unsigned short speed, return 0; } +/** + * ata_down_xfermask_limit - adjust dev xfer masks downward + * @ap: Port associated with device @dev + * @dev: Device to adjust xfer masks + * @force_pio0: Force PIO0 + * + * Adjust xfer masks of @dev downward. Note that this function + * does not apply the change. Invoking ata_set_mode() afterwards + * will apply the limit. + * + * LOCKING: + * Inherited from caller. + * + * RETURNS: + * 0 on success, negative errno on failure + */ +int ata_down_xfermask_limit(struct ata_port *ap, struct ata_device *dev, + int force_pio0) +{ + unsigned long xfer_mask; + int highbit; + + xfer_mask = ata_pack_xfermask(dev->pio_mask, dev->mwdma_mask, + dev->udma_mask); + + if (!xfer_mask) + goto fail; + /* don't gear down to MWDMA from UDMA, go directly to PIO */ + if (xfer_mask & ATA_MASK_UDMA) + xfer_mask &= ~ATA_MASK_MWDMA; + + highbit = fls(xfer_mask) - 1; + xfer_mask &= ~(1 << highbit); + if (force_pio0) + xfer_mask &= 1 << ATA_SHIFT_PIO; + if (!xfer_mask) + goto fail; + + ata_unpack_xfermask(xfer_mask, &dev->pio_mask, &dev->mwdma_mask, + &dev->udma_mask); + + printk(KERN_WARNING "ata%u: dev %u limiting speed to %s\n", + ap->id, dev->devno, ata_mode_string(xfer_mask)); + + return 0; + + fail: + return -EINVAL; +} + static int ata_dev_set_mode(struct ata_port *ap, struct ata_device *dev) { unsigned int err_mask; int rc; + dev->flags &= ~ATA_DFLAG_PIO; if (dev->xfer_shift == ATA_SHIFT_PIO) dev->flags |= ATA_DFLAG_PIO; @@ -1752,12 +1969,8 @@ static int ata_dev_set_mode(struct ata_port *ap, struct ata_device *dev) } rc = ata_dev_revalidate(ap, dev, 0); - if (rc) { - printk(KERN_ERR - "ata%u: failed to revalidate after set xfermode\n", - ap->id); + if (rc) return rc; - } DPRINTK("xfer_shift=%u, xfer_mode=0x%x\n", dev->xfer_shift, (int)dev->xfer_mode); @@ -1768,19 +1981,60 @@ static int ata_dev_set_mode(struct ata_port *ap, struct ata_device *dev) return 0; } -static int ata_host_set_pio(struct ata_port *ap) +/** + * ata_set_mode - Program timings and issue SET FEATURES - XFER + * @ap: port on which timings will be programmed + * @r_failed_dev: out paramter for failed device + * + * Set ATA device disk transfer mode (PIO3, UDMA6, etc.). If + * ata_set_mode() fails, pointer to the failing device is + * returned in @r_failed_dev. + * + * LOCKING: + * PCI/etc. bus probe sem. + * + * RETURNS: + * 0 on success, negative errno otherwise + */ +int ata_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev) { - int i; + struct ata_device *dev; + int i, rc = 0, used_dma = 0, found = 0; + /* step 1: calculate xfer_mask */ for (i = 0; i < ATA_MAX_DEVICES; i++) { - struct ata_device *dev = &ap->device[i]; + unsigned int pio_mask, dma_mask; - if (!ata_dev_present(dev)) + dev = &ap->device[i]; + + if (!ata_dev_enabled(dev)) + continue; + + ata_dev_xfermask(ap, dev); + + pio_mask = ata_pack_xfermask(dev->pio_mask, 0, 0); + dma_mask = ata_pack_xfermask(0, dev->mwdma_mask, dev->udma_mask); + dev->pio_mode = ata_xfer_mask2mode(pio_mask); + dev->dma_mode = ata_xfer_mask2mode(dma_mask); + + found = 1; + if (dev->dma_mode) + used_dma = 1; + } + if (!found) + goto out; + + /* step 2: always set host PIO timings */ + for (i = 0; i < ATA_MAX_DEVICES; i++) { + dev = &ap->device[i]; + if (!ata_dev_enabled(dev)) continue; if (!dev->pio_mode) { - printk(KERN_WARNING "ata%u: no PIO support for device %d.\n", ap->id, i); - return -1; + printk(KERN_WARNING "ata%u: dev %u no PIO support\n", + ap->id, dev->devno); + rc = -EINVAL; + goto out; } dev->xfer_mode = dev->pio_mode; @@ -1789,17 +2043,11 @@ static int ata_host_set_pio(struct ata_port *ap) ap->ops->set_piomode(ap, dev); } - return 0; -} - -static void ata_host_set_dma(struct ata_port *ap) -{ - int i; - + /* step 3: set host DMA timings */ for (i = 0; i < ATA_MAX_DEVICES; i++) { - struct ata_device *dev = &ap->device[i]; + dev = &ap->device[i]; - if (!ata_dev_present(dev) || !dev->dma_mode) + if (!ata_dev_enabled(dev) || !dev->dma_mode) continue; dev->xfer_mode = dev->dma_mode; @@ -1807,79 +2055,33 @@ static void ata_host_set_dma(struct ata_port *ap) if (ap->ops->set_dmamode) ap->ops->set_dmamode(ap, dev); } -} - -/** - * ata_set_mode - Program timings and issue SET FEATURES - XFER - * @ap: port on which timings will be programmed - * - * Set ATA device disk transfer mode (PIO3, UDMA6, etc.). - * - * LOCKING: - * PCI/etc. bus probe sem. - */ -static void ata_set_mode(struct ata_port *ap) -{ - int i, rc, used_dma = 0; - - /* step 1: calculate xfer_mask */ - for (i = 0; i < ATA_MAX_DEVICES; i++) { - struct ata_device *dev = &ap->device[i]; - unsigned int pio_mask, dma_mask; - - if (!ata_dev_present(dev)) - continue; - - ata_dev_xfermask(ap, dev); - - /* TODO: let LLDD filter dev->*_mask here */ - - pio_mask = ata_pack_xfermask(dev->pio_mask, 0, 0); - dma_mask = ata_pack_xfermask(0, dev->mwdma_mask, dev->udma_mask); - dev->pio_mode = ata_xfer_mask2mode(pio_mask); - dev->dma_mode = ata_xfer_mask2mode(dma_mask); - - if (dev->dma_mode) - used_dma = 1; - } - - /* step 2: always set host PIO timings */ - rc = ata_host_set_pio(ap); - if (rc) - goto err_out; - - /* step 3: set host DMA timings */ - ata_host_set_dma(ap); /* step 4: update devices' xfer mode */ for (i = 0; i < ATA_MAX_DEVICES; i++) { - struct ata_device *dev = &ap->device[i]; + dev = &ap->device[i]; - if (!ata_dev_present(dev)) + if (!ata_dev_enabled(dev)) continue; - if (ata_dev_set_mode(ap, dev)) - goto err_out; + rc = ata_dev_set_mode(ap, dev); + if (rc) + goto out; } - /* - * Record simplex status. If we selected DMA then the other - * host channels are not permitted to do so. + /* Record simplex status. If we selected DMA then the other + * host channels are not permitted to do so. */ - if (used_dma && (ap->host_set->flags & ATA_HOST_SIMPLEX)) ap->host_set->simplex_claimed = 1; - /* - * Chip specific finalisation - */ + /* step5: chip specific finalisation */ if (ap->ops->post_set_mode) ap->ops->post_set_mode(ap); - return; - -err_out: - ata_port_disable(ap); + out: + if (rc) + *r_failed_dev = dev; + return rc; } /** @@ -2032,8 +2234,10 @@ static unsigned int ata_bus_softreset(struct ata_port *ap, * the bus shows 0xFF because the odd clown forgets the D7 * pulldown resistor. */ - if (ata_check_status(ap) == 0xFF) + if (ata_check_status(ap) == 0xFF) { + printk(KERN_ERR "ata%u: SRST failed (status 0xFF)\n", ap->id); return AC_ERR_OTHER; + } ata_bus_post_reset(ap, devmask); @@ -2057,7 +2261,7 @@ static unsigned int ata_bus_softreset(struct ata_port *ap, * Obtains host_set lock. * * SIDE EFFECTS: - * Sets ATA_FLAG_PORT_DISABLED if bus reset fails. + * Sets ATA_FLAG_DISABLED if bus reset fails. */ void ata_bus_reset(struct ata_port *ap) @@ -2134,9 +2338,11 @@ err_out: static int sata_phy_resume(struct ata_port *ap) { unsigned long timeout = jiffies + (HZ * 5); - u32 sstatus; + u32 scontrol, sstatus; - scr_write_flush(ap, SCR_CONTROL, 0x300); + scontrol = scr_read(ap, SCR_CONTROL); + scontrol = (scontrol & 0x0f0) | 0x300; + scr_write_flush(ap, SCR_CONTROL, scontrol); /* Wait for phy to become ready, if necessary. */ do { @@ -2165,7 +2371,18 @@ static int sata_phy_resume(struct ata_port *ap) void ata_std_probeinit(struct ata_port *ap) { if ((ap->flags & ATA_FLAG_SATA) && ap->ops->scr_read) { + u32 spd; + + /* set cable type and resume link */ + ap->cbl = ATA_CBL_SATA; sata_phy_resume(ap); + + /* init sata_spd_limit to the current value */ + spd = (scr_read(ap, SCR_CONTROL) & 0xf0) >> 4; + if (spd) + ap->sata_spd_limit &= (1 << spd) - 1; + + /* wait for device */ if (sata_dev_present(ap)) ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT); } @@ -2174,7 +2391,6 @@ void ata_std_probeinit(struct ata_port *ap) /** * ata_std_softreset - reset host port via ATA SRST * @ap: port to reset - * @verbose: fail verbosely * @classes: resulting classes of attached devices * * Reset host port using ATA SRST. This function is to be used @@ -2186,7 +2402,7 @@ void ata_std_probeinit(struct ata_port *ap) * RETURNS: * 0 on success, -errno otherwise. */ -int ata_std_softreset(struct ata_port *ap, int verbose, unsigned int *classes) +int ata_std_softreset(struct ata_port *ap, unsigned int *classes) { unsigned int slave_possible = ap->flags & ATA_FLAG_SLAVE_POSS; unsigned int devmask = 0, err_mask; @@ -2212,12 +2428,8 @@ int ata_std_softreset(struct ata_port *ap, int verbose, unsigned int *classes) DPRINTK("about to softreset, devmask=%x\n", devmask); err_mask = ata_bus_softreset(ap, devmask); if (err_mask) { - if (verbose) - printk(KERN_ERR "ata%u: SRST failed (err_mask=0x%x)\n", - ap->id, err_mask); - else - DPRINTK("EXIT, softreset failed (err_mask=0x%x)\n", - err_mask); + printk(KERN_ERR "ata%u: SRST failed (err_mask=0x%x)\n", + ap->id, err_mask); return -EIO; } @@ -2234,7 +2446,6 @@ int ata_std_softreset(struct ata_port *ap, int verbose, unsigned int *classes) /** * sata_std_hardreset - reset host port via SATA phy reset * @ap: port to reset - * @verbose: fail verbosely * @class: resulting class of attached device * * SATA phy-reset host port using DET bits of SControl register. @@ -2247,20 +2458,36 @@ int ata_std_softreset(struct ata_port *ap, int verbose, unsigned int *classes) * RETURNS: * 0 on success, -errno otherwise. */ -int sata_std_hardreset(struct ata_port *ap, int verbose, unsigned int *class) +int sata_std_hardreset(struct ata_port *ap, unsigned int *class) { + u32 scontrol; + DPRINTK("ENTER\n"); - /* Issue phy wake/reset */ - scr_write_flush(ap, SCR_CONTROL, 0x301); + if (ata_set_sata_spd_needed(ap)) { + /* SATA spec says nothing about how to reconfigure + * spd. To be on the safe side, turn off phy during + * reconfiguration. This works for at least ICH7 AHCI + * and Sil3124. + */ + scontrol = scr_read(ap, SCR_CONTROL); + scontrol = (scontrol & 0x0f0) | 0x302; + scr_write_flush(ap, SCR_CONTROL, scontrol); - /* - * Couldn't find anything in SATA I/II specs, but AHCI-1.1 + ata_set_sata_spd(ap); + } + + /* issue phy wake/reset */ + scontrol = scr_read(ap, SCR_CONTROL); + scontrol = (scontrol & 0x0f0) | 0x301; + scr_write_flush(ap, SCR_CONTROL, scontrol); + + /* Couldn't find anything in SATA I/II specs, but AHCI-1.1 * 10.4.2 says at least 1 ms. */ msleep(1); - /* Bring phy back */ + /* bring phy back */ sata_phy_resume(ap); /* TODO: phy layer with polling, timeouts, etc. */ @@ -2271,11 +2498,8 @@ int sata_std_hardreset(struct ata_port *ap, int verbose, unsigned int *class) } if (ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT)) { - if (verbose) - printk(KERN_ERR "ata%u: COMRESET failed " - "(device not ready)\n", ap->id); - else - DPRINTK("EXIT, device not ready\n"); + printk(KERN_ERR + "ata%u: COMRESET failed (device not ready)\n", ap->id); return -EIO; } @@ -2306,10 +2530,6 @@ void ata_std_postreset(struct ata_port *ap, unsigned int *classes) { DPRINTK("ENTER\n"); - /* set cable type if it isn't already set */ - if (ap->cbl == ATA_CBL_NONE && ap->flags & ATA_FLAG_SATA) - ap->cbl = ATA_CBL_SATA; - /* print link status */ if (ap->cbl == ATA_CBL_SATA) sata_print_link_status(ap); @@ -2359,7 +2579,7 @@ int ata_std_probe_reset(struct ata_port *ap, unsigned int *classes) ata_reset_fn_t hardreset; hardreset = NULL; - if (ap->flags & ATA_FLAG_SATA && ap->ops->scr_read) + if (ap->cbl == ATA_CBL_SATA && ap->ops->scr_read) hardreset = sata_std_hardreset; return ata_drive_probe_reset(ap, ata_std_probeinit, @@ -2367,16 +2587,15 @@ int ata_std_probe_reset(struct ata_port *ap, unsigned int *classes) ata_std_postreset, classes); } -static int do_probe_reset(struct ata_port *ap, ata_reset_fn_t reset, - ata_postreset_fn_t postreset, - unsigned int *classes) +int ata_do_reset(struct ata_port *ap, ata_reset_fn_t reset, + ata_postreset_fn_t postreset, unsigned int *classes) { int i, rc; for (i = 0; i < ATA_MAX_DEVICES; i++) classes[i] = ATA_DEV_UNKNOWN; - rc = reset(ap, 0, classes); + rc = reset(ap, classes); if (rc) return rc; @@ -2396,7 +2615,7 @@ static int do_probe_reset(struct ata_port *ap, ata_reset_fn_t reset, if (postreset) postreset(ap, classes); - return classes[0] != ATA_DEV_UNKNOWN ? 0 : -ENODEV; + return 0; } /** @@ -2420,8 +2639,6 @@ static int do_probe_reset(struct ata_port *ap, ata_reset_fn_t reset, * - If classification is supported, fill classes[] with * recognized class codes. * - If classification is not supported, leave classes[] alone. - * - If verbose is non-zero, print error message on failure; - * otherwise, shut up. * * LOCKING: * Kernel thread context (may sleep) @@ -2440,22 +2657,46 @@ int ata_drive_probe_reset(struct ata_port *ap, ata_probeinit_fn_t probeinit, if (probeinit) probeinit(ap); - if (softreset) { - rc = do_probe_reset(ap, softreset, postreset, classes); - if (rc == 0) - return 0; + if (softreset && !ata_set_sata_spd_needed(ap)) { + rc = ata_do_reset(ap, softreset, postreset, classes); + if (rc == 0 && classes[0] != ATA_DEV_UNKNOWN) + goto done; + printk(KERN_INFO "ata%u: softreset failed, will try " + "hardreset in 5 secs\n", ap->id); + ssleep(5); } if (!hardreset) - return rc; + goto done; - rc = do_probe_reset(ap, hardreset, postreset, classes); - if (rc == 0 || rc != -ENODEV) - return rc; + while (1) { + rc = ata_do_reset(ap, hardreset, postreset, classes); + if (rc == 0) { + if (classes[0] != ATA_DEV_UNKNOWN) + goto done; + break; + } - if (softreset) - rc = do_probe_reset(ap, softreset, postreset, classes); + if (ata_down_sata_spd_limit(ap)) + goto done; + printk(KERN_INFO "ata%u: hardreset failed, will retry " + "in 5 secs\n", ap->id); + ssleep(5); + } + + if (softreset) { + printk(KERN_INFO "ata%u: hardreset succeeded without " + "classification, will retry softreset in 5 secs\n", + ap->id); + ssleep(5); + + rc = ata_do_reset(ap, softreset, postreset, classes); + } + + done: + if (rc == 0 && classes[0] == ATA_DEV_UNKNOWN) + rc = -ENODEV; return rc; } @@ -2539,15 +2780,14 @@ static int ata_dev_same_device(struct ata_port *ap, struct ata_device *dev, int ata_dev_revalidate(struct ata_port *ap, struct ata_device *dev, int post_reset) { - unsigned int class; - u16 *id; + unsigned int class = dev->class; + u16 *id = NULL; int rc; - if (!ata_dev_present(dev)) - return -ENODEV; - - class = dev->class; - id = NULL; + if (!ata_dev_enabled(dev)) { + rc = -ENODEV; + goto fail; + } /* allocate & read ID data */ rc = ata_dev_read_id(ap, dev, &class, post_reset, &id); @@ -2564,7 +2804,9 @@ int ata_dev_revalidate(struct ata_port *ap, struct ata_device *dev, dev->id = id; /* configure device according to the new ID */ - return ata_dev_configure(ap, dev, 0); + rc = ata_dev_configure(ap, dev, 0); + if (rc == 0) + return 0; fail: printk(KERN_ERR "ata%u: dev %u revalidation failed (errno=%d)\n", @@ -2666,23 +2908,34 @@ static void ata_dev_xfermask(struct ata_port *ap, struct ata_device *dev) unsigned long xfer_mask; int i; - xfer_mask = ata_pack_xfermask(ap->pio_mask, ap->mwdma_mask, - ap->udma_mask); + xfer_mask = ata_pack_xfermask(ap->pio_mask, + ap->mwdma_mask, ap->udma_mask); + + /* Apply cable rule here. Don't apply it early because when + * we handle hot plug the cable type can itself change. + */ + if (ap->cbl == ATA_CBL_PATA40) + xfer_mask &= ~(0xF8 << ATA_SHIFT_UDMA); /* FIXME: Use port-wide xfermask for now */ for (i = 0; i < ATA_MAX_DEVICES; i++) { struct ata_device *d = &ap->device[i]; - if (!ata_dev_present(d)) + + if (ata_dev_absent(d)) continue; - xfer_mask &= ata_pack_xfermask(d->pio_mask, d->mwdma_mask, - d->udma_mask); + + if (ata_dev_disabled(d)) { + /* to avoid violating device selection timing */ + xfer_mask &= ata_pack_xfermask(d->pio_mask, + UINT_MAX, UINT_MAX); + continue; + } + + xfer_mask &= ata_pack_xfermask(d->pio_mask, + d->mwdma_mask, d->udma_mask); xfer_mask &= ata_id_xfermask(d->id); if (ata_dma_blacklisted(d)) xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA); - /* Apply cable rule here. Don't apply it early because when - we handle hot plug the cable type can itself change */ - if (ap->cbl == ATA_CBL_PATA40) - xfer_mask &= ~(0xF8 << ATA_SHIFT_UDMA); } if (ata_dma_blacklisted(dev)) @@ -2693,11 +2946,12 @@ static void ata_dev_xfermask(struct ata_port *ap, struct ata_device *dev) if (hs->simplex_claimed) xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA); } + if (ap->ops->mode_filter) xfer_mask = ap->ops->mode_filter(ap, dev, xfer_mask); - ata_unpack_xfermask(xfer_mask, &dev->pio_mask, &dev->mwdma_mask, - &dev->udma_mask); + ata_unpack_xfermask(xfer_mask, &dev->pio_mask, + &dev->mwdma_mask, &dev->udma_mask); } /** @@ -2731,7 +2985,7 @@ static unsigned int ata_dev_set_xfermode(struct ata_port *ap, tf.protocol = ATA_PROT_NODATA; tf.nsect = dev->xfer_mode; - err_mask = ata_exec_internal(ap, dev, &tf, DMA_NONE, NULL, 0); + err_mask = ata_exec_internal(ap, dev, &tf, NULL, DMA_NONE, NULL, 0); DPRINTK("EXIT, err_mask=%x\n", err_mask); return err_mask; @@ -2771,7 +3025,7 @@ static unsigned int ata_dev_init_params(struct ata_port *ap, tf.nsect = sectors; tf.device |= (heads - 1) & 0x0f; /* max head = num. of heads - 1 */ - err_mask = ata_exec_internal(ap, dev, &tf, DMA_NONE, NULL, 0); + err_mask = ata_exec_internal(ap, dev, &tf, NULL, DMA_NONE, NULL, 0); DPRINTK("EXIT, err_mask=%x\n", err_mask); return err_mask; @@ -3159,7 +3413,7 @@ void ata_poll_qc_complete(struct ata_queued_cmd *qc) /** * ata_pio_poll - poll using PIO, depending on current state - * @ap: the target ata_port + * @qc: qc in progress * * LOCKING: * None. (executing in kernel thread context) @@ -3167,17 +3421,13 @@ void ata_poll_qc_complete(struct ata_queued_cmd *qc) * RETURNS: * timeout value to use */ - -static unsigned long ata_pio_poll(struct ata_port *ap) +static unsigned long ata_pio_poll(struct ata_queued_cmd *qc) { - struct ata_queued_cmd *qc; + struct ata_port *ap = qc->ap; u8 status; unsigned int poll_state = HSM_ST_UNKNOWN; unsigned int reg_state = HSM_ST_UNKNOWN; - qc = ata_qc_from_tag(ap, ap->active_tag); - WARN_ON(qc == NULL); - switch (ap->hsm_task_state) { case HSM_ST: case HSM_ST_POLL: @@ -3211,7 +3461,7 @@ static unsigned long ata_pio_poll(struct ata_port *ap) /** * ata_pio_complete - check if drive is busy or idle - * @ap: the target ata_port + * @qc: qc to complete * * LOCKING: * None. (executing in kernel thread context) @@ -3219,10 +3469,9 @@ static unsigned long ata_pio_poll(struct ata_port *ap) * RETURNS: * Non-zero if qc completed, zero otherwise. */ - -static int ata_pio_complete (struct ata_port *ap) +static int ata_pio_complete(struct ata_queued_cmd *qc) { - struct ata_queued_cmd *qc; + struct ata_port *ap = qc->ap; u8 drv_stat; /* @@ -3243,9 +3492,6 @@ static int ata_pio_complete (struct ata_port *ap) } } - qc = ata_qc_from_tag(ap, ap->active_tag); - WARN_ON(qc == NULL); - drv_stat = ata_wait_idle(ap); if (!ata_ok(drv_stat)) { qc->err_mask |= __ac_err_mask(drv_stat); @@ -3581,15 +3827,14 @@ err_out: /** * ata_pio_block - start PIO on a block - * @ap: the target ata_port + * @qc: qc to transfer block for * * LOCKING: * None. (executing in kernel thread context) */ - -static void ata_pio_block(struct ata_port *ap) +static void ata_pio_block(struct ata_queued_cmd *qc) { - struct ata_queued_cmd *qc; + struct ata_port *ap = qc->ap; u8 status; /* @@ -3611,9 +3856,6 @@ static void ata_pio_block(struct ata_port *ap) } } - qc = ata_qc_from_tag(ap, ap->active_tag); - WARN_ON(qc == NULL); - /* check error */ if (status & (ATA_ERR | ATA_DF)) { qc->err_mask |= AC_ERR_DEV; @@ -3642,15 +3884,13 @@ static void ata_pio_block(struct ata_port *ap) } } -static void ata_pio_error(struct ata_port *ap) +static void ata_pio_error(struct ata_queued_cmd *qc) { - struct ata_queued_cmd *qc; - - qc = ata_qc_from_tag(ap, ap->active_tag); - WARN_ON(qc == NULL); + struct ata_port *ap = qc->ap; if (qc->tf.command != ATA_CMD_PACKET) - printk(KERN_WARNING "ata%u: PIO error\n", ap->id); + printk(KERN_WARNING "ata%u: dev %u PIO error\n", + ap->id, qc->dev->devno); /* make sure qc->err_mask is available to * know what's wrong and recover @@ -3664,7 +3904,8 @@ static void ata_pio_error(struct ata_port *ap) static void ata_pio_task(void *_data) { - struct ata_port *ap = _data; + struct ata_queued_cmd *qc = _data; + struct ata_port *ap = qc->ap; unsigned long timeout; int qc_completed; @@ -3677,33 +3918,33 @@ fsm_start: return; case HSM_ST: - ata_pio_block(ap); + ata_pio_block(qc); break; case HSM_ST_LAST: - qc_completed = ata_pio_complete(ap); + qc_completed = ata_pio_complete(qc); break; case HSM_ST_POLL: case HSM_ST_LAST_POLL: - timeout = ata_pio_poll(ap); + timeout = ata_pio_poll(qc); break; case HSM_ST_TMOUT: case HSM_ST_ERR: - ata_pio_error(ap); + ata_pio_error(qc); return; } if (timeout) - ata_port_queue_task(ap, ata_pio_task, ap, timeout); + ata_port_queue_task(ap, ata_pio_task, qc, timeout); else if (!qc_completed) goto fsm_start; } /** * atapi_packet_task - Write CDB bytes to hardware - * @_data: Port to which ATAPI device is attached. + * @_data: qc in progress * * When device has indicated its readiness to accept * a CDB, this function is called. Send the CDB. @@ -3714,17 +3955,12 @@ fsm_start: * LOCKING: * Kernel thread context (may sleep) */ - static void atapi_packet_task(void *_data) { - struct ata_port *ap = _data; - struct ata_queued_cmd *qc; + struct ata_queued_cmd *qc = _data; + struct ata_port *ap = qc->ap; u8 status; - qc = ata_qc_from_tag(ap, ap->active_tag); - WARN_ON(qc == NULL); - WARN_ON(!(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)) { @@ -3764,7 +4000,7 @@ static void atapi_packet_task(void *_data) /* PIO commands are handled by polling */ ap->hsm_task_state = HSM_ST; - ata_port_queue_task(ap, ata_pio_task, ap, 0); + ata_port_queue_task(ap, ata_pio_task, qc, 0); } return; @@ -3773,99 +4009,6 @@ err_out: ata_poll_qc_complete(qc); } -/** - * ata_qc_timeout - Handle timeout of queued command - * @qc: Command that timed out - * - * 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) - */ - -static void ata_qc_timeout(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - struct ata_host_set *host_set = ap->host_set; - u8 host_stat = 0, drv_stat; - unsigned long flags; - - DPRINTK("ENTER\n"); - - ap->hsm_task_state = HSM_ST_IDLE; - - spin_lock_irqsave(&host_set->lock, flags); - - switch (qc->tf.protocol) { - - case ATA_PROT_DMA: - case ATA_PROT_ATAPI_DMA: - host_stat = ap->ops->bmdma_status(ap); - - /* before we do anything else, clear DMA-Start bit */ - ap->ops->bmdma_stop(qc); - - /* fall through */ - - default: - ata_altstatus(ap); - drv_stat = ata_chk_status(ap); - - /* ack bmdma irq events */ - ap->ops->irq_clear(ap); - - printk(KERN_ERR "ata%u: command 0x%x timeout, stat 0x%x host_stat 0x%x\n", - ap->id, qc->tf.command, drv_stat, host_stat); - - /* complete taskfile transaction */ - qc->err_mask |= ac_err_mask(drv_stat); - break; - } - - spin_unlock_irqrestore(&host_set->lock, flags); - - ata_eh_qc_complete(qc); - - DPRINTK("EXIT\n"); -} - -/** - * 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) -{ - DPRINTK("ENTER\n"); - - ata_qc_timeout(ata_qc_from_tag(ap, ap->active_tag)); - - DPRINTK("EXIT\n"); -} - /** * ata_qc_new - Request an available ATA command, for queueing * @ap: Port associated with device @dev @@ -3997,15 +4140,14 @@ static inline int ata_should_dma_map(struct ata_queued_cmd *qc) * * LOCKING: * spin_lock_irqsave(host_set lock) - * - * RETURNS: - * Zero on success, AC_ERR_* mask on failure */ - -unsigned int ata_qc_issue(struct ata_queued_cmd *qc) +void ata_qc_issue(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; + qc->ap->active_tag = qc->tag; + qc->flags |= ATA_QCFLAG_ACTIVE; + if (ata_should_dma_map(qc)) { if (qc->flags & ATA_QCFLAG_SG) { if (ata_sg_setup(qc)) @@ -4020,17 +4162,18 @@ unsigned int ata_qc_issue(struct ata_queued_cmd *qc) ap->ops->qc_prep(qc); - qc->ap->active_tag = qc->tag; - qc->flags |= ATA_QCFLAG_ACTIVE; - - return ap->ops->qc_issue(qc); + qc->err_mask |= ap->ops->qc_issue(qc); + if (unlikely(qc->err_mask)) + goto err; + return; sg_err: qc->flags &= ~ATA_QCFLAG_DMAMAP; - return AC_ERR_SYSTEM; + qc->err_mask |= AC_ERR_SYSTEM; +err: + ata_qc_complete(qc); } - /** * ata_qc_issue_prot - issue taskfile to device in proto-dependent manner * @qc: command to issue to device @@ -4070,26 +4213,26 @@ unsigned int ata_qc_issue_prot(struct ata_queued_cmd *qc) ata_qc_set_polling(qc); ata_tf_to_host(ap, &qc->tf); ap->hsm_task_state = HSM_ST; - ata_port_queue_task(ap, ata_pio_task, ap, 0); + ata_port_queue_task(ap, ata_pio_task, qc, 0); break; case ATA_PROT_ATAPI: ata_qc_set_polling(qc); ata_tf_to_host(ap, &qc->tf); - ata_port_queue_task(ap, atapi_packet_task, ap, 0); + ata_port_queue_task(ap, atapi_packet_task, qc, 0); break; case ATA_PROT_ATAPI_NODATA: ap->flags |= ATA_FLAG_NOINTR; ata_tf_to_host(ap, &qc->tf); - ata_port_queue_task(ap, atapi_packet_task, ap, 0); + ata_port_queue_task(ap, atapi_packet_task, qc, 0); break; case ATA_PROT_ATAPI_DMA: ap->flags |= ATA_FLAG_NOINTR; ap->ops->tf_load(ap, &qc->tf); /* load tf registers */ ap->ops->bmdma_setup(qc); /* set up bmdma */ - ata_port_queue_task(ap, atapi_packet_task, ap, 0); + ata_port_queue_task(ap, atapi_packet_task, qc, 0); break; default: @@ -4211,7 +4354,7 @@ irqreturn_t ata_interrupt (int irq, void *dev_instance, struct pt_regs *regs) ap = host_set->ports[i]; if (ap && - !(ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR))) { + !(ap->flags & (ATA_FLAG_DISABLED | ATA_FLAG_NOINTR))) { struct ata_queued_cmd *qc; qc = ata_qc_from_tag(ap, ap->active_tag); @@ -4243,7 +4386,7 @@ static int ata_do_simple_cmd(struct ata_port *ap, struct ata_device *dev, tf.flags |= ATA_TFLAG_DEVICE; tf.protocol = ATA_PROT_NODATA; - err = ata_exec_internal(ap, dev, &tf, DMA_NONE, NULL, 0); + err = ata_exec_internal(ap, dev, &tf, NULL, DMA_NONE, NULL, 0); if (err) printk(KERN_ERR "%s: ata command failed: %d\n", __FUNCTION__, err); @@ -4289,10 +4432,12 @@ static int ata_start_drive(struct ata_port *ap, struct ata_device *dev) int ata_device_resume(struct ata_port *ap, struct ata_device *dev) { if (ap->flags & ATA_FLAG_SUSPENDED) { + struct ata_device *failed_dev; ap->flags &= ~ATA_FLAG_SUSPENDED; - ata_set_mode(ap); + while (ata_set_mode(ap, &failed_dev)) + ata_dev_disable(ap, failed_dev); } - if (!ata_dev_present(dev)) + if (!ata_dev_enabled(dev)) return 0; if (dev->class == ATA_DEV_ATA) ata_start_drive(ap, dev); @@ -4310,7 +4455,7 @@ int ata_device_resume(struct ata_port *ap, struct ata_device *dev) */ int ata_device_suspend(struct ata_port *ap, struct ata_device *dev, pm_message_t state) { - if (!ata_dev_present(dev)) + if (!ata_dev_enabled(dev)) return 0; if (dev->class == ATA_DEV_ATA) ata_flush_cache(ap, dev); @@ -4430,7 +4575,7 @@ static void ata_host_init(struct ata_port *ap, struct Scsi_Host *host, host->unique_id = ata_unique_id++; host->max_cmd_len = 12; - ap->flags = ATA_FLAG_PORT_DISABLED; + ap->flags = ATA_FLAG_DISABLED; ap->id = host->unique_id; ap->host = host; ap->ctl = ATA_DEVCTL_OBS; @@ -4445,6 +4590,7 @@ static void ata_host_init(struct ata_port *ap, struct Scsi_Host *host, ap->flags |= ent->host_flags; ap->ops = ent->port_ops; ap->cbl = ATA_CBL_NONE; + ap->sata_spd_limit = UINT_MAX; ap->active_tag = ATA_TAG_POISON; ap->last_ctl = 0xFF; @@ -4505,7 +4651,7 @@ static struct ata_port * ata_host_add(const struct ata_probe_ent *ent, host->transportt = &ata_scsi_transport_template; - ap = (struct ata_port *) &host->hostdata[0]; + ap = ata_shost_to_port(host); ata_host_init(ap, host, host_set, ent, port_no); @@ -4718,7 +4864,7 @@ void ata_host_set_remove(struct ata_host_set *host_set) int ata_scsi_release(struct Scsi_Host *host) { - struct ata_port *ap = (struct ata_port *) &host->hostdata[0]; + struct ata_port *ap = ata_shost_to_port(host); int i; DPRINTK("ENTER\n"); @@ -4885,6 +5031,52 @@ int ata_ratelimit(void) return rc; } +/** + * ata_wait_register - wait until register value changes + * @reg: IO-mapped register + * @mask: Mask to apply to read register value + * @val: Wait condition + * @interval_msec: polling interval in milliseconds + * @timeout_msec: timeout in milliseconds + * + * Waiting for some bits of register to change is a common + * operation for ATA controllers. This function reads 32bit LE + * IO-mapped register @reg and tests for the following condition. + * + * (*@reg & mask) != val + * + * If the condition is met, it returns; otherwise, the process is + * repeated after @interval_msec until timeout. + * + * LOCKING: + * Kernel thread context (may sleep) + * + * RETURNS: + * The final register value. + */ +u32 ata_wait_register(void __iomem *reg, u32 mask, u32 val, + unsigned long interval_msec, + unsigned long timeout_msec) +{ + unsigned long timeout; + u32 tmp; + + tmp = ioread32(reg); + + /* Calculate timeout _after_ the first read to make sure + * preceding writes reach the controller before starting to + * eat away the timeout. + */ + timeout = jiffies + (timeout_msec * HZ) / 1000; + + while ((tmp & mask) == val && time_before(jiffies, timeout)) { + msleep(interval_msec); + tmp = ioread32(reg); + } + + return tmp; +} + /* * libata is essentially a library of internal helper functions for * low-level ATA host controller drivers. As such, the API/ABI is @@ -4900,7 +5092,6 @@ EXPORT_SYMBOL_GPL(ata_sg_init); EXPORT_SYMBOL_GPL(ata_sg_init_one); EXPORT_SYMBOL_GPL(__ata_qc_complete); EXPORT_SYMBOL_GPL(ata_qc_issue_prot); -EXPORT_SYMBOL_GPL(ata_eng_timeout); EXPORT_SYMBOL_GPL(ata_tf_load); EXPORT_SYMBOL_GPL(ata_tf_read); EXPORT_SYMBOL_GPL(ata_noop_dev_select); @@ -4922,6 +5113,7 @@ EXPORT_SYMBOL_GPL(ata_bmdma_irq_clear); EXPORT_SYMBOL_GPL(ata_bmdma_status); EXPORT_SYMBOL_GPL(ata_bmdma_stop); EXPORT_SYMBOL_GPL(ata_port_probe); +EXPORT_SYMBOL_GPL(ata_set_sata_spd); EXPORT_SYMBOL_GPL(sata_phy_reset); EXPORT_SYMBOL_GPL(__sata_phy_reset); EXPORT_SYMBOL_GPL(ata_bus_reset); @@ -4936,19 +5128,17 @@ EXPORT_SYMBOL_GPL(ata_dev_classify); EXPORT_SYMBOL_GPL(ata_dev_pair); EXPORT_SYMBOL_GPL(ata_port_disable); EXPORT_SYMBOL_GPL(ata_ratelimit); +EXPORT_SYMBOL_GPL(ata_wait_register); EXPORT_SYMBOL_GPL(ata_busy_sleep); EXPORT_SYMBOL_GPL(ata_port_queue_task); EXPORT_SYMBOL_GPL(ata_scsi_ioctl); EXPORT_SYMBOL_GPL(ata_scsi_queuecmd); -EXPORT_SYMBOL_GPL(ata_scsi_error); EXPORT_SYMBOL_GPL(ata_scsi_slave_config); EXPORT_SYMBOL_GPL(ata_scsi_release); EXPORT_SYMBOL_GPL(ata_host_intr); EXPORT_SYMBOL_GPL(ata_id_string); EXPORT_SYMBOL_GPL(ata_id_c_string); EXPORT_SYMBOL_GPL(ata_scsi_simulate); -EXPORT_SYMBOL_GPL(ata_eh_qc_complete); -EXPORT_SYMBOL_GPL(ata_eh_qc_retry); EXPORT_SYMBOL_GPL(ata_pio_need_iordy); EXPORT_SYMBOL_GPL(ata_timing_compute); @@ -4970,3 +5160,8 @@ EXPORT_SYMBOL_GPL(ata_device_suspend); EXPORT_SYMBOL_GPL(ata_device_resume); EXPORT_SYMBOL_GPL(ata_scsi_device_suspend); EXPORT_SYMBOL_GPL(ata_scsi_device_resume); + +EXPORT_SYMBOL_GPL(ata_scsi_error); +EXPORT_SYMBOL_GPL(ata_eng_timeout); +EXPORT_SYMBOL_GPL(ata_eh_qc_complete); +EXPORT_SYMBOL_GPL(ata_eh_qc_retry); diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c new file mode 100644 index 000000000000..c31b13fd5307 --- /dev/null +++ b/drivers/scsi/libata-eh.c @@ -0,0 +1,263 @@ +/* + * libata-eh.c - libata error handling + * + * Maintained by: Jeff Garzik + * Please ALWAYS copy linux-ide@vger.kernel.org + * on emails. + * + * Copyright 2006 Tejun Heo + * + * + * 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, 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; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, + * USA. + * + * + * libata documentation is available via 'make {ps|pdf}docs', + * as Documentation/DocBook/libata.* + * + * Hardware documentation available from http://www.t13.org/ and + * http://www.sata-io.org/ + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "libata.h" + +/** + * ata_scsi_timed_out - SCSI layer time out callback + * @cmd: timed out SCSI command + * + * Handles SCSI layer timeout. We race with normal completion of + * the qc for @cmd. If the qc is already gone, we lose and let + * the scsi command finish (EH_HANDLED). Otherwise, the qc has + * timed out and EH should be invoked. Prevent ata_qc_complete() + * from finishing it by setting EH_SCHEDULED and return + * EH_NOT_HANDLED. + * + * LOCKING: + * Called from timer context + * + * RETURNS: + * EH_HANDLED or EH_NOT_HANDLED + */ +enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd) +{ + struct Scsi_Host *host = cmd->device->host; + struct ata_port *ap = ata_shost_to_port(host); + unsigned long flags; + struct ata_queued_cmd *qc; + enum scsi_eh_timer_return ret = EH_HANDLED; + + DPRINTK("ENTER\n"); + + spin_lock_irqsave(&ap->host_set->lock, flags); + qc = ata_qc_from_tag(ap, ap->active_tag); + if (qc) { + WARN_ON(qc->scsicmd != cmd); + qc->flags |= ATA_QCFLAG_EH_SCHEDULED; + qc->err_mask |= AC_ERR_TIMEOUT; + ret = EH_NOT_HANDLED; + } + spin_unlock_irqrestore(&ap->host_set->lock, flags); + + DPRINTK("EXIT, ret=%d\n", ret); + return ret; +} + +/** + * 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. + */ +void ata_scsi_error(struct Scsi_Host *host) +{ + struct ata_port *ap = ata_shost_to_port(host); + + DPRINTK("ENTER\n"); + + /* synchronize with IRQ handler and port task */ + spin_unlock_wait(&ap->host_set->lock); + ata_port_flush_task(ap); + + WARN_ON(ata_qc_from_tag(ap, ap->active_tag) == NULL); + + ap->ops->eng_timeout(ap); + + WARN_ON(host->host_failed || !list_empty(&host->eh_cmd_q)); + + scsi_eh_flush_done_q(&ap->eh_done_q); + + DPRINTK("EXIT\n"); +} + +/** + * ata_qc_timeout - Handle timeout of queued command + * @qc: Command that timed out + * + * 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) + */ +static void ata_qc_timeout(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct ata_host_set *host_set = ap->host_set; + u8 host_stat = 0, drv_stat; + unsigned long flags; + + DPRINTK("ENTER\n"); + + ap->hsm_task_state = HSM_ST_IDLE; + + spin_lock_irqsave(&host_set->lock, flags); + + switch (qc->tf.protocol) { + + case ATA_PROT_DMA: + case ATA_PROT_ATAPI_DMA: + host_stat = ap->ops->bmdma_status(ap); + + /* before we do anything else, clear DMA-Start bit */ + ap->ops->bmdma_stop(qc); + + /* fall through */ + + default: + ata_altstatus(ap); + drv_stat = ata_chk_status(ap); + + /* ack bmdma irq events */ + ap->ops->irq_clear(ap); + + printk(KERN_ERR "ata%u: command 0x%x timeout, stat 0x%x host_stat 0x%x\n", + ap->id, qc->tf.command, drv_stat, host_stat); + + /* complete taskfile transaction */ + qc->err_mask |= ac_err_mask(drv_stat); + break; + } + + spin_unlock_irqrestore(&host_set->lock, flags); + + ata_eh_qc_complete(qc); + + DPRINTK("EXIT\n"); +} + +/** + * 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) +{ + DPRINTK("ENTER\n"); + + ata_qc_timeout(ata_qc_from_tag(ap, ap->active_tag)); + + DPRINTK("EXIT\n"); +} + +static void ata_eh_scsidone(struct scsi_cmnd *scmd) +{ + /* nada */ +} + +static void __ata_eh_qc_complete(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct scsi_cmnd *scmd = qc->scsicmd; + unsigned long flags; + + spin_lock_irqsave(&ap->host_set->lock, flags); + qc->scsidone = ata_eh_scsidone; + __ata_qc_complete(qc); + WARN_ON(ata_tag_valid(qc->tag)); + spin_unlock_irqrestore(&ap->host_set->lock, flags); + + scsi_eh_finish_cmd(scmd, &ap->eh_done_q); +} + +/** + * ata_eh_qc_complete - Complete an active ATA command from EH + * @qc: Command to complete + * + * Indicate to the mid and upper layers that an ATA command has + * completed. To be used from EH. + */ +void ata_eh_qc_complete(struct ata_queued_cmd *qc) +{ + struct scsi_cmnd *scmd = qc->scsicmd; + scmd->retries = scmd->allowed; + __ata_eh_qc_complete(qc); +} + +/** + * ata_eh_qc_retry - Tell midlayer to retry an ATA command after EH + * @qc: Command to retry + * + * Indicate to the mid and upper layers that an ATA command + * should be retried. To be used from EH. + * + * SCSI midlayer limits the number of retries to scmd->allowed. + * scmd->retries is decremented for commands which get retried + * due to unrelated failures (qc->err_mask is zero). + */ +void ata_eh_qc_retry(struct ata_queued_cmd *qc) +{ + struct scsi_cmnd *scmd = qc->scsicmd; + if (!qc->err_mask && scmd->retries) + scmd->retries--; + __ata_eh_qc_complete(qc); +} diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c index 410c78795ca8..159537879f7e 100644 --- a/drivers/scsi/libata-scsi.c +++ b/drivers/scsi/libata-scsi.c @@ -53,7 +53,6 @@ typedef unsigned int (*ata_xlat_func_t)(struct ata_queued_cmd *qc, const u8 *scsicmd); static struct ata_device * ata_scsi_find_dev(struct ata_port *ap, const struct scsi_device *scsidev); -enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd); #define RW_RECOVERY_MPAGE 0x1 #define RW_RECOVERY_MPAGE_LEN 12 @@ -99,6 +98,7 @@ static const u8 def_control_mpage[CONTROL_MPAGE_LEN] = { * It just needs the eh_timed_out hook. */ struct scsi_transport_template ata_scsi_transport_template = { + .eh_strategy_handler = ata_scsi_error, .eh_timed_out = ata_scsi_timed_out, }; @@ -395,7 +395,7 @@ void ata_dump_status(unsigned id, struct ata_taskfile *tf) int ata_scsi_device_resume(struct scsi_device *sdev) { - struct ata_port *ap = (struct ata_port *) &sdev->host->hostdata[0]; + struct ata_port *ap = ata_shost_to_port(sdev->host); struct ata_device *dev = &ap->device[sdev->id]; return ata_device_resume(ap, dev); @@ -403,7 +403,7 @@ int ata_scsi_device_resume(struct scsi_device *sdev) int ata_scsi_device_suspend(struct scsi_device *sdev, pm_message_t state) { - struct ata_port *ap = (struct ata_port *) &sdev->host->hostdata[0]; + struct ata_port *ap = ata_shost_to_port(sdev->host); struct ata_device *dev = &ap->device[sdev->id]; return ata_device_suspend(ap, dev, state); @@ -545,17 +545,12 @@ void ata_gen_ata_desc_sense(struct ata_queued_cmd *qc) cmd->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION; - /* - * Read the controller registers. - */ - WARN_ON(qc->ap->ops->tf_read == NULL); - qc->ap->ops->tf_read(qc->ap, tf); - /* * Use ata_to_sense_error() to map status register bits * onto sense key, asc & ascq. */ - if (tf->command & (ATA_BUSY | ATA_DF | ATA_ERR | ATA_DRQ)) { + if (qc->err_mask || + tf->command & (ATA_BUSY | ATA_DF | ATA_ERR | ATA_DRQ)) { ata_to_sense_error(qc->ap->id, tf->command, tf->feature, &sb[1], &sb[2], &sb[3]); sb[1] &= 0x0f; @@ -620,17 +615,12 @@ void ata_gen_fixed_sense(struct ata_queued_cmd *qc) cmd->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION; - /* - * Read the controller registers. - */ - WARN_ON(qc->ap->ops->tf_read == NULL); - qc->ap->ops->tf_read(qc->ap, tf); - /* * Use ata_to_sense_error() to map status register bits * onto sense key, asc & ascq. */ - if (tf->command & (ATA_BUSY | ATA_DF | ATA_ERR | ATA_DRQ)) { + if (qc->err_mask || + tf->command & (ATA_BUSY | ATA_DF | ATA_ERR | ATA_DRQ)) { ata_to_sense_error(qc->ap->id, tf->command, tf->feature, &sb[2], &sb[12], &sb[13]); sb[2] &= 0x0f; @@ -714,7 +704,7 @@ int ata_scsi_slave_config(struct scsi_device *sdev) struct ata_port *ap; struct ata_device *dev; - ap = (struct ata_port *) &sdev->host->hostdata[0]; + ap = ata_shost_to_port(sdev->host); dev = &ap->device[sdev->id]; ata_scsi_dev_config(sdev, dev); @@ -723,141 +713,6 @@ int ata_scsi_slave_config(struct scsi_device *sdev) return 0; /* scsi layer doesn't check return value, sigh */ } -/** - * ata_scsi_timed_out - SCSI layer time out callback - * @cmd: timed out SCSI command - * - * Handles SCSI layer timeout. We race with normal completion of - * the qc for @cmd. If the qc is already gone, we lose and let - * the scsi command finish (EH_HANDLED). Otherwise, the qc has - * timed out and EH should be invoked. Prevent ata_qc_complete() - * from finishing it by setting EH_SCHEDULED and return - * EH_NOT_HANDLED. - * - * LOCKING: - * Called from timer context - * - * RETURNS: - * EH_HANDLED or EH_NOT_HANDLED - */ -enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd) -{ - struct Scsi_Host *host = cmd->device->host; - struct ata_port *ap = (struct ata_port *) &host->hostdata[0]; - unsigned long flags; - struct ata_queued_cmd *qc; - enum scsi_eh_timer_return ret = EH_HANDLED; - - DPRINTK("ENTER\n"); - - spin_lock_irqsave(&ap->host_set->lock, flags); - qc = ata_qc_from_tag(ap, ap->active_tag); - if (qc) { - WARN_ON(qc->scsicmd != cmd); - qc->flags |= ATA_QCFLAG_EH_SCHEDULED; - qc->err_mask |= AC_ERR_TIMEOUT; - ret = EH_NOT_HANDLED; - } - spin_unlock_irqrestore(&ap->host_set->lock, flags); - - DPRINTK("EXIT, ret=%d\n", ret); - return ret; -} - -/** - * 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; - unsigned long flags; - - DPRINTK("ENTER\n"); - - ap = (struct ata_port *) &host->hostdata[0]; - - spin_lock_irqsave(&ap->host_set->lock, flags); - WARN_ON(ap->flags & ATA_FLAG_IN_EH); - ap->flags |= ATA_FLAG_IN_EH; - WARN_ON(ata_qc_from_tag(ap, ap->active_tag) == NULL); - spin_unlock_irqrestore(&ap->host_set->lock, flags); - - ata_port_flush_task(ap); - - ap->ops->eng_timeout(ap); - - WARN_ON(host->host_failed || !list_empty(&host->eh_cmd_q)); - - scsi_eh_flush_done_q(&ap->eh_done_q); - - spin_lock_irqsave(&ap->host_set->lock, flags); - ap->flags &= ~ATA_FLAG_IN_EH; - spin_unlock_irqrestore(&ap->host_set->lock, flags); - - DPRINTK("EXIT\n"); - return 0; -} - -static void ata_eh_scsidone(struct scsi_cmnd *scmd) -{ - /* nada */ -} - -static void __ata_eh_qc_complete(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - struct scsi_cmnd *scmd = qc->scsicmd; - unsigned long flags; - - spin_lock_irqsave(&ap->host_set->lock, flags); - qc->scsidone = ata_eh_scsidone; - __ata_qc_complete(qc); - WARN_ON(ata_tag_valid(qc->tag)); - spin_unlock_irqrestore(&ap->host_set->lock, flags); - - scsi_eh_finish_cmd(scmd, &ap->eh_done_q); -} - -/** - * ata_eh_qc_complete - Complete an active ATA command from EH - * @qc: Command to complete - * - * Indicate to the mid and upper layers that an ATA command has - * completed. To be used from EH. - */ -void ata_eh_qc_complete(struct ata_queued_cmd *qc) -{ - struct scsi_cmnd *scmd = qc->scsicmd; - scmd->retries = scmd->allowed; - __ata_eh_qc_complete(qc); -} - -/** - * ata_eh_qc_retry - Tell midlayer to retry an ATA command after EH - * @qc: Command to retry - * - * Indicate to the mid and upper layers that an ATA command - * should be retried. To be used from EH. - * - * SCSI midlayer limits the number of retries to scmd->allowed. - * This function might need to adjust scmd->retries for commands - * which get retried due to unrelated NCQ failures. - */ -void ata_eh_qc_retry(struct ata_queued_cmd *qc) -{ - __ata_eh_qc_complete(qc); -} - /** * ata_scsi_start_stop_xlat - Translate SCSI START STOP UNIT command * @qc: Storage for translated ATA taskfile @@ -1197,6 +1052,7 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, const u8 *scsicm u64 block; u32 n_block; + qc->flags |= ATA_QCFLAG_IO; tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; if (scsicmd[0] == WRITE_10 || scsicmd[0] == WRITE_6 || @@ -1343,11 +1199,14 @@ static void ata_scsi_qc_complete(struct ata_queued_cmd *qc) */ if (((cdb[0] == ATA_16) || (cdb[0] == ATA_12)) && ((cdb[2] & 0x20) || need_sense)) { + qc->ap->ops->tf_read(qc->ap, &qc->tf); ata_gen_ata_desc_sense(qc); } else { if (!need_sense) { cmd->result = SAM_STAT_GOOD; } else { + qc->ap->ops->tf_read(qc->ap, &qc->tf); + /* TODO: decide which descriptor format to use * for 48b LBA devices and call that here * instead of the fixed desc, which is only @@ -1431,9 +1290,7 @@ static void ata_scsi_translate(struct ata_port *ap, struct ata_device *dev, goto early_finish; /* select device, send command to hardware */ - qc->err_mask = ata_qc_issue(qc); - if (qc->err_mask) - ata_qc_complete(qc); + ata_qc_issue(qc); VPRINTK("EXIT\n"); return; @@ -2141,13 +1998,15 @@ void ata_scsi_badcmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *), u8 static void atapi_sense_complete(struct ata_queued_cmd *qc) { - if (qc->err_mask && ((qc->err_mask & AC_ERR_DEV) == 0)) + if (qc->err_mask && ((qc->err_mask & AC_ERR_DEV) == 0)) { /* FIXME: not quite right; we don't want the * translation of taskfile registers into * a sense descriptors, since that's only * correct for ATA, not ATAPI */ + qc->ap->ops->tf_read(qc->ap, &qc->tf); ata_gen_ata_desc_sense(qc); + } qc->scsidone(qc->scsicmd); ata_qc_free(qc); @@ -2199,9 +2058,7 @@ static void atapi_request_sense(struct ata_queued_cmd *qc) qc->complete_fn = atapi_sense_complete; - qc->err_mask = ata_qc_issue(qc); - if (qc->err_mask) - ata_qc_complete(qc); + ata_qc_issue(qc); DPRINTK("EXIT\n"); } @@ -2217,17 +2074,15 @@ static void atapi_qc_complete(struct ata_queued_cmd *qc) cmd->result = SAM_STAT_CHECK_CONDITION; atapi_request_sense(qc); return; - } - - else if (unlikely(err_mask)) + } else if (unlikely(err_mask)) { /* FIXME: not quite right; we don't want the * translation of taskfile registers into * a sense descriptors, since that's only * correct for ATA, not ATAPI */ + qc->ap->ops->tf_read(qc->ap, &qc->tf); ata_gen_ata_desc_sense(qc); - - else { + } else { u8 *scsicmd = cmd->cmnd; if ((scsicmd[0] == INQUIRY) && ((scsicmd[1] & 0x03) == 0)) { @@ -2309,11 +2164,9 @@ static unsigned int atapi_xlat(struct ata_queued_cmd *qc, const u8 *scsicmd) 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 != DMA_TO_DEVICE) + if (atapi_dmadir && (cmd->sc_data_direction != DMA_TO_DEVICE)) + /* some SATA bridges need us to indicate data xfer direction */ qc->tf.feature |= ATAPI_DMADIR; -#endif } qc->nbytes = cmd->bufflen; @@ -2353,7 +2206,7 @@ ata_scsi_find_dev(struct ata_port *ap, const struct scsi_device *scsidev) (scsidev->lun != 0))) return NULL; - if (unlikely(!ata_dev_present(dev))) + if (unlikely(!ata_dev_enabled(dev))) return NULL; if (!atapi_enabled || (ap->flags & ATA_FLAG_NO_ATAPI)) { @@ -2625,7 +2478,7 @@ int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) struct scsi_device *scsidev = cmd->device; struct Scsi_Host *shost = scsidev->host; - ap = (struct ata_port *) &shost->hostdata[0]; + ap = ata_shost_to_port(shost); spin_unlock(shost->host_lock); spin_lock(&ap->host_set->lock); @@ -2741,13 +2594,13 @@ void ata_scsi_scan_host(struct ata_port *ap) struct ata_device *dev; unsigned int i; - if (ap->flags & ATA_FLAG_PORT_DISABLED) + if (ap->flags & ATA_FLAG_DISABLED) return; for (i = 0; i < ATA_MAX_DEVICES; i++) { dev = &ap->device[i]; - if (ata_dev_present(dev)) + if (ata_dev_enabled(dev)) scsi_scan_target(&ap->host->shost_gendev, 0, i, 0, 0); } } diff --git a/drivers/scsi/libata.h b/drivers/scsi/libata.h index 65f52beea884..3f8b0a863781 100644 --- a/drivers/scsi/libata.h +++ b/drivers/scsi/libata.h @@ -29,7 +29,7 @@ #define __LIBATA_H__ #define DRV_NAME "libata" -#define DRV_VERSION "1.20" /* must be exactly four chars */ +#define DRV_VERSION "1.30" /* must be exactly four chars */ struct ata_scsi_args { struct ata_port *ap; @@ -41,13 +41,25 @@ struct ata_scsi_args { /* libata-core.c */ extern int atapi_enabled; +extern int atapi_dmadir; extern int libata_fua; extern struct ata_queued_cmd *ata_qc_new_init(struct ata_port *ap, struct ata_device *dev); extern int ata_rwcmd_protocol(struct ata_queued_cmd *qc); +extern void ata_dev_disable(struct ata_port *ap, struct ata_device *dev); extern void ata_port_flush_task(struct ata_port *ap); +extern unsigned ata_exec_internal(struct ata_port *ap, struct ata_device *dev, + struct ata_taskfile *tf, const u8 *cdb, + int dma_dir, void *buf, unsigned int buflen); +extern int ata_down_sata_spd_limit(struct ata_port *ap); +extern int ata_set_sata_spd_needed(struct ata_port *ap); +extern int ata_down_xfermask_limit(struct ata_port *ap, struct ata_device *dev, + int force_pio0); +extern int ata_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev); +extern int ata_do_reset(struct ata_port *ap, ata_reset_fn_t reset, + ata_postreset_fn_t postreset, unsigned int *classes); extern void ata_qc_free(struct ata_queued_cmd *qc); -extern unsigned int ata_qc_issue(struct ata_queued_cmd *qc); +extern void ata_qc_issue(struct ata_queued_cmd *qc); extern int ata_check_atapi_dma(struct ata_queued_cmd *qc); extern void ata_dev_select(struct ata_port *ap, unsigned int device, unsigned int wait, unsigned int can_sleep); @@ -60,7 +72,6 @@ extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg); extern struct scsi_transport_template ata_scsi_transport_template; extern void ata_scsi_scan_host(struct ata_port *ap); -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); @@ -90,4 +101,8 @@ extern void ata_scsi_rbuf_fill(struct ata_scsi_args *args, unsigned int (*actor) (struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen)); +/* libata-eh.c */ +extern enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd); +extern void ata_scsi_error(struct Scsi_Host *host); + #endif /* __LIBATA_H__ */ diff --git a/drivers/scsi/pcmcia/aha152x_stub.c b/drivers/scsi/pcmcia/aha152x_stub.c index 5609847e254a..ee449b29fc82 100644 --- a/drivers/scsi/pcmcia/aha152x_stub.c +++ b/drivers/scsi/pcmcia/aha152x_stub.c @@ -89,29 +89,29 @@ MODULE_LICENSE("Dual MPL/GPL"); /*====================================================================*/ typedef struct scsi_info_t { - dev_link_t link; + struct pcmcia_device *p_dev; dev_node_t node; struct Scsi_Host *host; } scsi_info_t; -static void aha152x_release_cs(dev_link_t *link); +static void aha152x_release_cs(struct pcmcia_device *link); static void aha152x_detach(struct pcmcia_device *p_dev); -static void aha152x_config_cs(dev_link_t *link); +static int aha152x_config_cs(struct pcmcia_device *link); -static dev_link_t *dev_list; +static struct pcmcia_device *dev_list; -static int aha152x_attach(struct pcmcia_device *p_dev) +static int aha152x_probe(struct pcmcia_device *link) { scsi_info_t *info; - dev_link_t *link; - + DEBUG(0, "aha152x_attach()\n"); /* Create new SCSI device */ info = kmalloc(sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; memset(info, 0, sizeof(*info)); - link = &info->link; link->priv = info; + info->p_dev = link; + link->priv = info; link->io.NumPorts1 = 0x20; link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; @@ -119,41 +119,22 @@ static int aha152x_attach(struct pcmcia_device *p_dev) link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; link->conf.Present = PRESENT_OPTION; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - aha152x_config_cs(link); - - return 0; + return aha152x_config_cs(link); } /* aha152x_attach */ /*====================================================================*/ -static void aha152x_detach(struct pcmcia_device *p_dev) +static void aha152x_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); - dev_link_t **linkp; - DEBUG(0, "aha152x_detach(0x%p)\n", link); - - /* Locate device structure */ - for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) - if (*linkp == link) break; - if (*linkp == NULL) - return; - if (link->state & DEV_CONFIG) - aha152x_release_cs(link); + aha152x_release_cs(link); /* Unlink device structure, free bits */ - *linkp = link->next; kfree(link->priv); - } /* aha152x_detach */ /*====================================================================*/ @@ -161,9 +142,8 @@ static void aha152x_detach(struct pcmcia_device *p_dev) #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) -static void aha152x_config_cs(dev_link_t *link) +static int aha152x_config_cs(struct pcmcia_device *link) { - client_handle_t handle = link->handle; scsi_info_t *info = link->priv; struct aha152x_setup s; tuple_t tuple; @@ -178,19 +158,16 @@ static void aha152x_config_cs(dev_link_t *link) tuple.TupleData = tuple_data; tuple.TupleDataMax = 64; tuple.TupleOffset = 0; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; - /* Configure card */ - link->state |= DEV_CONFIG; - tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); while (1) { - if (pcmcia_get_tuple_data(handle, &tuple) != 0 || - pcmcia_parse_tuple(handle, &tuple, &parse) != 0) + if (pcmcia_get_tuple_data(link, &tuple) != 0 || + pcmcia_parse_tuple(link, &tuple, &parse) != 0) goto next_entry; /* For New Media T&J, look for a SCSI window */ if (parse.cftable_entry.io.win[0].len >= 0x20) @@ -201,15 +178,15 @@ static void aha152x_config_cs(dev_link_t *link) if ((parse.cftable_entry.io.nwin > 0) && (link->io.BasePort1 < 0xffff)) { link->conf.ConfigIndex = parse.cftable_entry.index; - i = pcmcia_request_io(handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) break; } next_entry: - CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple)); + CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple)); } - CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq)); - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); /* Set configuration options for the aha152x driver */ memset(&s, 0, sizeof(s)); @@ -231,53 +208,30 @@ static void aha152x_config_cs(dev_link_t *link) } sprintf(info->node.dev_name, "scsi%d", host->host_no); - link->dev = &info->node; + link->dev_node = &info->node; info->host = host; - link->state &= ~DEV_CONFIG_PENDING; - return; - + return 0; + cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); aha152x_release_cs(link); - return; + return -ENODEV; } -static void aha152x_release_cs(dev_link_t *link) +static void aha152x_release_cs(struct pcmcia_device *link) { scsi_info_t *info = link->priv; aha152x_release(info->host); - link->dev = NULL; - - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - - link->state &= ~DEV_CONFIG; + pcmcia_disable_device(link); } -static int aha152x_suspend(struct pcmcia_device *dev) +static int aha152x_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(dev); - - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) - pcmcia_release_configuration(link->handle); - - return 0; -} - -static int aha152x_resume(struct pcmcia_device *dev) -{ - dev_link_t *link = dev_to_instance(dev); scsi_info_t *info = link->priv; - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - pcmcia_request_configuration(link->handle, &link->conf); - aha152x_host_reset_host(info->host); - } + aha152x_host_reset_host(info->host); return 0; } @@ -297,10 +251,9 @@ static struct pcmcia_driver aha152x_cs_driver = { .drv = { .name = "aha152x_cs", }, - .probe = aha152x_attach, + .probe = aha152x_probe, .remove = aha152x_detach, .id_table = aha152x_ids, - .suspend = aha152x_suspend, .resume = aha152x_resume, }; @@ -317,4 +270,3 @@ static void __exit exit_aha152x_cs(void) module_init(init_aha152x_cs); module_exit(exit_aha152x_cs); - diff --git a/drivers/scsi/pcmcia/fdomain_stub.c b/drivers/scsi/pcmcia/fdomain_stub.c index 788c58d805f3..85f7ffac19a0 100644 --- a/drivers/scsi/pcmcia/fdomain_stub.c +++ b/drivers/scsi/pcmcia/fdomain_stub.c @@ -73,57 +73,48 @@ static char *version = /*====================================================================*/ typedef struct scsi_info_t { - dev_link_t link; + struct pcmcia_device *p_dev; dev_node_t node; struct Scsi_Host *host; } scsi_info_t; -static void fdomain_release(dev_link_t *link); +static void fdomain_release(struct pcmcia_device *link); static void fdomain_detach(struct pcmcia_device *p_dev); -static void fdomain_config(dev_link_t *link); +static int fdomain_config(struct pcmcia_device *link); -static int fdomain_attach(struct pcmcia_device *p_dev) +static int fdomain_probe(struct pcmcia_device *link) { - scsi_info_t *info; - dev_link_t *link; + scsi_info_t *info; - DEBUG(0, "fdomain_attach()\n"); + DEBUG(0, "fdomain_attach()\n"); - /* Create new SCSI device */ - info = kmalloc(sizeof(*info), GFP_KERNEL); - if (!info) return -ENOMEM; - memset(info, 0, sizeof(*info)); - link = &info->link; link->priv = info; - link->io.NumPorts1 = 0x10; - link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; - link->io.IOAddrLines = 10; - link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; - link->irq.IRQInfo1 = IRQ_LEVEL_ID; - link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; - link->conf.IntType = INT_MEMORY_AND_IO; - link->conf.Present = PRESENT_OPTION; + /* Create new SCSI device */ + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; - link->handle = p_dev; - p_dev->instance = link; + info->p_dev = link; + link->priv = info; + link->io.NumPorts1 = 0x10; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; + link->io.IOAddrLines = 10; + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.IRQInfo1 = IRQ_LEVEL_ID; + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.IntType = INT_MEMORY_AND_IO; + link->conf.Present = PRESENT_OPTION; - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - fdomain_config(link); - - return 0; + return fdomain_config(link); } /* fdomain_attach */ /*====================================================================*/ -static void fdomain_detach(struct pcmcia_device *p_dev) +static void fdomain_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); - DEBUG(0, "fdomain_detach(0x%p)\n", link); - if (link->state & DEV_CONFIG) - fdomain_release(link); + fdomain_release(link); kfree(link->priv); } /* fdomain_detach */ @@ -133,9 +124,8 @@ static void fdomain_detach(struct pcmcia_device *p_dev) #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) -static void fdomain_config(dev_link_t *link) +static int fdomain_config(struct pcmcia_device *link) { - client_handle_t handle = link->handle; scsi_info_t *info = link->priv; tuple_t tuple; cisparse_t parse; @@ -150,103 +140,75 @@ static void fdomain_config(dev_link_t *link) tuple.TupleData = tuple_data; tuple.TupleDataMax = 64; tuple.TupleOffset = 0; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; - /* Configure card */ - link->state |= DEV_CONFIG; - tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); while (1) { - if (pcmcia_get_tuple_data(handle, &tuple) != 0 || - pcmcia_parse_tuple(handle, &tuple, &parse) != 0) + if (pcmcia_get_tuple_data(link, &tuple) != 0 || + pcmcia_parse_tuple(link, &tuple, &parse) != 0) goto next_entry; link->conf.ConfigIndex = parse.cftable_entry.index; link->io.BasePort1 = parse.cftable_entry.io.win[0].base; - i = pcmcia_request_io(handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) break; next_entry: - CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple)); + CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple)); } - CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq)); - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf)); - + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); + /* A bad hack... */ release_region(link->io.BasePort1, link->io.NumPorts1); /* Set configuration options for the fdomain driver */ sprintf(str, "%d,%d", link->io.BasePort1, link->irq.AssignedIRQ); fdomain_setup(str); - + host = __fdomain_16x0_detect(&fdomain_driver_template); if (!host) { printk(KERN_INFO "fdomain_cs: no SCSI devices found\n"); goto cs_failed; } - - scsi_add_host(host, NULL); /* XXX handle failure */ + + if (scsi_add_host(host, NULL)) + goto cs_failed; scsi_scan_host(host); sprintf(info->node.dev_name, "scsi%d", host->host_no); - link->dev = &info->node; + link->dev_node = &info->node; info->host = host; - - link->state &= ~DEV_CONFIG_PENDING; - return; - + + return 0; + cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); fdomain_release(link); - return; - + return -ENODEV; } /* fdomain_config */ /*====================================================================*/ -static void fdomain_release(dev_link_t *link) +static void fdomain_release(struct pcmcia_device *link) { - scsi_info_t *info = link->priv; + scsi_info_t *info = link->priv; - DEBUG(0, "fdomain_release(0x%p)\n", link); + DEBUG(0, "fdomain_release(0x%p)\n", link); - scsi_remove_host(info->host); - link->dev = NULL; - - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - - scsi_unregister(info->host); - - link->state &= ~DEV_CONFIG; + scsi_remove_host(info->host); + pcmcia_disable_device(link); + scsi_unregister(info->host); } /*====================================================================*/ -static int fdomain_suspend(struct pcmcia_device *dev) +static int fdomain_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(dev); - - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) - pcmcia_release_configuration(link->handle); - - return 0; -} - -static int fdomain_resume(struct pcmcia_device *dev) -{ - dev_link_t *link = dev_to_instance(dev); - - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - pcmcia_request_configuration(link->handle, &link->conf); - fdomain_16x0_bus_reset(NULL); - } + fdomain_16x0_bus_reset(NULL); return 0; } @@ -264,10 +226,9 @@ static struct pcmcia_driver fdomain_cs_driver = { .drv = { .name = "fdomain_cs", }, - .probe = fdomain_attach, + .probe = fdomain_probe, .remove = fdomain_detach, .id_table = fdomain_ids, - .suspend = fdomain_suspend, .resume = fdomain_resume, }; diff --git a/drivers/scsi/pcmcia/nsp_cs.c b/drivers/scsi/pcmcia/nsp_cs.c index 9e3ab3fd5355..231f9c311c69 100644 --- a/drivers/scsi/pcmcia/nsp_cs.c +++ b/drivers/scsi/pcmcia/nsp_cs.c @@ -1593,11 +1593,11 @@ static int nsp_eh_host_reset(Scsi_Cmnd *SCpnt) configure the card at this point -- we wait until we receive a card insertion event. ======================================================================*/ -static int nsp_cs_attach(struct pcmcia_device *p_dev) +static int nsp_cs_probe(struct pcmcia_device *link) { scsi_info_t *info; - dev_link_t *link; nsp_hw_data *data = &nsp_data_base; + int ret; nsp_dbg(NSP_DEBUG_INIT, "in"); @@ -1605,7 +1605,7 @@ static int nsp_cs_attach(struct pcmcia_device *p_dev) info = kmalloc(sizeof(*info), GFP_KERNEL); if (info == NULL) { return -ENOMEM; } memset(info, 0, sizeof(*info)); - link = &info->link; + info->p_dev = link; link->priv = info; data->ScsiInfo = info; @@ -1627,18 +1627,13 @@ static int nsp_cs_attach(struct pcmcia_device *p_dev) /* General socket configuration */ link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; link->conf.Present = PRESENT_OPTION; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - nsp_cs_config(link); + ret = nsp_cs_config(link); nsp_dbg(NSP_DEBUG_INIT, "link=0x%p", link); - return 0; + return ret; } /* nsp_cs_attach */ @@ -1648,16 +1643,12 @@ static int nsp_cs_attach(struct pcmcia_device *p_dev) structures are freed. Otherwise, the structures will be freed when the device is released. ======================================================================*/ -static void nsp_cs_detach(struct pcmcia_device *p_dev) +static void nsp_cs_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); - nsp_dbg(NSP_DEBUG_INIT, "in, link=0x%p", link); - if (link->state & DEV_CONFIG) { - ((scsi_info_t *)link->priv)->stop = 1; - nsp_cs_release(link); - } + ((scsi_info_t *)link->priv)->stop = 1; + nsp_cs_release(link); kfree(link->priv); link->priv = NULL; @@ -1672,9 +1663,9 @@ static void nsp_cs_detach(struct pcmcia_device *p_dev) #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) /*====================================================================*/ -static void nsp_cs_config(dev_link_t *link) +static int nsp_cs_config(struct pcmcia_device *link) { - client_handle_t handle = link->handle; + int ret; scsi_info_t *info = link->priv; tuple_t tuple; cisparse_t parse; @@ -1698,26 +1689,22 @@ static void nsp_cs_config(dev_link_t *link) tuple.TupleData = tuple_data; tuple.TupleDataMax = sizeof(tuple_data); tuple.TupleOffset = 0; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; - /* Configure card */ - link->state |= DEV_CONFIG; - /* Look up the current Vcc */ - CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf)); - link->conf.Vcc = conf.Vcc; + CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(link, &conf)); tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); while (1) { cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); - if (pcmcia_get_tuple_data(handle, &tuple) != 0 || - pcmcia_parse_tuple(handle, &tuple, &parse) != 0) + if (pcmcia_get_tuple_data(link, &tuple) != 0 || + pcmcia_parse_tuple(link, &tuple, &parse) != 0) goto next_entry; if (cfg->flags & CISTPL_CFTABLE_DEFAULT) { dflt = *cfg; } @@ -1743,10 +1730,10 @@ static void nsp_cs_config(dev_link_t *link) } if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM)) { - link->conf.Vpp1 = link->conf.Vpp2 = + link->conf.Vpp = cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000; } else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM)) { - link->conf.Vpp1 = link->conf.Vpp2 = + link->conf.Vpp = dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000; } @@ -1773,7 +1760,7 @@ static void nsp_cs_config(dev_link_t *link) link->io.NumPorts2 = io->win[1].len; } /* This reserves IO space but doesn't actually enable it */ - if (pcmcia_request_io(link->handle, &link->io) != 0) + if (pcmcia_request_io(link, &link->io) != 0) goto next_entry; } @@ -1788,7 +1775,7 @@ static void nsp_cs_config(dev_link_t *link) req.Size = 0x1000; } req.AccessSpeed = 0; - if (pcmcia_request_window(&link->handle, &req, &link->win) != 0) + if (pcmcia_request_window(&link, &req, &link->win) != 0) goto next_entry; map.Page = 0; map.CardOffset = mem->win[0].card_addr; if (pcmcia_map_mem_page(link->win, &map) != 0) @@ -1802,17 +1789,14 @@ static void nsp_cs_config(dev_link_t *link) next_entry: nsp_dbg(NSP_DEBUG_INIT, "next"); - - if (link->io.NumPorts1) { - pcmcia_release_io(link->handle, &link->io); - } - CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple)); + pcmcia_disable_device(link); + CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple)); } if (link->conf.Attributes & CONF_ENABLE_IRQ) { - CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); } - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); if (free_ports) { if (link->io.BasePort1) { @@ -1854,16 +1838,19 @@ static void nsp_cs_config(dev_link_t *link) #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,74)) - scsi_add_host (host, NULL); + ret = scsi_add_host (host, NULL); + if (ret) + goto cs_failed; + scsi_scan_host(host); snprintf(info->node.dev_name, sizeof(info->node.dev_name), "scsi%d", host->host_no); - link->dev = &info->node; + link->dev_node = &info->node; info->host = host; #else nsp_dbg(NSP_DEBUG_INIT, "GET_SCSI_INFO"); - tail = &link->dev; + tail = &link->dev_node; info->ndev = 0; nsp_dbg(NSP_DEBUG_INIT, "host=0x%p", host); @@ -1908,11 +1895,10 @@ static void nsp_cs_config(dev_link_t *link) #endif /* Finally, report what we've done */ - printk(KERN_INFO "nsp_cs: index 0x%02x: Vcc %d.%d", - link->conf.ConfigIndex, - link->conf.Vcc/10, link->conf.Vcc%10); - if (link->conf.Vpp1) { - printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10); + printk(KERN_INFO "nsp_cs: index 0x%02x: ", + link->conf.ConfigIndex); + if (link->conf.Vpp) { + printk(", Vpp %d.%d", link->conf.Vpp/10, link->conf.Vpp%10); } if (link->conf.Attributes & CONF_ENABLE_IRQ) { printk(", irq %d", link->irq.AssignedIRQ); @@ -1929,15 +1915,14 @@ static void nsp_cs_config(dev_link_t *link) req.Base+req.Size-1); printk("\n"); - link->state &= ~DEV_CONFIG_PENDING; - return; + return 0; cs_failed: nsp_dbg(NSP_DEBUG_INIT, "config fail"); - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); nsp_cs_release(link); - return; + return -ENODEV; } /* nsp_cs_config */ #undef CS_CHECK @@ -1947,7 +1932,7 @@ static void nsp_cs_config(dev_link_t *link) device, and release the PCMCIA configuration. If the device is still open, this will be postponed until it is closed. ======================================================================*/ -static void nsp_cs_release(dev_link_t *link) +static void nsp_cs_release(struct pcmcia_device *link) { scsi_info_t *info = link->priv; nsp_hw_data *data = NULL; @@ -1968,22 +1953,15 @@ static void nsp_cs_release(dev_link_t *link) #else scsi_unregister_host(&nsp_driver_template); #endif - link->dev = NULL; + link->dev_node = NULL; if (link->win) { if (data != NULL) { iounmap((void *)(data->MmioAddress)); } - pcmcia_release_window(link->win); } - pcmcia_release_configuration(link->handle); - if (link->io.NumPorts1) { - pcmcia_release_io(link->handle, &link->io); - } - if (link->irq.AssignedIRQ) { - pcmcia_release_irq(link->handle, &link->irq); - } - link->state &= ~DEV_CONFIG; + pcmcia_disable_device(link); + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,2)) if (info->host != NULL) { scsi_host_put(info->host); @@ -1991,14 +1969,11 @@ static void nsp_cs_release(dev_link_t *link) #endif } /* nsp_cs_release */ -static int nsp_cs_suspend(struct pcmcia_device *dev) +static int nsp_cs_suspend(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(dev); scsi_info_t *info = link->priv; nsp_hw_data *data; - link->state |= DEV_SUSPEND; - nsp_dbg(NSP_DEBUG_INIT, "event: suspend"); if (info->host != NULL) { @@ -2011,25 +1986,16 @@ static int nsp_cs_suspend(struct pcmcia_device *dev) info->stop = 1; - if (link->state & DEV_CONFIG) - pcmcia_release_configuration(link->handle); - return 0; } -static int nsp_cs_resume(struct pcmcia_device *dev) +static int nsp_cs_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(dev); scsi_info_t *info = link->priv; nsp_hw_data *data; nsp_dbg(NSP_DEBUG_INIT, "event: resume"); - link->state &= ~DEV_SUSPEND; - - if (link->state & DEV_CONFIG) - pcmcia_request_configuration(link->handle, &link->conf); - info->stop = 0; if (info->host != NULL) { @@ -2065,7 +2031,7 @@ static struct pcmcia_driver nsp_driver = { .drv = { .name = "nsp_cs", }, - .probe = nsp_cs_attach, + .probe = nsp_cs_probe, .remove = nsp_cs_detach, .id_table = nsp_cs_ids, .suspend = nsp_cs_suspend, @@ -2098,19 +2064,7 @@ static int __init nsp_cs_init(void) static void __exit nsp_cs_exit(void) { nsp_msg(KERN_INFO, "unloading..."); - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,68)) pcmcia_unregister_driver(&nsp_driver); -#else - unregister_pcmcia_driver(&dev_info); - /* XXX: this really needs to move into generic code.. */ - while (dev_list != NULL) { - if (dev_list->state & DEV_CONFIG) { - nsp_cs_release(dev_list); - } - nsp_cs_detach(dev_list); - } -#endif } diff --git a/drivers/scsi/pcmcia/nsp_cs.h b/drivers/scsi/pcmcia/nsp_cs.h index b66b140a745e..8908b8e5b78a 100644 --- a/drivers/scsi/pcmcia/nsp_cs.h +++ b/drivers/scsi/pcmcia/nsp_cs.h @@ -225,7 +225,7 @@ /*====================================================================*/ typedef struct scsi_info_t { - dev_link_t link; + struct pcmcia_device *p_dev; struct Scsi_Host *host; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,74)) dev_node_t node; @@ -297,8 +297,8 @@ typedef struct _nsp_hw_data { /* Card service functions */ static void nsp_cs_detach (struct pcmcia_device *p_dev); -static void nsp_cs_release(dev_link_t *link); -static void nsp_cs_config (dev_link_t *link); +static void nsp_cs_release(struct pcmcia_device *link); +static int nsp_cs_config (struct pcmcia_device *link); /* Linux SCSI subsystem specific functions */ static struct Scsi_Host *nsp_detect (struct scsi_host_template *sht); @@ -450,7 +450,7 @@ static inline struct Scsi_Host *scsi_host_hn_get(unsigned short hostno) return host; } -static void cs_error(client_handle_t handle, int func, int ret) +static void cs_error(struct pcmcia_device *handle, int func, int ret) { error_info_t err = { func, ret }; pcmcia_report_error(handle, &err); diff --git a/drivers/scsi/pcmcia/qlogic_stub.c b/drivers/scsi/pcmcia/qlogic_stub.c index dce7e687fd4a..86c2ac6ae623 100644 --- a/drivers/scsi/pcmcia/qlogic_stub.c +++ b/drivers/scsi/pcmcia/qlogic_stub.c @@ -91,18 +91,18 @@ static struct scsi_host_template qlogicfas_driver_template = { /*====================================================================*/ typedef struct scsi_info_t { - dev_link_t link; + struct pcmcia_device *p_dev; dev_node_t node; struct Scsi_Host *host; unsigned short manf_id; } scsi_info_t; -static void qlogic_release(dev_link_t *link); +static void qlogic_release(struct pcmcia_device *link); static void qlogic_detach(struct pcmcia_device *p_dev); -static void qlogic_config(dev_link_t * link); +static int qlogic_config(struct pcmcia_device * link); static struct Scsi_Host *qlogic_detect(struct scsi_host_template *host, - dev_link_t *link, int qbase, int qlirq) + struct pcmcia_device *link, int qbase, int qlirq) { int qltyp; /* type of chip */ int qinitid; @@ -156,10 +156,9 @@ free_scsi_host: err: return NULL; } -static int qlogic_attach(struct pcmcia_device *p_dev) +static int qlogic_probe(struct pcmcia_device *link) { scsi_info_t *info; - dev_link_t *link; DEBUG(0, "qlogic_attach()\n"); @@ -168,7 +167,7 @@ static int qlogic_attach(struct pcmcia_device *p_dev) if (!info) return -ENOMEM; memset(info, 0, sizeof(*info)); - link = &info->link; + info->p_dev = link; link->priv = info; link->io.NumPorts1 = 16; link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; @@ -176,30 +175,19 @@ static int qlogic_attach(struct pcmcia_device *p_dev) link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; link->conf.Present = PRESENT_OPTION; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - qlogic_config(link); - - return 0; + return qlogic_config(link); } /* qlogic_attach */ /*====================================================================*/ -static void qlogic_detach(struct pcmcia_device *p_dev) +static void qlogic_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); - DEBUG(0, "qlogic_detach(0x%p)\n", link); - if (link->state & DEV_CONFIG) - qlogic_release(link); - + qlogic_release(link); kfree(link->priv); } /* qlogic_detach */ @@ -209,9 +197,8 @@ static void qlogic_detach(struct pcmcia_device *p_dev) #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) -static void qlogic_config(dev_link_t * link) +static int qlogic_config(struct pcmcia_device * link) { - client_handle_t handle = link->handle; scsi_info_t *info = link->priv; tuple_t tuple; cisparse_t parse; @@ -225,38 +212,35 @@ static void qlogic_config(dev_link_t * link) tuple.TupleDataMax = 64; tuple.TupleOffset = 0; tuple.DesiredTuple = CISTPL_CONFIG; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; tuple.DesiredTuple = CISTPL_MANFID; - if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) && (pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS)) + if ((pcmcia_get_first_tuple(link, &tuple) == CS_SUCCESS) && (pcmcia_get_tuple_data(link, &tuple) == CS_SUCCESS)) info->manf_id = le16_to_cpu(tuple.TupleData[0]); - /* Configure card */ - link->state |= DEV_CONFIG; - tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); while (1) { - if (pcmcia_get_tuple_data(handle, &tuple) != 0 || - pcmcia_parse_tuple(handle, &tuple, &parse) != 0) + if (pcmcia_get_tuple_data(link, &tuple) != 0 || + pcmcia_parse_tuple(link, &tuple, &parse) != 0) goto next_entry; link->conf.ConfigIndex = parse.cftable_entry.index; link->io.BasePort1 = parse.cftable_entry.io.win[0].base; link->io.NumPorts1 = parse.cftable_entry.io.win[0].len; if (link->io.BasePort1 != 0) { - i = pcmcia_request_io(handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) break; } next_entry: - CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple)); + CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple)); } - CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq)); - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); if ((info->manf_id == MANFID_MACNICA) || (info->manf_id == MANFID_PIONEER) || (info->manf_id == 0x0098)) { /* set ATAcmd */ @@ -275,82 +259,54 @@ static void qlogic_config(dev_link_t * link) if (!host) { printk(KERN_INFO "%s: no SCSI devices found\n", qlogic_name); - goto out; + goto cs_failed; } sprintf(info->node.dev_name, "scsi%d", host->host_no); - link->dev = &info->node; + link->dev_node = &info->node; info->host = host; -out: - link->state &= ~DEV_CONFIG_PENDING; - return; + return 0; cs_failed: - cs_error(link->handle, last_fn, last_ret); - link->dev = NULL; - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - link->state &= ~DEV_CONFIG; - return; + cs_error(link, last_fn, last_ret); + pcmcia_disable_device(link); + return -ENODEV; } /* qlogic_config */ /*====================================================================*/ -static void qlogic_release(dev_link_t *link) +static void qlogic_release(struct pcmcia_device *link) { scsi_info_t *info = link->priv; DEBUG(0, "qlogic_release(0x%p)\n", link); scsi_remove_host(info->host); - link->dev = NULL; free_irq(link->irq.AssignedIRQ, info->host); - - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); + pcmcia_disable_device(link); scsi_host_put(info->host); - - link->state &= ~DEV_CONFIG; } /*====================================================================*/ -static int qlogic_suspend(struct pcmcia_device *dev) +static int qlogic_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(dev); + scsi_info_t *info = link->priv; - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) - pcmcia_release_configuration(link->handle); - - return 0; -} - -static int qlogic_resume(struct pcmcia_device *dev) -{ - dev_link_t *link = dev_to_instance(dev); - - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - scsi_info_t *info = link->priv; - - pcmcia_request_configuration(link->handle, &link->conf); - if ((info->manf_id == MANFID_MACNICA) || - (info->manf_id == MANFID_PIONEER) || - (info->manf_id == 0x0098)) { - outb(0x80, link->io.BasePort1 + 0xd); - outb(0x24, link->io.BasePort1 + 0x9); - outb(0x04, link->io.BasePort1 + 0xd); - } - /* Ugggglllyyyy!!! */ - qlogicfas408_bus_reset(NULL); + pcmcia_request_configuration(link, &link->conf); + if ((info->manf_id == MANFID_MACNICA) || + (info->manf_id == MANFID_PIONEER) || + (info->manf_id == 0x0098)) { + outb(0x80, link->io.BasePort1 + 0xd); + outb(0x24, link->io.BasePort1 + 0x9); + outb(0x04, link->io.BasePort1 + 0xd); } + /* Ugggglllyyyy!!! */ + qlogicfas408_bus_reset(NULL); return 0; } @@ -382,10 +338,9 @@ static struct pcmcia_driver qlogic_cs_driver = { .drv = { .name = "qlogic_cs", }, - .probe = qlogic_attach, + .probe = qlogic_probe, .remove = qlogic_detach, .id_table = qlogic_ids, - .suspend = qlogic_suspend, .resume = qlogic_resume, }; diff --git a/drivers/scsi/pcmcia/sym53c500_cs.c b/drivers/scsi/pcmcia/sym53c500_cs.c index 3a4dd6f5b81f..9f59827707f0 100644 --- a/drivers/scsi/pcmcia/sym53c500_cs.c +++ b/drivers/scsi/pcmcia/sym53c500_cs.c @@ -202,7 +202,7 @@ static char *version = /* ================================================================== */ struct scsi_info_t { - dev_link_t link; + struct pcmcia_device *p_dev; dev_node_t node; struct Scsi_Host *host; unsigned short manf_id; @@ -527,7 +527,7 @@ idle_out: } static void -SYM53C500_release(dev_link_t *link) +SYM53C500_release(struct pcmcia_device *link) { struct scsi_info_t *info = link->priv; struct Scsi_Host *shost = info->host; @@ -550,13 +550,7 @@ SYM53C500_release(dev_link_t *link) if (shost->io_port && shost->n_io_port) release_region(shost->io_port, shost->n_io_port); - link->dev = NULL; - - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - pcmcia_release_irq(link->handle, &link->irq); - - link->state &= ~DEV_CONFIG; + pcmcia_disable_device(link); scsi_host_put(shost); } /* SYM53C500_release */ @@ -713,10 +707,9 @@ static struct scsi_host_template sym53c500_driver_template = { #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) -static void -SYM53C500_config(dev_link_t *link) +static int +SYM53C500_config(struct pcmcia_device *link) { - client_handle_t handle = link->handle; struct scsi_info_t *info = link->priv; tuple_t tuple; cisparse_t parse; @@ -733,40 +726,37 @@ SYM53C500_config(dev_link_t *link) tuple.TupleDataMax = 64; tuple.TupleOffset = 0; tuple.DesiredTuple = CISTPL_CONFIG; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; tuple.DesiredTuple = CISTPL_MANFID; - if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) && - (pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS)) + if ((pcmcia_get_first_tuple(link, &tuple) == CS_SUCCESS) && + (pcmcia_get_tuple_data(link, &tuple) == CS_SUCCESS)) info->manf_id = le16_to_cpu(tuple.TupleData[0]); - /* Configure card */ - link->state |= DEV_CONFIG; - tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); while (1) { - if (pcmcia_get_tuple_data(handle, &tuple) != 0 || - pcmcia_parse_tuple(handle, &tuple, &parse) != 0) + if (pcmcia_get_tuple_data(link, &tuple) != 0 || + pcmcia_parse_tuple(link, &tuple, &parse) != 0) goto next_entry; link->conf.ConfigIndex = parse.cftable_entry.index; link->io.BasePort1 = parse.cftable_entry.io.win[0].base; link->io.NumPorts1 = parse.cftable_entry.io.win[0].len; if (link->io.BasePort1 != 0) { - i = pcmcia_request_io(handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) break; } next_entry: - CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple)); + CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple)); } - CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq)); - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf)); + CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); /* * That's the trouble with copying liberally from another driver. @@ -835,7 +825,7 @@ next_entry: data->fast_pio = USE_FAST_PIO; sprintf(info->node.dev_name, "scsi%d", host->host_no); - link->dev = &info->node; + link->dev_node = &info->node; info->host = host; if (scsi_add_host(host, NULL)) @@ -843,7 +833,7 @@ next_entry: scsi_scan_host(host); - goto out; /* SUCCESS */ + return 0; err_free_irq: free_irq(irq_level, host); @@ -852,74 +842,50 @@ err_free_scsi: err_release: release_region(port_base, 0x10); printk(KERN_INFO "sym53c500_cs: no SCSI devices found\n"); - -out: - link->state &= ~DEV_CONFIG_PENDING; - return; + return -ENODEV; cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); SYM53C500_release(link); - return; + return -ENODEV; } /* SYM53C500_config */ -static int sym53c500_suspend(struct pcmcia_device *dev) +static int sym53c500_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(dev); - - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) - pcmcia_release_configuration(link->handle); - - return 0; -} - -static int sym53c500_resume(struct pcmcia_device *dev) -{ - dev_link_t *link = dev_to_instance(dev); struct scsi_info_t *info = link->priv; - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) { - pcmcia_request_configuration(link->handle, &link->conf); - - /* See earlier comment about manufacturer IDs. */ - if ((info->manf_id == MANFID_MACNICA) || - (info->manf_id == MANFID_PIONEER) || - (info->manf_id == 0x0098)) { - outb(0x80, link->io.BasePort1 + 0xd); - outb(0x24, link->io.BasePort1 + 0x9); - outb(0x04, link->io.BasePort1 + 0xd); - } - /* - * If things don't work after a "resume", - * this is a good place to start looking. - */ - SYM53C500_int_host_reset(link->io.BasePort1); + /* See earlier comment about manufacturer IDs. */ + if ((info->manf_id == MANFID_MACNICA) || + (info->manf_id == MANFID_PIONEER) || + (info->manf_id == 0x0098)) { + outb(0x80, link->io.BasePort1 + 0xd); + outb(0x24, link->io.BasePort1 + 0x9); + outb(0x04, link->io.BasePort1 + 0xd); } + /* + * If things don't work after a "resume", + * this is a good place to start looking. + */ + SYM53C500_int_host_reset(link->io.BasePort1); return 0; } static void -SYM53C500_detach(struct pcmcia_device *p_dev) +SYM53C500_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); - DEBUG(0, "SYM53C500_detach(0x%p)\n", link); - if (link->state & DEV_CONFIG) - SYM53C500_release(link); + SYM53C500_release(link); kfree(link->priv); link->priv = NULL; } /* SYM53C500_detach */ static int -SYM53C500_attach(struct pcmcia_device *p_dev) +SYM53C500_probe(struct pcmcia_device *link) { struct scsi_info_t *info; - dev_link_t *link; DEBUG(0, "SYM53C500_attach()\n"); @@ -928,7 +894,7 @@ SYM53C500_attach(struct pcmcia_device *p_dev) if (!info) return -ENOMEM; memset(info, 0, sizeof(*info)); - link = &info->link; + info->p_dev = link; link->priv = info; link->io.NumPorts1 = 16; link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; @@ -936,17 +902,10 @@ SYM53C500_attach(struct pcmcia_device *p_dev) link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; link->irq.IRQInfo1 = IRQ_LEVEL_ID; link->conf.Attributes = CONF_ENABLE_IRQ; - link->conf.Vcc = 50; link->conf.IntType = INT_MEMORY_AND_IO; link->conf.Present = PRESENT_OPTION; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - SYM53C500_config(link); - - return 0; + return SYM53C500_config(link); } /* SYM53C500_attach */ MODULE_AUTHOR("Bob Tracy "); @@ -966,10 +925,9 @@ static struct pcmcia_driver sym53c500_cs_driver = { .drv = { .name = "sym53c500_cs", }, - .probe = SYM53C500_attach, + .probe = SYM53C500_probe, .remove = SYM53C500_detach, .id_table = sym53c500_ids, - .suspend = sym53c500_suspend, .resume = sym53c500_resume, }; diff --git a/drivers/scsi/pdc_adma.c b/drivers/scsi/pdc_adma.c index 3c85c4b66e19..b9a3c566f833 100644 --- a/drivers/scsi/pdc_adma.c +++ b/drivers/scsi/pdc_adma.c @@ -46,7 +46,7 @@ #include #define DRV_NAME "pdc_adma" -#define DRV_VERSION "0.03" +#define DRV_VERSION "0.04" /* macro to calculate base address for ATA regs */ #define ADMA_ATA_REGS(base,port_no) ((base) + ((port_no) * 0x40)) @@ -143,7 +143,6 @@ static struct scsi_host_template adma_ata_sht = { .name = DRV_NAME, .ioctl = ata_scsi_ioctl, .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, @@ -456,7 +455,7 @@ static inline unsigned int adma_intr_pkt(struct ata_host_set *host_set) continue; handled = 1; adma_enter_reg_mode(ap); - if (ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR)) + if (ap->flags & (ATA_FLAG_DISABLED | ATA_FLAG_NOINTR)) continue; pp = ap->private_data; if (!pp || pp->state != adma_state_pkt) @@ -481,7 +480,7 @@ static inline unsigned int adma_intr_mmio(struct ata_host_set *host_set) for (port_no = 0; port_no < host_set->n_ports; ++port_no) { struct ata_port *ap; ap = host_set->ports[port_no]; - if (ap && (!(ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR)))) { + if (ap && (!(ap->flags & (ATA_FLAG_DISABLED | ATA_FLAG_NOINTR)))) { struct ata_queued_cmd *qc; struct adma_port_priv *pp = ap->private_data; if (!pp || pp->state != adma_state_mmio) diff --git a/drivers/scsi/sata_mv.c b/drivers/scsi/sata_mv.c index fa901fd65085..4aabc759fcec 100644 --- a/drivers/scsi/sata_mv.c +++ b/drivers/scsi/sata_mv.c @@ -378,7 +378,6 @@ static struct scsi_host_template mv_sht = { .name = DRV_NAME, .ioctl = ata_scsi_ioctl, .queuecommand = ata_scsi_queuecmd, - .eh_strategy_handler = ata_scsi_error, .can_queue = MV_USE_Q_DEPTH, .this_id = ATA_SHT_THIS_ID, .sg_tablesize = MV_MAX_SG_CT / 2, @@ -1397,7 +1396,7 @@ static void mv_host_intr(struct ata_host_set *host_set, u32 relevant, } } - if (ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR)) + if (ap->flags & (ATA_FLAG_DISABLED | ATA_FLAG_NOINTR)) continue; err_mask = ac_err_mask(ata_status); @@ -1991,7 +1990,7 @@ comreset_retry: tf.nsect = readb((void __iomem *) ap->ioaddr.nsect_addr); dev->class = ata_dev_classify(&tf); - if (!ata_dev_present(dev)) { + if (!ata_dev_enabled(dev)) { VPRINTK("Port disabled post-sig: No device present.\n"); ata_port_disable(ap); } diff --git a/drivers/scsi/sata_nv.c b/drivers/scsi/sata_nv.c index f77bf183dfab..3a70875be8ba 100644 --- a/drivers/scsi/sata_nv.c +++ b/drivers/scsi/sata_nv.c @@ -44,7 +44,7 @@ #include #define DRV_NAME "sata_nv" -#define DRV_VERSION "0.8" +#define DRV_VERSION "0.9" enum { NV_PORTS = 2, @@ -201,7 +201,6 @@ static struct scsi_host_template nv_sht = { .name = DRV_NAME, .ioctl = ata_scsi_ioctl, .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, @@ -280,7 +279,7 @@ static irqreturn_t nv_interrupt (int irq, void *dev_instance, ap = host_set->ports[i]; if (ap && - !(ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR))) { + !(ap->flags & (ATA_FLAG_DISABLED | ATA_FLAG_NOINTR))) { struct ata_queued_cmd *qc; qc = ata_qc_from_tag(ap, ap->active_tag); diff --git a/drivers/scsi/sata_promise.c b/drivers/scsi/sata_promise.c index cc928c68a479..ddbc0c6dd9fe 100644 --- a/drivers/scsi/sata_promise.c +++ b/drivers/scsi/sata_promise.c @@ -111,7 +111,6 @@ static struct scsi_host_template pdc_ata_sht = { .name = DRV_NAME, .ioctl = ata_scsi_ioctl, .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, @@ -534,7 +533,7 @@ static irqreturn_t pdc_interrupt (int irq, void *dev_instance, struct pt_regs *r ap = host_set->ports[i]; tmp = mask & (1 << (i + 1)); if (tmp && ap && - !(ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR))) { + !(ap->flags & (ATA_FLAG_DISABLED | ATA_FLAG_NOINTR))) { struct ata_queued_cmd *qc; qc = ata_qc_from_tag(ap, ap->active_tag); @@ -677,10 +676,6 @@ static int pdc_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *e if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "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; diff --git a/drivers/scsi/sata_qstor.c b/drivers/scsi/sata_qstor.c index 9ffe1ef0d205..259c2dec4e21 100644 --- a/drivers/scsi/sata_qstor.c +++ b/drivers/scsi/sata_qstor.c @@ -41,7 +41,7 @@ #include #define DRV_NAME "sata_qstor" -#define DRV_VERSION "0.05" +#define DRV_VERSION "0.06" enum { QS_PORTS = 4, @@ -132,7 +132,6 @@ static struct scsi_host_template qs_ata_sht = { .name = DRV_NAME, .ioctl = ata_scsi_ioctl, .queuecommand = ata_scsi_queuecmd, - .eh_strategy_handler = ata_scsi_error, .can_queue = ATA_DEF_QUEUE, .this_id = ATA_SHT_THIS_ID, .sg_tablesize = QS_MAX_PRD, @@ -396,7 +395,7 @@ static inline unsigned int qs_intr_pkt(struct ata_host_set *host_set) sff1, sff0, port_no, sHST, sDST); handled = 1; if (ap && !(ap->flags & - (ATA_FLAG_PORT_DISABLED|ATA_FLAG_NOINTR))) { + (ATA_FLAG_DISABLED|ATA_FLAG_NOINTR))) { struct ata_queued_cmd *qc; struct qs_port_priv *pp = ap->private_data; if (!pp || pp->state != qs_state_pkt) @@ -429,7 +428,7 @@ static inline unsigned int qs_intr_mmio(struct ata_host_set *host_set) struct ata_port *ap; ap = host_set->ports[port_no]; if (ap && - !(ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR))) { + !(ap->flags & (ATA_FLAG_DISABLED | ATA_FLAG_NOINTR))) { struct ata_queued_cmd *qc; struct qs_port_priv *pp = ap->private_data; if (!pp || pp->state != qs_state_mmio) diff --git a/drivers/scsi/sata_sil.c b/drivers/scsi/sata_sil.c index 18c296c56899..c9333577330e 100644 --- a/drivers/scsi/sata_sil.c +++ b/drivers/scsi/sata_sil.c @@ -46,7 +46,7 @@ #include #define DRV_NAME "sata_sil" -#define DRV_VERSION "0.9" +#define DRV_VERSION "1.0" enum { /* @@ -146,7 +146,6 @@ static struct scsi_host_template sil_sht = { .name = DRV_NAME, .ioctl = ata_scsi_ioctl, .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, @@ -264,7 +263,7 @@ static void sil_post_set_mode (struct ata_port *ap) for (i = 0; i < 2; i++) { dev = &ap->device[i]; - if (!ata_dev_present(dev)) + if (!ata_dev_enabled(dev)) dev_mode[i] = 0; /* PIO0/1/2 */ else if (dev->flags & ATA_DFLAG_PIO) dev_mode[i] = 1; /* PIO3/4 */ @@ -391,10 +390,6 @@ static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "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; diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c index 068c98a4111b..e9fd869140c5 100644 --- a/drivers/scsi/sata_sil24.c +++ b/drivers/scsi/sata_sil24.c @@ -31,7 +31,7 @@ #include #define DRV_NAME "sata_sil24" -#define DRV_VERSION "0.23" +#define DRV_VERSION "0.24" /* * Port request block (PRB) 32 bytes @@ -86,6 +86,13 @@ enum { /* HOST_SLOT_STAT bits */ HOST_SSTAT_ATTN = (1 << 31), + /* HOST_CTRL bits */ + HOST_CTRL_M66EN = (1 << 16), /* M66EN PCI bus signal */ + HOST_CTRL_TRDY = (1 << 17), /* latched PCI TRDY */ + HOST_CTRL_STOP = (1 << 18), /* latched PCI STOP */ + HOST_CTRL_DEVSEL = (1 << 19), /* latched PCI DEVSEL */ + HOST_CTRL_REQ64 = (1 << 20), /* latched PCI REQ64 */ + /* * Port registers * (8192 bytes @ +0x0000, +0x2000, +0x4000 and +0x6000 @ BAR2) @@ -142,8 +149,12 @@ enum { PORT_IRQ_PWR_CHG = (1 << 3), /* power management change */ PORT_IRQ_PHYRDY_CHG = (1 << 4), /* PHY ready change */ PORT_IRQ_COMWAKE = (1 << 5), /* COMWAKE received */ - PORT_IRQ_UNK_FIS = (1 << 6), /* Unknown FIS received */ - PORT_IRQ_SDB_FIS = (1 << 11), /* SDB FIS received */ + PORT_IRQ_UNK_FIS = (1 << 6), /* unknown FIS received */ + PORT_IRQ_DEV_XCHG = (1 << 7), /* device exchanged */ + PORT_IRQ_8B10B = (1 << 8), /* 8b/10b decode error threshold */ + PORT_IRQ_CRC = (1 << 9), /* CRC error threshold */ + PORT_IRQ_HANDSHAKE = (1 << 10), /* handshake error threshold */ + PORT_IRQ_SDB_NOTIFY = (1 << 11), /* SDB notify received */ /* bits[27:16] are unmasked (raw) */ PORT_IRQ_RAW_SHIFT = 16, @@ -174,7 +185,7 @@ enum { PORT_CERR_CMD_PCIPERR = 27, /* ctrl[15:13] 110 - PCI parity err while fetching PRB */ PORT_CERR_XFR_UNDEF = 32, /* PSD ecode 00 - undefined */ PORT_CERR_XFR_TGTABRT = 33, /* PSD ecode 01 - target abort */ - PORT_CERR_XFR_MSGABRT = 34, /* PSD ecode 10 - master abort */ + PORT_CERR_XFR_MSTABRT = 34, /* PSD ecode 10 - master abort */ PORT_CERR_XFR_PCIPERR = 35, /* PSD ecode 11 - PCI prity err during transfer */ PORT_CERR_SENDSERVICE = 36, /* FIS received while sending service */ @@ -207,6 +218,11 @@ enum { BID_SIL3132 = 1, BID_SIL3131 = 2, + /* host flags */ + SIL24_COMMON_FLAGS = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA, + SIL24_FLAG_PCIX_IRQ_WOC = (1 << 24), /* IRQ loss errata on PCI-X */ + IRQ_STAT_4PORTS = 0xf, }; @@ -281,7 +297,6 @@ static struct scsi_host_template sil24_sht = { .name = DRV_NAME, .ioctl = ata_scsi_ioctl, .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, @@ -334,9 +349,8 @@ static struct ata_port_info sil24_port_info[] = { /* sil_3124 */ { .sht = &sil24_sht, - .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | - SIL24_NPORTS2FLAG(4), + .host_flags = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(4) | + SIL24_FLAG_PCIX_IRQ_WOC, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ .udma_mask = 0x3f, /* udma0-5 */ @@ -345,9 +359,7 @@ static struct ata_port_info sil24_port_info[] = { /* sil_3132 */ { .sht = &sil24_sht, - .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | - SIL24_NPORTS2FLAG(2), + .host_flags = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(2), .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ .udma_mask = 0x3f, /* udma0-5 */ @@ -356,9 +368,7 @@ static struct ata_port_info sil24_port_info[] = { /* sil_3131/sil_3531 */ { .sht = &sil24_sht, - .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | - SIL24_NPORTS2FLAG(1), + .host_flags = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(1), .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ .udma_mask = 0x3f, /* udma0-5 */ @@ -427,15 +437,30 @@ static void sil24_tf_read(struct ata_port *ap, struct ata_taskfile *tf) *tf = pp->tf; } -static int sil24_softreset(struct ata_port *ap, int verbose, - unsigned int *class) +static int sil24_init_port(struct ata_port *ap) +{ + void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr; + u32 tmp; + + writel(PORT_CS_INIT, port + PORT_CTRL_STAT); + ata_wait_register(port + PORT_CTRL_STAT, + PORT_CS_INIT, PORT_CS_INIT, 10, 100); + tmp = ata_wait_register(port + PORT_CTRL_STAT, + PORT_CS_RDY, 0, 10, 100); + + if ((tmp & (PORT_CS_INIT | PORT_CS_RDY)) != PORT_CS_RDY) + return -EIO; + return 0; +} + +static int sil24_softreset(struct ata_port *ap, unsigned int *class) { void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr; struct sil24_port_priv *pp = ap->private_data; struct sil24_prb *prb = &pp->cmd_block[0].ata.prb; dma_addr_t paddr = pp->cmd_block_dma; - unsigned long timeout = jiffies + ATA_TMOUT_BOOT * HZ; - u32 irq_enable, irq_stat; + u32 mask, irq_enable, irq_stat; + const char *reason; DPRINTK("ENTER\n"); @@ -449,34 +474,35 @@ static int sil24_softreset(struct ata_port *ap, int verbose, irq_enable = readl(port + PORT_IRQ_ENABLE_SET); writel(irq_enable, port + PORT_IRQ_ENABLE_CLR); - /* - * XXX: Not sure whether the following sleep is needed or not. - * The original driver had it. So.... - */ - msleep(10); + /* put the port into known state */ + if (sil24_init_port(ap)) { + reason ="port not ready"; + goto err; + } - prb->ctrl = PRB_CTRL_SRST; + /* do SRST */ + prb->ctrl = cpu_to_le16(PRB_CTRL_SRST); prb->fis[1] = 0; /* no PM yet */ writel((u32)paddr, port + PORT_CMD_ACTIVATE); + writel((u64)paddr >> 32, port + PORT_CMD_ACTIVATE + 4); - do { - irq_stat = readl(port + PORT_IRQ_STAT); - writel(irq_stat, port + PORT_IRQ_STAT); /* clear irq */ + mask = (PORT_IRQ_COMPLETE | PORT_IRQ_ERROR) << PORT_IRQ_RAW_SHIFT; + irq_stat = ata_wait_register(port + PORT_IRQ_STAT, mask, 0x0, + 100, ATA_TMOUT_BOOT / HZ * 1000); - irq_stat >>= PORT_IRQ_RAW_SHIFT; - if (irq_stat & (PORT_IRQ_COMPLETE | PORT_IRQ_ERROR)) - break; - - msleep(100); - } while (time_before(jiffies, timeout)); + writel(irq_stat, port + PORT_IRQ_STAT); /* clear IRQs */ + irq_stat >>= PORT_IRQ_RAW_SHIFT; /* restore IRQs */ writel(irq_enable, port + PORT_IRQ_ENABLE_SET); if (!(irq_stat & PORT_IRQ_COMPLETE)) { - DPRINTK("EXIT, srst failed\n"); - return -EIO; + if (irq_stat & PORT_IRQ_ERROR) + reason = "SRST command error"; + else + reason = "timeout"; + goto err; } sil24_update_tf(ap); @@ -488,15 +514,55 @@ static int sil24_softreset(struct ata_port *ap, int verbose, out: DPRINTK("EXIT, class=%u\n", *class); return 0; + + err: + printk(KERN_ERR "ata%u: softreset failed (%s)\n", ap->id, reason); + return -EIO; } -static int sil24_hardreset(struct ata_port *ap, int verbose, - unsigned int *class) +static int sil24_hardreset(struct ata_port *ap, unsigned int *class) { - unsigned int dummy_class; + void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr; + const char *reason; + int tout_msec; + u32 tmp; - /* sil24 doesn't report device signature after hard reset */ - return sata_std_hardreset(ap, verbose, &dummy_class); + /* sil24 does the right thing(tm) without any protection */ + ata_set_sata_spd(ap); + + tout_msec = 100; + if (sata_dev_present(ap)) + tout_msec = 5000; + + writel(PORT_CS_DEV_RST, port + PORT_CTRL_STAT); + tmp = ata_wait_register(port + PORT_CTRL_STAT, + PORT_CS_DEV_RST, PORT_CS_DEV_RST, 10, tout_msec); + + /* SStatus oscillates between zero and valid status for short + * duration after DEV_RST, give it time to settle. + */ + msleep(100); + + if (tmp & PORT_CS_DEV_RST) { + if (!sata_dev_present(ap)) + return 0; + reason = "link not ready"; + goto err; + } + + if (ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT)) { + reason = "device not ready"; + goto err; + } + + /* sil24 doesn't report device class code after hardreset, + * leave *class alone. + */ + return 0; + + err: + printk(KERN_ERR "ata%u: hardreset failed (%s)\n", ap->id, reason); + return -EIO; } static int sil24_probe_reset(struct ata_port *ap, unsigned int *classes) @@ -532,6 +598,7 @@ static void sil24_qc_prep(struct ata_queued_cmd *qc) union sil24_cmd_block *cb = pp->cmd_block + qc->tag; struct sil24_prb *prb; struct sil24_sge *sge; + u16 ctrl = 0; switch (qc->tf.protocol) { case ATA_PROT_PIO: @@ -539,7 +606,6 @@ static void sil24_qc_prep(struct ata_queued_cmd *qc) case ATA_PROT_NODATA: prb = &cb->ata.prb; sge = cb->ata.sge; - prb->ctrl = 0; break; case ATA_PROT_ATAPI: @@ -552,12 +618,10 @@ static void sil24_qc_prep(struct ata_queued_cmd *qc) if (qc->tf.protocol != ATA_PROT_ATAPI_NODATA) { if (qc->tf.flags & ATA_TFLAG_WRITE) - prb->ctrl = PRB_CTRL_PACKET_WRITE; + ctrl = PRB_CTRL_PACKET_WRITE; else - prb->ctrl = PRB_CTRL_PACKET_READ; - } else - prb->ctrl = 0; - + ctrl = PRB_CTRL_PACKET_READ; + } break; default: @@ -566,6 +630,7 @@ static void sil24_qc_prep(struct ata_queued_cmd *qc) BUG(); } + prb->ctrl = cpu_to_le16(ctrl); ata_tf_to_fis(&qc->tf, prb->fis, 0); if (qc->flags & ATA_QCFLAG_DMAMAP) @@ -580,6 +645,8 @@ static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc) dma_addr_t paddr = pp->cmd_block_dma + qc->tag * sizeof(*pp->cmd_block); writel((u32)paddr, port + PORT_CMD_ACTIVATE); + writel((u64)paddr >> 32, port + PORT_CMD_ACTIVATE + 4); + return 0; } @@ -728,6 +795,10 @@ static inline void sil24_host_intr(struct ata_port *ap) slot_stat = readl(port + PORT_SLOT_STAT); if (!(slot_stat & HOST_SSTAT_ATTN)) { struct sil24_port_priv *pp = ap->private_data; + + if (ap->flags & SIL24_FLAG_PCIX_IRQ_WOC) + writel(PORT_IRQ_COMPLETE, port + PORT_IRQ_STAT); + /* * !HOST_SSAT_ATTN guarantees successful completion, * so reading back tf registers is unnecessary for @@ -770,7 +841,7 @@ static irqreturn_t sil24_interrupt(int irq, void *dev_instance, struct pt_regs * for (i = 0; i < host_set->n_ports; i++) if (status & (1 << i)) { struct ata_port *ap = host_set->ports[i]; - if (ap && !(ap->flags & ATA_FLAG_PORT_DISABLED)) { + if (ap && !(ap->flags & ATA_FLAG_DISABLED)) { sil24_host_intr(host_set->ports[i]); handled++; } else @@ -859,6 +930,7 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) void __iomem *host_base = NULL; void __iomem *port_base = NULL; int i, rc; + u32 tmp; if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); @@ -911,35 +983,51 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) /* * Configure the device */ - /* - * FIXME: This device is certainly 64-bit capable. We just - * don't know how to use it. After fixing 32bit activation in - * this function, enable 64bit masks here. - */ - rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); - if (rc) { - dev_printk(KERN_ERR, &pdev->dev, - "32-bit DMA enable failed\n"); - goto out_free; - } - rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); - if (rc) { - dev_printk(KERN_ERR, &pdev->dev, - "32-bit consistent DMA enable failed\n"); - goto out_free; + if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK)) { + rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); + if (rc) { + rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, + "64-bit DMA enable failed\n"); + goto out_free; + } + } + } else { + rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, + "32-bit DMA enable failed\n"); + goto out_free; + } + rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, + "32-bit consistent DMA enable failed\n"); + goto out_free; + } } /* GPIO off */ writel(0, host_base + HOST_FLASH_CMD); - /* Mask interrupts during initialization */ + /* Apply workaround for completion IRQ loss on PCI-X errata */ + if (probe_ent->host_flags & SIL24_FLAG_PCIX_IRQ_WOC) { + tmp = readl(host_base + HOST_CTRL); + if (tmp & (HOST_CTRL_TRDY | HOST_CTRL_STOP | HOST_CTRL_DEVSEL)) + dev_printk(KERN_INFO, &pdev->dev, + "Applying completion IRQ loss on PCI-X " + "errata fix\n"); + else + probe_ent->host_flags &= ~SIL24_FLAG_PCIX_IRQ_WOC; + } + + /* clear global reset & mask interrupts during initialization */ writel(0, host_base + HOST_CTRL); for (i = 0; i < probe_ent->n_ports; i++) { void __iomem *port = port_base + i * PORT_REGS_SIZE; unsigned long portu = (unsigned long)port; - u32 tmp; - int cnt; probe_ent->port[i].cmd_addr = portu + PORT_PRB; probe_ent->port[i].scr_addr = portu + PORT_SCONTROL; @@ -953,18 +1041,20 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) tmp = readl(port + PORT_CTRL_STAT); if (tmp & PORT_CS_PORT_RST) { writel(PORT_CS_PORT_RST, port + PORT_CTRL_CLR); - readl(port + PORT_CTRL_STAT); /* sync */ - for (cnt = 0; cnt < 10; cnt++) { - msleep(10); - tmp = readl(port + PORT_CTRL_STAT); - if (!(tmp & PORT_CS_PORT_RST)) - break; - } + tmp = ata_wait_register(port + PORT_CTRL_STAT, + PORT_CS_PORT_RST, + PORT_CS_PORT_RST, 10, 100); if (tmp & PORT_CS_PORT_RST) dev_printk(KERN_ERR, &pdev->dev, "failed to clear port RST\n"); } + /* Configure IRQ WoC */ + if (probe_ent->host_flags & SIL24_FLAG_PCIX_IRQ_WOC) + writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_STAT); + else + writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR); + /* Zero error counters. */ writel(0x8000, port + PORT_DECODE_ERR_THRESH); writel(0x8000, port + PORT_CRC_ERR_THRESH); @@ -973,14 +1063,13 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) writel(0x0000, port + PORT_CRC_ERR_CNT); writel(0x0000, port + PORT_HSHK_ERR_CNT); - /* FIXME: 32bit activation? */ - writel(0, port + PORT_ACTIVATE_UPPER_ADDR); - writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_STAT); + /* Always use 64bit activation */ + writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_CLR); /* Configure interrupts */ writel(0xffff, port + PORT_IRQ_ENABLE_CLR); - writel(PORT_IRQ_COMPLETE | PORT_IRQ_ERROR | PORT_IRQ_SDB_FIS, - port + PORT_IRQ_ENABLE_SET); + writel(PORT_IRQ_COMPLETE | PORT_IRQ_ERROR | + PORT_IRQ_SDB_NOTIFY, port + PORT_IRQ_ENABLE_SET); /* Clear interrupts */ writel(0x0fff0fff, port + PORT_IRQ_STAT); @@ -988,11 +1077,6 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) /* Clear port multiplier enable and resume bits */ writel(PORT_CS_PM_EN | PORT_CS_RESUME, port + PORT_CTRL_CLR); - - /* Reset itself */ - if (__sil24_reset_controller(port)) - dev_printk(KERN_ERR, &pdev->dev, - "failed to reset controller\n"); } /* Turn on interrupts */ diff --git a/drivers/scsi/sata_sis.c b/drivers/scsi/sata_sis.c index acc8439dea23..3097821688dc 100644 --- a/drivers/scsi/sata_sis.c +++ b/drivers/scsi/sata_sis.c @@ -43,7 +43,7 @@ #include #define DRV_NAME "sata_sis" -#define DRV_VERSION "0.5" +#define DRV_VERSION "0.6" enum { sis_180 = 0, @@ -87,7 +87,6 @@ static struct scsi_host_template sis_sht = { .name = DRV_NAME, .ioctl = ata_scsi_ioctl, .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, diff --git a/drivers/scsi/sata_svw.c b/drivers/scsi/sata_svw.c index 724f0ed6a52d..d5eb5375e265 100644 --- a/drivers/scsi/sata_svw.c +++ b/drivers/scsi/sata_svw.c @@ -54,7 +54,7 @@ #endif /* CONFIG_PPC_OF */ #define DRV_NAME "sata_svw" -#define DRV_VERSION "1.07" +#define DRV_VERSION "1.8" enum { /* Taskfile registers offsets */ @@ -257,7 +257,7 @@ static int k2_sata_proc_info(struct Scsi_Host *shost, char *page, char **start, int len, index; /* Find the ata_port */ - ap = (struct ata_port *) &shost->hostdata[0]; + ap = ata_shost_to_port(shost); if (ap == NULL) return 0; @@ -290,7 +290,6 @@ static struct scsi_host_template k2_sata_sht = { .name = DRV_NAME, .ioctl = ata_scsi_ioctl, .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, diff --git a/drivers/scsi/sata_sx4.c b/drivers/scsi/sata_sx4.c index ae70f60c7c0d..a669d0589889 100644 --- a/drivers/scsi/sata_sx4.c +++ b/drivers/scsi/sata_sx4.c @@ -46,7 +46,7 @@ #include "sata_promise.h" #define DRV_NAME "sata_sx4" -#define DRV_VERSION "0.8" +#define DRV_VERSION "0.9" enum { @@ -182,7 +182,6 @@ static struct scsi_host_template pdc_sata_sht = { .name = DRV_NAME, .ioctl = ata_scsi_ioctl, .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, @@ -834,7 +833,7 @@ static irqreturn_t pdc20621_interrupt (int irq, void *dev_instance, struct pt_re 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 | ATA_FLAG_NOINTR))) { + !(ap->flags & (ATA_FLAG_DISABLED | ATA_FLAG_NOINTR))) { struct ata_queued_cmd *qc; qc = ata_qc_from_tag(ap, ap->active_tag); @@ -1376,10 +1375,6 @@ static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id * if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "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; diff --git a/drivers/scsi/sata_uli.c b/drivers/scsi/sata_uli.c index 7ac5a5f5a905..15f81bfc30f0 100644 --- a/drivers/scsi/sata_uli.c +++ b/drivers/scsi/sata_uli.c @@ -37,7 +37,7 @@ #include #define DRV_NAME "sata_uli" -#define DRV_VERSION "0.5" +#define DRV_VERSION "0.6" enum { uli_5289 = 0, @@ -81,7 +81,6 @@ static struct scsi_host_template uli_sht = { .name = DRV_NAME, .ioctl = ata_scsi_ioctl, .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, diff --git a/drivers/scsi/sata_via.c b/drivers/scsi/sata_via.c index 791bf652ba63..17aefab5f42f 100644 --- a/drivers/scsi/sata_via.c +++ b/drivers/scsi/sata_via.c @@ -47,7 +47,7 @@ #include #define DRV_NAME "sata_via" -#define DRV_VERSION "1.1" +#define DRV_VERSION "1.2" enum board_ids_enum { vt6420, @@ -94,7 +94,6 @@ static struct scsi_host_template svia_sht = { .name = DRV_NAME, .ioctl = ata_scsi_ioctl, .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, diff --git a/drivers/scsi/sata_vsc.c b/drivers/scsi/sata_vsc.c index 836bbbb26ff2..9646c3932129 100644 --- a/drivers/scsi/sata_vsc.c +++ b/drivers/scsi/sata_vsc.c @@ -222,7 +222,7 @@ static irqreturn_t vsc_sata_interrupt (int irq, void *dev_instance, ap = host_set->ports[i]; if (ap && !(ap->flags & - (ATA_FLAG_PORT_DISABLED|ATA_FLAG_NOINTR))) { + (ATA_FLAG_DISABLED|ATA_FLAG_NOINTR))) { struct ata_queued_cmd *qc; qc = ata_qc_from_tag(ap, ap->active_tag); @@ -263,7 +263,6 @@ static struct scsi_host_template vsc_sata_sht = { .name = DRV_NAME, .ioctl = ata_scsi_ioctl, .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, diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 5f0fdfb2618c..1c75646f9689 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -1537,8 +1537,8 @@ int scsi_error_handler(void *data) * what we need to do to get it up and online again (if we can). * If we fail, we end up taking the thing offline. */ - if (shost->hostt->eh_strategy_handler) - shost->hostt->eh_strategy_handler(shost); + if (shost->transportt->eh_strategy_handler) + shost->transportt->eh_strategy_handler(shost); else scsi_unjam_host(shost); diff --git a/drivers/scsi/zalon.c b/drivers/scsi/zalon.c index b131432c677d..a6cfbb3b361c 100644 --- a/drivers/scsi/zalon.c +++ b/drivers/scsi/zalon.c @@ -88,7 +88,7 @@ zalon_probe(struct parisc_device *dev) struct gsc_irq gsc_irq; u32 zalon_vers; int error = -ENODEV; - void __iomem *zalon = ioremap(dev->hpa.start, 4096); + void __iomem *zalon = ioremap_nocache(dev->hpa.start, 4096); void __iomem *io_port = zalon + GSC_SCSI_ZALON_OFFSET; static int unit = 0; struct Scsi_Host *host; diff --git a/drivers/serial/8250_gsc.c b/drivers/serial/8250_gsc.c index 8b4947933d9b..913c71cc0569 100644 --- a/drivers/serial/8250_gsc.c +++ b/drivers/serial/8250_gsc.c @@ -52,13 +52,14 @@ serial_init_chip(struct parisc_device *dev) address += 0x800; } - memset(&port, 0, sizeof(struct uart_port)); - port.mapbase = address; - port.irq = dev->irq; - port.iotype = UPIO_MEM; - port.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF; - port.uartclk = LASI_BASE_BAUD * 16; - port.dev = &dev->dev; + memset(&port, 0, sizeof(port)); + port.iotype = UPIO_MEM; + port.uartclk = LASI_BASE_BAUD * 16; + port.mapbase = address; + port.membase = ioremap_nocache(address, 16); + port.irq = dev->irq; + port.flags = UPF_BOOT_AUTOCONF; + port.dev = &dev->dev; err = serial8250_register_port(&port); if (err < 0) { diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index fe0d8b8e91c8..7d22dc0478d3 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -63,6 +63,33 @@ config SERIAL_8250_CONSOLE If unsure, say N. +config SERIAL_8250_GSC + tristate + depends on SERIAL_8250 && GSC + default SERIAL_8250 + +config SERIAL_8250_PCI + tristate "8250/16550 PCI device support" if EMBEDDED + depends on SERIAL_8250 && PCI + default SERIAL_8250 + help + This builds standard PCI serial support. You may be able to + disable this feature if you only need legacy serial support. + Saves about 9K. + +config SERIAL_8250_PNP + tristate "8250/16550 PNP device support" if EMBEDDED + depends on SERIAL_8250 && PNP + default SERIAL_8250 + help + This builds standard PNP serial support. You may be able to + disable this feature if you only need legacy serial support. + +config SERIAL_8250_HP300 + tristate + depends on SERIAL_8250 && HP300 + default SERIAL_8250 + config SERIAL_8250_CS tristate "8250/16550 PCMCIA device support" depends on PCMCIA && SERIAL_8250 diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index d2b4c214876b..0a71bf68a03f 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -4,15 +4,13 @@ # $Id: Makefile,v 1.8 2002/07/21 21:32:30 rmk Exp $ # -serial-8250-y := -serial-8250-$(CONFIG_PNP) += 8250_pnp.o -serial-8250-$(CONFIG_GSC) += 8250_gsc.o -serial-8250-$(CONFIG_PCI) += 8250_pci.o -serial-8250-$(CONFIG_HP300) += 8250_hp300.o - obj-$(CONFIG_SERIAL_CORE) += serial_core.o obj-$(CONFIG_SERIAL_21285) += 21285.o -obj-$(CONFIG_SERIAL_8250) += 8250.o $(serial-8250-y) +obj-$(CONFIG_SERIAL_8250) += 8250.o +obj-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o +obj-$(CONFIG_SERIAL_8250_GSC) += 8250_gsc.o +obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o +obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o obj-$(CONFIG_SERIAL_8250_CONSOLE) += 8250_early.o diff --git a/drivers/serial/jsm/jsm.h b/drivers/serial/jsm/jsm.h index dfc1e86d3aa1..043f50b1d10c 100644 --- a/drivers/serial/jsm/jsm.h +++ b/drivers/serial/jsm/jsm.h @@ -20,7 +20,7 @@ * * Contact Information: * Scott H Kilau - * Wendy Xiong + * Wendy Xiong * ***********************************************************************/ diff --git a/drivers/serial/jsm/jsm_driver.c b/drivers/serial/jsm/jsm_driver.c index b1b66e71d281..b3e1f71be4da 100644 --- a/drivers/serial/jsm/jsm_driver.c +++ b/drivers/serial/jsm/jsm_driver.c @@ -20,7 +20,7 @@ * * Contact Information: * Scott H Kilau - * Wendy Xiong + * Wendy Xiong * * ***********************************************************************/ diff --git a/drivers/serial/jsm/jsm_neo.c b/drivers/serial/jsm/jsm_neo.c index 87e4e2cf8ce7..a5fc589d6ef5 100644 --- a/drivers/serial/jsm/jsm_neo.c +++ b/drivers/serial/jsm/jsm_neo.c @@ -20,7 +20,7 @@ * * Contact Information: * Scott H Kilau - * Wendy Xiong + * Wendy Xiong * ***********************************************************************/ #include /* For udelay */ diff --git a/drivers/serial/jsm/jsm_tty.c b/drivers/serial/jsm/jsm_tty.c index 4d48b625cd3d..7d823705193c 100644 --- a/drivers/serial/jsm/jsm_tty.c +++ b/drivers/serial/jsm/jsm_tty.c @@ -142,12 +142,14 @@ static void jsm_tty_send_xchar(struct uart_port *port, char ch) { unsigned long lock_flags; struct jsm_channel *channel = (struct jsm_channel *)port; + struct termios *termios; spin_lock_irqsave(&port->lock, lock_flags); - if (ch == port->info->tty->termios->c_cc[VSTART]) + termios = port->info->tty->termios; + if (ch == termios->c_cc[VSTART]) channel->ch_bd->bd_ops->send_start_character(channel); - if (ch == port->info->tty->termios->c_cc[VSTOP]) + if (ch == termios->c_cc[VSTOP]) channel->ch_bd->bd_ops->send_stop_character(channel); spin_unlock_irqrestore(&port->lock, lock_flags); } @@ -178,6 +180,7 @@ static int jsm_tty_open(struct uart_port *port) struct jsm_board *brd; int rc = 0; struct jsm_channel *channel = (struct jsm_channel *)port; + struct termios *termios; /* Get board pointer from our array of majors we have allocated */ brd = channel->ch_bd; @@ -239,12 +242,13 @@ static int jsm_tty_open(struct uart_port *port) channel->ch_cached_lsr = 0; channel->ch_stops_sent = 0; - channel->ch_c_cflag = port->info->tty->termios->c_cflag; - channel->ch_c_iflag = port->info->tty->termios->c_iflag; - channel->ch_c_oflag = port->info->tty->termios->c_oflag; - channel->ch_c_lflag = port->info->tty->termios->c_lflag; - channel->ch_startc = port->info->tty->termios->c_cc[VSTART]; - channel->ch_stopc = port->info->tty->termios->c_cc[VSTOP]; + termios = port->info->tty->termios; + channel->ch_c_cflag = termios->c_cflag; + channel->ch_c_iflag = termios->c_iflag; + channel->ch_c_oflag = termios->c_oflag; + channel->ch_c_lflag = termios->c_lflag; + channel->ch_startc = termios->c_cc[VSTART]; + channel->ch_stopc = termios->c_cc[VSTOP]; /* Tell UART to init itself */ brd->bd_ops->uart_init(channel); @@ -784,6 +788,7 @@ static void jsm_carrier(struct jsm_channel *ch) void jsm_check_queue_flow_control(struct jsm_channel *ch) { + struct board_ops *bd_ops = ch->ch_bd->bd_ops; int qleft = 0; /* Store how much space we have left in the queue */ @@ -809,7 +814,7 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch) /* HWFLOW */ if (ch->ch_c_cflag & CRTSCTS) { if(!(ch->ch_flags & CH_RECEIVER_OFF)) { - ch->ch_bd->bd_ops->disable_receiver(ch); + bd_ops->disable_receiver(ch); ch->ch_flags |= (CH_RECEIVER_OFF); jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "Internal queue hit hilevel mark (%d)! Turning off interrupts.\n", @@ -819,7 +824,7 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch) /* SWFLOW */ else if (ch->ch_c_iflag & IXOFF) { if (ch->ch_stops_sent <= MAX_STOPS_SENT) { - ch->ch_bd->bd_ops->send_stop_character(ch); + bd_ops->send_stop_character(ch); ch->ch_stops_sent++; jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "Sending stop char! Times sent: %x\n", ch->ch_stops_sent); @@ -846,7 +851,7 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch) /* HWFLOW */ if (ch->ch_c_cflag & CRTSCTS) { if (ch->ch_flags & CH_RECEIVER_OFF) { - ch->ch_bd->bd_ops->enable_receiver(ch); + bd_ops->enable_receiver(ch); ch->ch_flags &= ~(CH_RECEIVER_OFF); jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "Internal queue hit lowlevel mark (%d)! Turning on interrupts.\n", @@ -856,7 +861,7 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch) /* SWFLOW */ else if (ch->ch_c_iflag & IXOFF && ch->ch_stops_sent) { ch->ch_stops_sent = 0; - ch->ch_bd->bd_ops->send_start_character(ch); + bd_ops->send_start_character(ch); jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "Sending start char!\n"); } } diff --git a/drivers/serial/mux.c b/drivers/serial/mux.c index 868eaf4a1a68..64c0e89124c9 100644 --- a/drivers/serial/mux.c +++ b/drivers/serial/mux.c @@ -51,7 +51,7 @@ #define MUX_BREAK(status) ((status & 0xF000) == 0x2000) #define MUX_NR 256 -static unsigned int port_cnt = 0; +static unsigned int port_cnt __read_mostly; static struct uart_port mux_ports[MUX_NR]; static struct uart_driver mux_driver = { @@ -461,7 +461,7 @@ static int __init mux_probe(struct parisc_device *dev) port->iobase = 0; port->mapbase = dev->hpa.start + MUX_OFFSET + (i * MUX_LINE_OFFSET); - port->membase = ioremap(port->mapbase, MUX_LINE_OFFSET); + port->membase = ioremap_nocache(port->mapbase, MUX_LINE_OFFSET); port->iotype = UPIO_MEM; port->type = PORT_MUX; port->irq = NO_IRQ; diff --git a/drivers/serial/serial_cs.c b/drivers/serial/serial_cs.c index c30333694fde..2c70773543e0 100644 --- a/drivers/serial/serial_cs.c +++ b/drivers/serial/serial_cs.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -97,11 +98,13 @@ static const struct multi_id multi_id[] = { #define MULTI_COUNT (sizeof(multi_id)/sizeof(struct multi_id)) struct serial_info { - dev_link_t link; + struct pcmcia_device *p_dev; int ndev; int multi; int slave; int manfid; + int prodid; + int c950ctrl; dev_node_t node[4]; int line[4]; }; @@ -113,9 +116,36 @@ struct serial_cfg_mem { }; -static void serial_config(dev_link_t * link); +static int serial_config(struct pcmcia_device * link); +static void wakeup_card(struct serial_info *info) +{ + int ctrl = info->c950ctrl; + + if (info->manfid == MANFID_OXSEMI) { + outb(12, ctrl + 1); + } else if (info->manfid == MANFID_POSSIO && info->prodid == PRODID_POSSIO_GCC) { + /* request_region? oxsemi branch does no request_region too... */ + /* This sequence is needed to properly initialize MC45 attached to OXCF950. + * I tried decreasing these msleep()s, but it worked properly (survived + * 1000 stop/start operations) with these timeouts (or bigger). */ + outb(0xA, ctrl + 1); + msleep(100); + outb(0xE, ctrl + 1); + msleep(300); + outb(0xC, ctrl + 1); + msleep(100); + outb(0xE, ctrl + 1); + msleep(200); + outb(0xF, ctrl + 1); + msleep(100); + outb(0xE, ctrl + 1); + msleep(100); + outb(0xC, ctrl + 1); + } +} + /*====================================================================== After a card is removed, serial_remove() will unregister @@ -123,67 +153,45 @@ static void serial_config(dev_link_t * link); ======================================================================*/ -static void serial_remove(dev_link_t *link) +static void serial_remove(struct pcmcia_device *link) { struct serial_info *info = link->priv; int i; - link->state &= ~DEV_PRESENT; - DEBUG(0, "serial_release(0x%p)\n", link); /* * Recheck to see if the device is still configured. */ - if (info->link.state & DEV_CONFIG) { - for (i = 0; i < info->ndev; i++) - serial8250_unregister_port(info->line[i]); + for (i = 0; i < info->ndev; i++) + serial8250_unregister_port(info->line[i]); - info->link.dev = NULL; + info->p_dev->dev_node = NULL; - if (!info->slave) { - pcmcia_release_configuration(info->link.handle); - pcmcia_release_io(info->link.handle, &info->link.io); - pcmcia_release_irq(info->link.handle, &info->link.irq); - } - - info->link.state &= ~DEV_CONFIG; - } + if (!info->slave) + pcmcia_disable_device(link); } -static int serial_suspend(struct pcmcia_device *dev) +static int serial_suspend(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(dev); - link->state |= DEV_SUSPEND; + struct serial_info *info = link->priv; + int i; - if (link->state & DEV_CONFIG) { - struct serial_info *info = link->priv; - int i; - - for (i = 0; i < info->ndev; i++) - serial8250_suspend_port(info->line[i]); - - if (!info->slave) - pcmcia_release_configuration(link->handle); - } + for (i = 0; i < info->ndev; i++) + serial8250_suspend_port(info->line[i]); return 0; } -static int serial_resume(struct pcmcia_device *dev) +static int serial_resume(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(dev); - link->state &= ~DEV_SUSPEND; - - if (DEV_OK(link)) { + if (pcmcia_dev_present(link)) { struct serial_info *info = link->priv; int i; - if (!info->slave) - pcmcia_request_configuration(link->handle, &link->conf); - for (i = 0; i < info->ndev; i++) serial8250_resume_port(info->line[i]); + wakeup_card(info); } return 0; @@ -197,10 +205,9 @@ static int serial_resume(struct pcmcia_device *dev) ======================================================================*/ -static int serial_probe(struct pcmcia_device *p_dev) +static int serial_probe(struct pcmcia_device *link) { struct serial_info *info; - dev_link_t *link; DEBUG(0, "serial_attach()\n"); @@ -209,7 +216,7 @@ static int serial_probe(struct pcmcia_device *p_dev) if (!info) return -ENOMEM; memset(info, 0, sizeof (*info)); - link = &info->link; + info->p_dev = link; link->priv = info; link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; @@ -223,12 +230,7 @@ static int serial_probe(struct pcmcia_device *p_dev) } link->conf.IntType = INT_MEMORY_AND_IO; - link->handle = p_dev; - p_dev->instance = link; - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - serial_config(link); - - return 0; + return serial_config(link); } /*====================================================================== @@ -240,9 +242,8 @@ static int serial_probe(struct pcmcia_device *p_dev) ======================================================================*/ -static void serial_detach(struct pcmcia_device *p_dev) +static void serial_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); struct serial_info *info = link->priv; DEBUG(0, "serial_detach(0x%p)\n", link); @@ -263,7 +264,7 @@ static void serial_detach(struct pcmcia_device *p_dev) /*====================================================================*/ -static int setup_serial(client_handle_t handle, struct serial_info * info, +static int setup_serial(struct pcmcia_device *handle, struct serial_info * info, kio_addr_t iobase, int irq) { struct uart_port port; @@ -298,7 +299,7 @@ static int setup_serial(client_handle_t handle, struct serial_info * info, /*====================================================================*/ static int -first_tuple(client_handle_t handle, tuple_t * tuple, cisparse_t * parse) +first_tuple(struct pcmcia_device *handle, tuple_t * tuple, cisparse_t * parse) { int i; i = pcmcia_get_first_tuple(handle, tuple); @@ -311,7 +312,7 @@ first_tuple(client_handle_t handle, tuple_t * tuple, cisparse_t * parse) } static int -next_tuple(client_handle_t handle, tuple_t * tuple, cisparse_t * parse) +next_tuple(struct pcmcia_device *handle, tuple_t * tuple, cisparse_t * parse) { int i; i = pcmcia_get_next_tuple(handle, tuple); @@ -325,11 +326,10 @@ next_tuple(client_handle_t handle, tuple_t * tuple, cisparse_t * parse) /*====================================================================*/ -static int simple_config(dev_link_t *link) +static int simple_config(struct pcmcia_device *link) { static const kio_addr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; static const int size_table[2] = { 8, 16 }; - client_handle_t handle = link->handle; struct serial_info *info = link->priv; struct serial_cfg_mem *cfg_mem; tuple_t *tuple; @@ -350,7 +350,7 @@ static int simple_config(dev_link_t *link) buf = cfg_mem->buf; /* If the card is already configured, look up the port and irq */ - i = pcmcia_get_configuration_info(handle, &config); + i = pcmcia_get_configuration_info(link, &config); if ((i == CS_SUCCESS) && (config.Attributes & CONF_VALID_CLIENT)) { kio_addr_t port = 0; if ((config.BasePort2 != 0) && (config.NumPorts2 == 8)) { @@ -363,10 +363,9 @@ static int simple_config(dev_link_t *link) } if (info->slave) { kfree(cfg_mem); - return setup_serial(handle, info, port, config.AssignedIRQ); + return setup_serial(link, info, port, config.AssignedIRQ); } } - link->conf.Vcc = config.Vcc; /* First pass: look for a config entry that looks normal. */ tuple->TupleData = (cisdata_t *) buf; @@ -377,12 +376,12 @@ static int simple_config(dev_link_t *link) /* Two tries: without IO aliases, then with aliases */ for (s = 0; s < 2; s++) { for (try = 0; try < 2; try++) { - i = first_tuple(handle, tuple, parse); + i = first_tuple(link, tuple, parse); while (i != CS_NO_MORE_ITEMS) { if (i != CS_SUCCESS) goto next_entry; if (cf->vpp1.present & (1 << CISTPL_POWER_VNOM)) - link->conf.Vpp1 = link->conf.Vpp2 = + link->conf.Vpp = cf->vpp1.param[CISTPL_POWER_VNOM] / 10000; if ((cf->io.nwin > 0) && (cf->io.win[0].len == size_table[s]) && (cf->io.win[0].base != 0)) { @@ -390,19 +389,19 @@ static int simple_config(dev_link_t *link) link->io.BasePort1 = cf->io.win[0].base; link->io.IOAddrLines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK; - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) goto found_port; } next_entry: - i = next_tuple(handle, tuple, parse); + i = next_tuple(link, tuple, parse); } } } /* Second pass: try to find an entry that isn't picky about its base address, then try to grab any standard serial port address, and finally try to get any free port. */ - i = first_tuple(handle, tuple, parse); + i = first_tuple(link, tuple, parse); while (i != CS_NO_MORE_ITEMS) { if ((i == CS_SUCCESS) && (cf->io.nwin > 0) && ((cf->io.flags & CISTPL_IO_LINES_MASK) <= 3)) { @@ -410,50 +409,48 @@ next_entry: for (j = 0; j < 5; j++) { link->io.BasePort1 = base[j]; link->io.IOAddrLines = base[j] ? 16 : 3; - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); if (i == CS_SUCCESS) goto found_port; } } - i = next_tuple(handle, tuple, parse); + i = next_tuple(link, tuple, parse); } found_port: if (i != CS_SUCCESS) { printk(KERN_NOTICE "serial_cs: no usable port range found, giving up\n"); - cs_error(link->handle, RequestIO, i); + cs_error(link, RequestIO, i); kfree(cfg_mem); return -1; } - i = pcmcia_request_irq(link->handle, &link->irq); + i = pcmcia_request_irq(link, &link->irq); if (i != CS_SUCCESS) { - cs_error(link->handle, RequestIRQ, i); + cs_error(link, RequestIRQ, i); link->irq.AssignedIRQ = 0; } if (info->multi && (info->manfid == MANFID_3COM)) link->conf.ConfigIndex &= ~(0x08); - i = pcmcia_request_configuration(link->handle, &link->conf); + i = pcmcia_request_configuration(link, &link->conf); if (i != CS_SUCCESS) { - cs_error(link->handle, RequestConfiguration, i); + cs_error(link, RequestConfiguration, i); kfree(cfg_mem); return -1; } kfree(cfg_mem); - return setup_serial(handle, info, link->io.BasePort1, link->irq.AssignedIRQ); + return setup_serial(link, info, link->io.BasePort1, link->irq.AssignedIRQ); } -static int multi_config(dev_link_t * link) +static int multi_config(struct pcmcia_device * link) { - client_handle_t handle = link->handle; struct serial_info *info = link->priv; struct serial_cfg_mem *cfg_mem; tuple_t *tuple; u_char *buf; cisparse_t *parse; cistpl_cftable_entry_t *cf; - config_info_t config; int i, rc, base2 = 0; cfg_mem = kmalloc(sizeof(struct serial_cfg_mem), GFP_KERNEL); @@ -464,14 +461,6 @@ static int multi_config(dev_link_t * link) cf = &parse->cftable_entry; buf = cfg_mem->buf; - i = pcmcia_get_configuration_info(handle, &config); - if (i != CS_SUCCESS) { - cs_error(handle, GetConfigurationInfo, i); - rc = -1; - goto free_cfg_mem; - } - link->conf.Vcc = config.Vcc; - tuple->TupleData = (cisdata_t *) buf; tuple->TupleOffset = 0; tuple->TupleDataMax = 255; @@ -480,7 +469,7 @@ static int multi_config(dev_link_t * link) /* First, look for a generic full-sized window */ link->io.NumPorts1 = info->multi * 8; - i = first_tuple(handle, tuple, parse); + i = first_tuple(link, tuple, parse); while (i != CS_NO_MORE_ITEMS) { /* The quad port cards have bad CIS's, so just look for a window larger than 8 ports and assume it will be right */ @@ -490,19 +479,19 @@ static int multi_config(dev_link_t * link) link->io.BasePort1 = cf->io.win[0].base; link->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK; - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); base2 = link->io.BasePort1 + 8; if (i == CS_SUCCESS) break; } - i = next_tuple(handle, tuple, parse); + i = next_tuple(link, tuple, parse); } /* If that didn't work, look for two windows */ if (i != CS_SUCCESS) { link->io.NumPorts1 = link->io.NumPorts2 = 8; info->multi = 2; - i = first_tuple(handle, tuple, parse); + i = first_tuple(link, tuple, parse); while (i != CS_NO_MORE_ITEMS) { if ((i == CS_SUCCESS) && (cf->io.nwin == 2)) { link->conf.ConfigIndex = cf->index; @@ -510,26 +499,26 @@ static int multi_config(dev_link_t * link) link->io.BasePort2 = cf->io.win[1].base; link->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK; - i = pcmcia_request_io(link->handle, &link->io); + i = pcmcia_request_io(link, &link->io); base2 = link->io.BasePort2; if (i == CS_SUCCESS) break; } - i = next_tuple(handle, tuple, parse); + i = next_tuple(link, tuple, parse); } } if (i != CS_SUCCESS) { - cs_error(link->handle, RequestIO, i); + cs_error(link, RequestIO, i); rc = -1; goto free_cfg_mem; } - i = pcmcia_request_irq(link->handle, &link->irq); + i = pcmcia_request_irq(link, &link->irq); if (i != CS_SUCCESS) { printk(KERN_NOTICE "serial_cs: no usable port range found, giving up\n"); - cs_error(link->handle, RequestIRQ, i); + cs_error(link, RequestIRQ, i); link->irq.AssignedIRQ = 0; } /* Socket Dual IO: this enables irq's for second port */ @@ -537,35 +526,43 @@ static int multi_config(dev_link_t * link) link->conf.Present |= PRESENT_EXT_STATUS; link->conf.ExtStatus = ESR_REQ_ATTN_ENA; } - i = pcmcia_request_configuration(link->handle, &link->conf); + i = pcmcia_request_configuration(link, &link->conf); if (i != CS_SUCCESS) { - cs_error(link->handle, RequestConfiguration, i); + cs_error(link, RequestConfiguration, i); rc = -1; goto free_cfg_mem; } /* The Oxford Semiconductor OXCF950 cards are in fact single-port: - 8 registers are for the UART, the others are extra registers */ - if (info->manfid == MANFID_OXSEMI) { + * 8 registers are for the UART, the others are extra registers. + * Siemen's MC45 PCMCIA (Possio's GCC) is OXCF950 based too. + */ + if (info->manfid == MANFID_OXSEMI || (info->manfid == MANFID_POSSIO && + info->prodid == PRODID_POSSIO_GCC)) { + int err; + if (cf->index == 1 || cf->index == 3) { - setup_serial(handle, info, base2, link->irq.AssignedIRQ); - outb(12, link->io.BasePort1 + 1); + err = setup_serial(link, info, base2, + link->irq.AssignedIRQ); + base2 = link->io.BasePort1; } else { - setup_serial(handle, info, link->io.BasePort1, link->irq.AssignedIRQ); - outb(12, base2 + 1); + err = setup_serial(link, info, link->io.BasePort1, + link->irq.AssignedIRQ); } + info->c950ctrl = base2; + wakeup_card(info); rc = 0; goto free_cfg_mem; } - setup_serial(handle, info, link->io.BasePort1, link->irq.AssignedIRQ); + setup_serial(link, info, link->io.BasePort1, link->irq.AssignedIRQ); /* The Nokia cards are not really multiport cards */ if (info->manfid == MANFID_NOKIA) { rc = 0; goto free_cfg_mem; } for (i = 0; i < info->multi - 1; i++) - setup_serial(handle, info, base2 + (8 * i), + setup_serial(link, info, base2 + (8 * i), link->irq.AssignedIRQ); rc = 0; free_cfg_mem: @@ -581,9 +578,8 @@ free_cfg_mem: ======================================================================*/ -void serial_config(dev_link_t * link) +static int serial_config(struct pcmcia_device * link) { - client_handle_t handle = link->handle; struct serial_info *info = link->priv; struct serial_cfg_mem *cfg_mem; tuple_t *tuple; @@ -609,7 +605,7 @@ void serial_config(dev_link_t * link) tuple->Attributes = 0; /* Get configuration register information */ tuple->DesiredTuple = CISTPL_CONFIG; - last_ret = first_tuple(handle, tuple, parse); + last_ret = first_tuple(link, tuple, parse); if (last_ret != CS_SUCCESS) { last_fn = ParseTuple; goto cs_failed; @@ -617,18 +613,16 @@ void serial_config(dev_link_t * link) link->conf.ConfigBase = parse->config.base; link->conf.Present = parse->config.rmask[0]; - /* Configure card */ - link->state |= DEV_CONFIG; - /* Is this a compliant multifunction card? */ tuple->DesiredTuple = CISTPL_LONGLINK_MFC; tuple->Attributes = TUPLE_RETURN_COMMON | TUPLE_RETURN_LINK; - info->multi = (first_tuple(handle, tuple, parse) == CS_SUCCESS); + info->multi = (first_tuple(link, tuple, parse) == CS_SUCCESS); /* Is this a multiport card? */ tuple->DesiredTuple = CISTPL_MANFID; - if (first_tuple(handle, tuple, parse) == CS_SUCCESS) { + if (first_tuple(link, tuple, parse) == CS_SUCCESS) { info->manfid = parse->manfid.manf; + info->prodid = le16_to_cpu(buf[1]); for (i = 0; i < MULTI_COUNT; i++) if ((info->manfid == multi_id[i].manfid) && (parse->manfid.card == multi_id[i].prodid)) @@ -641,11 +635,11 @@ void serial_config(dev_link_t * link) multifunction cards that ask for appropriate IO port ranges */ tuple->DesiredTuple = CISTPL_FUNCID; if ((info->multi == 0) && - ((first_tuple(handle, tuple, parse) != CS_SUCCESS) || + ((first_tuple(link, tuple, parse) != CS_SUCCESS) || (parse->funcid.func == CISTPL_FUNCID_MULTI) || (parse->funcid.func == CISTPL_FUNCID_SERIAL))) { tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY; - if (first_tuple(handle, tuple, parse) == CS_SUCCESS) { + if (first_tuple(link, tuple, parse) == CS_SUCCESS) { if ((cf->io.nwin == 1) && (cf->io.win[0].len % 8 == 0)) info->multi = cf->io.win[0].len >> 3; if ((cf->io.nwin == 2) && (cf->io.win[0].len == 8) && @@ -664,31 +658,30 @@ void serial_config(dev_link_t * link) if (info->manfid == MANFID_IBM) { conf_reg_t reg = { 0, CS_READ, 0x800, 0 }; - last_ret = pcmcia_access_configuration_register(link->handle, ®); + last_ret = pcmcia_access_configuration_register(link, ®); if (last_ret) { last_fn = AccessConfigurationRegister; goto cs_failed; } reg.Action = CS_WRITE; reg.Value = reg.Value | 1; - last_ret = pcmcia_access_configuration_register(link->handle, ®); + last_ret = pcmcia_access_configuration_register(link, ®); if (last_ret) { last_fn = AccessConfigurationRegister; goto cs_failed; } } - link->dev = &info->node[0]; - link->state &= ~DEV_CONFIG_PENDING; + link->dev_node = &info->node[0]; kfree(cfg_mem); - return; + return 0; cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); failed: serial_remove(link); - link->state &= ~DEV_CONFIG_PENDING; kfree(cfg_mem); + return -ENODEV; } static struct pcmcia_device_id serial_ids[] = { @@ -739,6 +732,7 @@ static struct pcmcia_device_id serial_ids[] = { PCMCIA_MFC_DEVICE_PROD_ID1(1, "Motorola MARQUIS", 0xf03e4e77), PCMCIA_MFC_DEVICE_PROD_ID2(1, "FAX/Modem/Ethernet Combo Card ", 0x1ed59302), PCMCIA_DEVICE_MANF_CARD(0x0089, 0x0301), + PCMCIA_DEVICE_MANF_CARD(0x00a4, 0x0276), PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0039), PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0006), PCMCIA_DEVICE_MANF_CARD(0x0105, 0x410a), @@ -757,6 +751,7 @@ static struct pcmcia_device_id serial_ids[] = { PCMCIA_DEVICE_PROD_ID14("MEGAHERTZ", "PCMCIA MODEM", 0xf510db04, 0xbd6c43ef), PCMCIA_DEVICE_PROD_ID124("TOSHIBA", "T144PF", "PCMCIA MODEM", 0xb4585a1a, 0x7271409c, 0xbd6c43ef), PCMCIA_DEVICE_PROD_ID123("FUJITSU", "FC14F ", "MBH10213", 0x6ee5a3d8, 0x30ead12b, 0xb00f05a0), + PCMCIA_DEVICE_PROD_ID123("Novatel Wireless", "Merlin UMTS Modem", "U630", 0x32607776, 0xd9e73b13, 0xe87332e), PCMCIA_DEVICE_PROD_ID13("MEGAHERTZ", "V.34 PCMCIA MODEM", 0xf510db04, 0xbb2cce4a), PCMCIA_DEVICE_PROD_ID12("Brain Boxes", "Bluetooth PC Card", 0xee138382, 0xd4ce9b02), PCMCIA_DEVICE_PROD_ID12("CIRRUS LOGIC", "FAX MODEM", 0xe625f451, 0xcecd6dfa), diff --git a/drivers/sn/ioc3.c b/drivers/sn/ioc3.c index 93449a1a0065..0b49ff78efc1 100644 --- a/drivers/sn/ioc3.c +++ b/drivers/sn/ioc3.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -619,9 +620,9 @@ static int ioc3_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) pci_set_master(pdev); #ifdef USE_64BIT_DMA - ret = pci_set_dma_mask(pdev, 0xffffffffffffffffULL); + ret = pci_set_dma_mask(pdev, DMA_64BIT_MASK); if (!ret) { - ret = pci_set_consistent_dma_mask(pdev, 0xffffffffffffffffULL); + ret = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); if (ret < 0) { printk(KERN_WARNING "%s: Unable to obtain 64 bit DMA " "for consistent allocations\n", diff --git a/drivers/telephony/ixj_pcmcia.c b/drivers/telephony/ixj_pcmcia.c index d3a7b0c3d38b..dda0ca45d904 100644 --- a/drivers/telephony/ixj_pcmcia.c +++ b/drivers/telephony/ixj_pcmcia.c @@ -35,73 +35,52 @@ typedef struct ixj_info_t { } ixj_info_t; static void ixj_detach(struct pcmcia_device *p_dev); -static void ixj_config(dev_link_t * link); -static void ixj_cs_release(dev_link_t * link); +static int ixj_config(struct pcmcia_device * link); +static void ixj_cs_release(struct pcmcia_device * link); -static int ixj_attach(struct pcmcia_device *p_dev) +static int ixj_probe(struct pcmcia_device *p_dev) { - dev_link_t *link; - DEBUG(0, "ixj_attach()\n"); /* Create new ixj device */ - link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL); - if (!link) - return -ENOMEM; - memset(link, 0, sizeof(struct dev_link_t)); - link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; - link->io.Attributes2 = IO_DATA_PATH_WIDTH_8; - link->io.IOAddrLines = 3; - link->conf.Vcc = 50; - link->conf.IntType = INT_MEMORY_AND_IO; - link->priv = kmalloc(sizeof(struct ixj_info_t), GFP_KERNEL); - if (!link->priv) { - kfree(link); + p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + p_dev->io.Attributes2 = IO_DATA_PATH_WIDTH_8; + p_dev->io.IOAddrLines = 3; + p_dev->conf.IntType = INT_MEMORY_AND_IO; + p_dev->priv = kmalloc(sizeof(struct ixj_info_t), GFP_KERNEL); + if (!p_dev->priv) { return -ENOMEM; } - memset(link->priv, 0, sizeof(struct ixj_info_t)); + memset(p_dev->priv, 0, sizeof(struct ixj_info_t)); - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - ixj_config(link); - - return 0; + return ixj_config(p_dev); } -static void ixj_detach(struct pcmcia_device *p_dev) +static void ixj_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); - DEBUG(0, "ixj_detach(0x%p)\n", link); - link->state &= ~DEV_RELEASE_PENDING; - if (link->state & DEV_CONFIG) - ixj_cs_release(link); + ixj_cs_release(link); kfree(link->priv); - kfree(link); } #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) -static void ixj_get_serial(dev_link_t * link, IXJ * j) +static void ixj_get_serial(struct pcmcia_device * link, IXJ * j) { - client_handle_t handle; tuple_t tuple; u_short buf[128]; char *str; int last_ret, last_fn, i, place; - handle = link->handle; DEBUG(0, "ixj_get_serial(0x%p)\n", link); tuple.TupleData = (cisdata_t *) buf; tuple.TupleOffset = 0; tuple.TupleDataMax = 80; tuple.Attributes = 0; tuple.DesiredTuple = CISTPL_VERS_1; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); str = (char *) buf; printk("PCMCIA Version %d.%d\n", str[0], str[1]); str += 2; @@ -149,22 +128,19 @@ static void ixj_get_serial(dev_link_t * link, IXJ * j) return; } -static void ixj_config(dev_link_t * link) +static int ixj_config(struct pcmcia_device * link) { IXJ *j; - client_handle_t handle; ixj_info_t *info; tuple_t tuple; u_short buf[128]; cisparse_t parse; - config_info_t conf; cistpl_cftable_entry_t *cfg = &parse.cftable_entry; cistpl_cftable_entry_t dflt = { 0 }; int last_ret, last_fn; - handle = link->handle; info = link->priv; DEBUG(0, "ixj_config(0x%p)\n", link); tuple.TupleData = (cisdata_t *) buf; @@ -172,19 +148,17 @@ static void ixj_config(dev_link_t * link) tuple.TupleDataMax = 255; tuple.Attributes = 0; tuple.DesiredTuple = CISTPL_CONFIG; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; - link->state |= DEV_CONFIG; - CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf)); tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; tuple.Attributes = 0; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); while (1) { - if (pcmcia_get_tuple_data(handle, &tuple) != 0 || - pcmcia_parse_tuple(handle, &tuple, &parse) != 0) + if (pcmcia_get_tuple_data(link, &tuple) != 0 || + pcmcia_parse_tuple(link, &tuple, &parse) != 0) goto next_entry; if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) { cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io; @@ -195,7 +169,7 @@ static void ixj_config(dev_link_t * link) link->io.BasePort2 = io->win[1].base; link->io.NumPorts2 = io->win[1].len; } - if (pcmcia_request_io(link->handle, &link->io) != 0) + if (pcmcia_request_io(link, &link->io) != 0) goto next_entry; /* If we've got this far, we're done */ break; @@ -203,10 +177,10 @@ static void ixj_config(dev_link_t * link) next_entry: if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg; - CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple)); + CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple)); } - CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf)); + CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); /* * Register the card with the core. @@ -215,46 +189,21 @@ static void ixj_config(dev_link_t * link) info->ndev = 1; info->node.major = PHONE_MAJOR; - link->dev = &info->node; + link->dev_node = &info->node; ixj_get_serial(link, j); - link->state &= ~DEV_CONFIG_PENDING; - return; + return 0; cs_failed: - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); ixj_cs_release(link); + return -ENODEV; } -static void ixj_cs_release(dev_link_t *link) +static void ixj_cs_release(struct pcmcia_device *link) { ixj_info_t *info = link->priv; DEBUG(0, "ixj_cs_release(0x%p)\n", link); info->ndev = 0; - link->dev = NULL; - pcmcia_release_configuration(link->handle); - pcmcia_release_io(link->handle, &link->io); - link->state &= ~DEV_CONFIG; -} - -static int ixj_suspend(struct pcmcia_device *dev) -{ - dev_link_t *link = dev_to_instance(dev); - - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) - pcmcia_release_configuration(link->handle); - - return 0; -} - -static int ixj_resume(struct pcmcia_device *dev) -{ - dev_link_t *link = dev_to_instance(dev); - - link->state &= ~DEV_SUSPEND; - if (DEV_OK(link)) - pcmcia_request_configuration(link->handle, &link->conf); - - return 0; + pcmcia_disable_device(link); } static struct pcmcia_device_id ixj_ids[] = { @@ -268,11 +217,9 @@ static struct pcmcia_driver ixj_driver = { .drv = { .name = "ixj_cs", }, - .probe = ixj_attach, + .probe = ixj_probe, .remove = ixj_detach, .id_table = ixj_ids, - .suspend = ixj_suspend, - .resume = ixj_resume, }; static int __init ixj_pcmcia_init(void) diff --git a/drivers/usb/host/sl811_cs.c b/drivers/usb/host/sl811_cs.c index 134d2000128a..302aa1ec312f 100644 --- a/drivers/usb/host/sl811_cs.c +++ b/drivers/usb/host/sl811_cs.c @@ -67,11 +67,11 @@ module_param(pc_debug, int, 0644); static const char driver_name[DEV_NAME_LEN] = "sl811_cs"; typedef struct local_info_t { - dev_link_t link; + struct pcmcia_device *p_dev; dev_node_t node; } local_info_t; -static void sl811_cs_release(dev_link_t * link); +static void sl811_cs_release(struct pcmcia_device * link); /*====================================================================*/ @@ -138,41 +138,27 @@ static int sl811_hc_init(struct device *parent, ioaddr_t base_addr, int irq) /*====================================================================*/ -static void sl811_cs_detach(struct pcmcia_device *p_dev) +static void sl811_cs_detach(struct pcmcia_device *link) { - dev_link_t *link = dev_to_instance(p_dev); - DBG(0, "sl811_cs_detach(0x%p)\n", link); - link->state &= ~DEV_PRESENT; - if (link->state & DEV_CONFIG) - sl811_cs_release(link); + sl811_cs_release(link); /* This points to the parent local_info_t struct */ kfree(link->priv); } -static void sl811_cs_release(dev_link_t * link) +static void sl811_cs_release(struct pcmcia_device * link) { - DBG(0, "sl811_cs_release(0x%p)\n", link); - /* Unlink the device chain */ - link->dev = NULL; - + pcmcia_disable_device(link); platform_device_unregister(&platform_dev); - pcmcia_release_configuration(link->handle); - if (link->io.NumPorts1) - pcmcia_release_io(link->handle, &link->io); - if (link->irq.AssignedIRQ) - pcmcia_release_irq(link->handle, &link->irq); - link->state &= ~DEV_CONFIG; } -static void sl811_cs_config(dev_link_t *link) +static int sl811_cs_config(struct pcmcia_device *link) { - client_handle_t handle = link->handle; - struct device *parent = &handle_to_dev(handle); + struct device *parent = &handle_to_dev(link); local_info_t *dev = link->priv; tuple_t tuple; cisparse_t parse; @@ -188,27 +174,23 @@ static void sl811_cs_config(dev_link_t *link) tuple.TupleData = buf; tuple.TupleDataMax = sizeof(buf); tuple.TupleOffset = 0; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); - CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple)); - CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link, &tuple, &parse)); link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; - /* Configure card */ - link->state |= DEV_CONFIG; - /* Look up the current Vcc */ CS_CHECK(GetConfigurationInfo, - pcmcia_get_configuration_info(handle, &conf)); - link->conf.Vcc = conf.Vcc; + pcmcia_get_configuration_info(link, &conf)); tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; - CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple)); + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); while (1) { cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); - if (pcmcia_get_tuple_data(handle, &tuple) != 0 - || pcmcia_parse_tuple(handle, &tuple, &parse) + if (pcmcia_get_tuple_data(link, &tuple) != 0 + || pcmcia_parse_tuple(link, &tuple, &parse) != 0) goto next_entry; @@ -234,10 +216,10 @@ static void sl811_cs_config(dev_link_t *link) } if (cfg->vpp1.present & (1<conf.Vpp1 = link->conf.Vpp2 = + link->conf.Vpp = cfg->vpp1.param[CISTPL_POWER_VNOM]/10000; else if (dflt.vpp1.present & (1<conf.Vpp1 = link->conf.Vpp2 = + link->conf.Vpp = dflt.vpp1.param[CISTPL_POWER_VNOM]/10000; /* we need an interrupt */ @@ -254,15 +236,14 @@ static void sl811_cs_config(dev_link_t *link) link->io.BasePort1 = io->win[0].base; link->io.NumPorts1 = io->win[0].len; - if (pcmcia_request_io(link->handle, &link->io) != 0) + if (pcmcia_request_io(link, &link->io) != 0) goto next_entry; } break; next_entry: - if (link->io.NumPorts1) - pcmcia_release_io(link->handle, &link->io); - last_ret = pcmcia_get_next_tuple(handle, &tuple); + pcmcia_disable_device(link); + last_ret = pcmcia_get_next_tuple(link, &tuple); } /* require an IRQ and two registers */ @@ -270,71 +251,46 @@ next_entry: goto cs_failed; if (link->conf.Attributes & CONF_ENABLE_IRQ) CS_CHECK(RequestIRQ, - pcmcia_request_irq(link->handle, &link->irq)); + pcmcia_request_irq(link, &link->irq)); else goto cs_failed; CS_CHECK(RequestConfiguration, - pcmcia_request_configuration(link->handle, &link->conf)); + pcmcia_request_configuration(link, &link->conf)); sprintf(dev->node.dev_name, driver_name); dev->node.major = dev->node.minor = 0; - link->dev = &dev->node; + link->dev_node = &dev->node; - printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d", - dev->node.dev_name, link->conf.ConfigIndex, - link->conf.Vcc/10, link->conf.Vcc%10); - if (link->conf.Vpp1) - printk(", Vpp %d.%d", link->conf.Vpp1/10, link->conf.Vpp1%10); + printk(KERN_INFO "%s: index 0x%02x: ", + dev->node.dev_name, link->conf.ConfigIndex); + if (link->conf.Vpp) + printk(", Vpp %d.%d", link->conf.Vpp/10, link->conf.Vpp%10); printk(", irq %d", link->irq.AssignedIRQ); printk(", io 0x%04x-0x%04x", link->io.BasePort1, link->io.BasePort1+link->io.NumPorts1-1); printk("\n"); - link->state &= ~DEV_CONFIG_PENDING; - if (sl811_hc_init(parent, link->io.BasePort1, link->irq.AssignedIRQ) < 0) { cs_failed: printk("sl811_cs_config failed\n"); - cs_error(link->handle, last_fn, last_ret); + cs_error(link, last_fn, last_ret); sl811_cs_release(link); - link->state &= ~DEV_CONFIG_PENDING; + return -ENODEV; } -} - -static int sl811_suspend(struct pcmcia_device *dev) -{ - dev_link_t *link = dev_to_instance(dev); - - link->state |= DEV_SUSPEND; - if (link->state & DEV_CONFIG) - pcmcia_release_configuration(link->handle); - return 0; } -static int sl811_resume(struct pcmcia_device *dev) -{ - dev_link_t *link = dev_to_instance(dev); - - link->state &= ~DEV_SUSPEND; - if (link->state & DEV_CONFIG) - pcmcia_request_configuration(link->handle, &link->conf); - - return 0; -} - -static int sl811_cs_attach(struct pcmcia_device *p_dev) +static int sl811_cs_probe(struct pcmcia_device *link) { local_info_t *local; - dev_link_t *link; local = kmalloc(sizeof(local_info_t), GFP_KERNEL); if (!local) return -ENOMEM; memset(local, 0, sizeof(local_info_t)); - link = &local->link; + local->p_dev = link; link->priv = local; /* Initialize */ @@ -343,16 +299,9 @@ static int sl811_cs_attach(struct pcmcia_device *p_dev) link->irq.Handler = NULL; link->conf.Attributes = 0; - link->conf.Vcc = 33; link->conf.IntType = INT_MEMORY_AND_IO; - link->handle = p_dev; - p_dev->instance = link; - - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - sl811_cs_config(link); - - return 0; + return sl811_cs_config(link); } static struct pcmcia_device_id sl811_ids[] = { @@ -366,11 +315,9 @@ static struct pcmcia_driver sl811_cs_driver = { .drv = { .name = (char *)driver_name, }, - .probe = sl811_cs_attach, + .probe = sl811_cs_probe, .remove = sl811_cs_detach, .id_table = sl811_ids, - .suspend = sl811_suspend, - .resume = sl811_resume, }; /*====================================================================*/ diff --git a/drivers/usb/input/hid-input.c b/drivers/usb/input/hid-input.c index cb0d80f49252..25bc85f8ce39 100644 --- a/drivers/usb/input/hid-input.c +++ b/drivers/usb/input/hid-input.c @@ -510,7 +510,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x025: map_key_clear(KEY_TV); break; case 0x026: map_key_clear(KEY_MENU); break; case 0x031: map_key_clear(KEY_AUDIO); break; - case 0x032: map_key_clear(KEY_SUBTITLE); break; + case 0x032: map_key_clear(KEY_TEXT); break; case 0x033: map_key_clear(KEY_LAST); break; case 0x047: map_key_clear(KEY_MP3); break; case 0x048: map_key_clear(KEY_DVD); break; diff --git a/drivers/usb/net/zd1201.c b/drivers/usb/net/zd1201.c index fe9b60cd8d95..9b1e4ed1d07e 100644 --- a/drivers/usb/net/zd1201.c +++ b/drivers/usb/net/zd1201.c @@ -1736,6 +1736,7 @@ static const struct iw_handler_def zd1201_iw_handlers = { .standard = (iw_handler *)zd1201_iw_handler, .private = (iw_handler *)zd1201_private_handler, .private_args = (struct iw_priv_args *) zd1201_private_args, + .get_wireless_stats = zd1201_get_wireless_stats, }; static int zd1201_probe(struct usb_interface *interface, @@ -1796,7 +1797,6 @@ static int zd1201_probe(struct usb_interface *interface, zd->dev->open = zd1201_net_open; zd->dev->stop = zd1201_net_stop; zd->dev->get_stats = zd1201_get_stats; - zd->dev->get_wireless_stats = zd1201_get_wireless_stats; zd->dev->wireless_handlers = (struct iw_handler_def *)&zd1201_iw_handlers; zd->dev->hard_start_xmit = zd1201_hard_start_xmit; diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 495db5755df9..5cf2b80add7a 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -28,6 +28,7 @@ 2005-09-10 v0.4.3 added HUAWEI E600 card and Audiovox AirCard 2005-09-20 v0.4.4 increased recv buffer size: the card sometimes wants to send >2000 bytes. + 2006-04-10 v0.4.2 fixed two array overrun errors :-/ Work sponsored by: Sigos GmbH, Germany @@ -582,14 +583,14 @@ static void option_setup_urbs(struct usb_serial *serial) portdata = usb_get_serial_port_data(port); /* Do indat endpoints first */ - for (j = 0; j <= N_IN_URB; ++j) { + for (j = 0; j < N_IN_URB; ++j) { portdata->in_urbs[j] = option_setup_urb (serial, port->bulk_in_endpointAddress, USB_DIR_IN, port, portdata->in_buffer[j], IN_BUFLEN, option_indat_callback); } /* outdat endpoints */ - for (j = 0; j <= N_OUT_URB; ++j) { + for (j = 0; j < N_OUT_URB; ++j) { portdata->out_urbs[j] = option_setup_urb (serial, port->bulk_out_endpointAddress, USB_DIR_OUT, port, portdata->out_buffer[j], OUT_BUFLEN, option_outdat_callback); diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 22e9d696fdd2..9060e7137441 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -904,18 +904,6 @@ config FB_MATROX_MULTIHEAD There is no need for enabling 'Matrox multihead support' if you have only one Matrox card in the box. -config FB_RADEON_OLD - tristate "ATI Radeon display support (Old driver)" - depends on FB && PCI - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT - select FB_MACMODES if PPC - help - Choose this option if you want to use an ATI Radeon graphics card as - a framebuffer device. There are both PCI and AGP versions. You - don't need to choose this to run the Radeon in plain VGA mode. - config FB_RADEON tristate "ATI Radeon display support" depends on FB && PCI @@ -973,7 +961,7 @@ config FB_ATY128 config FB_ATY tristate "ATI Mach64 display support" if PCI || ATARI - depends on FB + depends on FB && !SPARC32 select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT diff --git a/drivers/video/Makefile b/drivers/video/Makefile index cb90218515ac..23de3b2c7856 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -39,7 +39,6 @@ obj-$(CONFIG_FB_KYRO) += kyro/ obj-$(CONFIG_FB_SAVAGE) += savage/ obj-$(CONFIG_FB_GEODE) += geode/ obj-$(CONFIG_FB_I810) += vgastate.o -obj-$(CONFIG_FB_RADEON_OLD) += radeonfb.o obj-$(CONFIG_FB_NEOMAGIC) += neofb.o vgastate.o obj-$(CONFIG_FB_VIRGE) += virgefb.o obj-$(CONFIG_FB_3DFX) += tdfxfb.o diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index b39e72d5413b..d9d7d3c4cae2 100644 --- a/drivers/video/aty/atyfb_base.c +++ b/drivers/video/aty/atyfb_base.c @@ -3400,7 +3400,7 @@ static int __devinit atyfb_pci_probe(struct pci_dev *pdev, const struct pci_devi struct atyfb_par *par; int i, rc = -ENOMEM; - for (i = ARRAY_SIZE(aty_chips); i >= 0; i--) + for (i = ARRAY_SIZE(aty_chips) - 1; i >= 0; i--) if (pdev->device == aty_chips[i].pci_id) break; diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 9d996f2c10d5..b895eaaa73fd 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -43,11 +43,11 @@ config LCD_DEVICE default y config BACKLIGHT_CORGI - tristate "Sharp Corgi Backlight Driver (SL-C7xx Series)" + tristate "Sharp Corgi Backlight Driver (SL Series)" depends on BACKLIGHT_DEVICE && PXA_SHARPSL default y help - If you have a Sharp Zaurus SL-C7xx, say y to enable the + If you have a Sharp Zaurus SL-C7xx, SL-Cxx00 or SL-6000x say y to enable the backlight driver. config BACKLIGHT_HP680 diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c index 151fda8dded0..334b1db1bd7c 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c @@ -16,14 +16,12 @@ static ssize_t backlight_show_power(struct class_device *cdev, char *buf) { - int rc; + int rc = -ENXIO; struct backlight_device *bd = to_backlight_device(cdev); down(&bd->sem); - if (likely(bd->props && bd->props->get_power)) - rc = sprintf(buf, "%d\n", bd->props->get_power(bd)); - else - rc = -ENXIO; + if (likely(bd->props)) + rc = sprintf(buf, "%d\n", bd->props->power); up(&bd->sem); return rc; @@ -31,7 +29,7 @@ static ssize_t backlight_show_power(struct class_device *cdev, char *buf) static ssize_t backlight_store_power(struct class_device *cdev, const char *buf, size_t count) { - int rc, power; + int rc = -ENXIO, power; char *endp; struct backlight_device *bd = to_backlight_device(cdev); @@ -40,12 +38,13 @@ static ssize_t backlight_store_power(struct class_device *cdev, const char *buf, return -EINVAL; down(&bd->sem); - if (likely(bd->props && bd->props->set_power)) { + if (likely(bd->props)) { pr_debug("backlight: set power to %d\n", power); - bd->props->set_power(bd, power); + bd->props->power = power; + if (likely(bd->props->update_status)) + bd->props->update_status(bd); rc = count; - } else - rc = -ENXIO; + } up(&bd->sem); return rc; @@ -53,14 +52,12 @@ static ssize_t backlight_store_power(struct class_device *cdev, const char *buf, static ssize_t backlight_show_brightness(struct class_device *cdev, char *buf) { - int rc; + int rc = -ENXIO; struct backlight_device *bd = to_backlight_device(cdev); down(&bd->sem); - if (likely(bd->props && bd->props->get_brightness)) - rc = sprintf(buf, "%d\n", bd->props->get_brightness(bd)); - else - rc = -ENXIO; + if (likely(bd->props)) + rc = sprintf(buf, "%d\n", bd->props->brightness); up(&bd->sem); return rc; @@ -68,7 +65,7 @@ static ssize_t backlight_show_brightness(struct class_device *cdev, char *buf) static ssize_t backlight_store_brightness(struct class_device *cdev, const char *buf, size_t count) { - int rc, brightness; + int rc = -ENXIO, brightness; char *endp; struct backlight_device *bd = to_backlight_device(cdev); @@ -77,12 +74,18 @@ static ssize_t backlight_store_brightness(struct class_device *cdev, const char return -EINVAL; down(&bd->sem); - if (likely(bd->props && bd->props->set_brightness)) { - pr_debug("backlight: set brightness to %d\n", brightness); - bd->props->set_brightness(bd, brightness); - rc = count; - } else - rc = -ENXIO; + if (likely(bd->props)) { + if (brightness > bd->props->max_brightness) + rc = -EINVAL; + else { + pr_debug("backlight: set brightness to %d\n", + brightness); + bd->props->brightness = brightness; + if (likely(bd->props->update_status)) + bd->props->update_status(bd); + rc = count; + } + } up(&bd->sem); return rc; @@ -90,14 +93,26 @@ static ssize_t backlight_store_brightness(struct class_device *cdev, const char static ssize_t backlight_show_max_brightness(struct class_device *cdev, char *buf) { - int rc; + int rc = -ENXIO; struct backlight_device *bd = to_backlight_device(cdev); down(&bd->sem); if (likely(bd->props)) rc = sprintf(buf, "%d\n", bd->props->max_brightness); - else - rc = -ENXIO; + up(&bd->sem); + + return rc; +} + +static ssize_t backlight_show_actual_brightness(struct class_device *cdev, + char *buf) +{ + int rc = -ENXIO; + struct backlight_device *bd = to_backlight_device(cdev); + + down(&bd->sem); + if (likely(bd->props && bd->props->get_brightness)) + rc = sprintf(buf, "%d\n", bd->props->get_brightness(bd)); up(&bd->sem); return rc; @@ -123,7 +138,10 @@ static struct class backlight_class = { static struct class_device_attribute bl_class_device_attributes[] = { DECLARE_ATTR(power, 0644, backlight_show_power, backlight_store_power), - DECLARE_ATTR(brightness, 0644, backlight_show_brightness, backlight_store_brightness), + DECLARE_ATTR(brightness, 0644, backlight_show_brightness, + backlight_store_brightness), + DECLARE_ATTR(actual_brightness, 0444, backlight_show_actual_brightness, + NULL), DECLARE_ATTR(max_brightness, 0444, backlight_show_max_brightness, NULL), }; @@ -144,8 +162,12 @@ static int fb_notifier_callback(struct notifier_block *self, bd = container_of(self, struct backlight_device, fb_notif); down(&bd->sem); if (bd->props) - if (!bd->props->check_fb || bd->props->check_fb(evdata->info)) - bd->props->set_power(bd, *(int *)evdata->data); + if (!bd->props->check_fb || + bd->props->check_fb(evdata->info)) { + bd->props->fb_blank = *(int *)evdata->data; + if (likely(bd->props && bd->props->update_status)) + bd->props->update_status(bd); + } up(&bd->sem); return 0; } @@ -231,6 +253,12 @@ void backlight_device_unregister(struct backlight_device *bd) &bl_class_device_attributes[i]); down(&bd->sem); + if (likely(bd->props && bd->props->update_status)) { + bd->props->brightness = 0; + bd->props->power = 0; + bd->props->update_status(bd); + } + bd->props = NULL; up(&bd->sem); diff --git a/drivers/video/backlight/corgi_bl.c b/drivers/video/backlight/corgi_bl.c index d0aaf450e8c7..2ebbfd95145f 100644 --- a/drivers/video/backlight/corgi_bl.c +++ b/drivers/video/backlight/corgi_bl.c @@ -1,7 +1,7 @@ /* - * Backlight Driver for Sharp Corgi + * Backlight Driver for Sharp Zaurus Handhelds (various models) * - * Copyright (c) 2004-2005 Richard Purdie + * Copyright (c) 2004-2006 Richard Purdie * * Based on Sharp's 2.4 Backlight Driver * @@ -15,80 +15,63 @@ #include #include #include -#include +#include #include #include - #include #include -#define CORGI_DEFAULT_INTENSITY 0x1f -#define CORGI_LIMIT_MASK 0x0b - -static int corgibl_powermode = FB_BLANK_UNBLANK; -static int current_intensity = 0; -static int corgibl_limit = 0; -static void (*corgibl_mach_set_intensity)(int intensity); -static spinlock_t bl_lock = SPIN_LOCK_UNLOCKED; +static int corgibl_intensity; +static DEFINE_MUTEX(bl_mutex); static struct backlight_properties corgibl_data; +static struct backlight_device *corgi_backlight_device; +static struct corgibl_machinfo *bl_machinfo; -static void corgibl_send_intensity(int intensity) +static unsigned long corgibl_flags; +#define CORGIBL_SUSPENDED 0x01 +#define CORGIBL_BATTLOW 0x02 + +static int corgibl_send_intensity(struct backlight_device *bd) { - unsigned long flags; void (*corgi_kick_batt)(void); + int intensity = bd->props->brightness; - if (corgibl_powermode != FB_BLANK_UNBLANK) { + if (bd->props->power != FB_BLANK_UNBLANK) intensity = 0; - } else { - if (corgibl_limit) - intensity &= CORGI_LIMIT_MASK; - } + if (bd->props->fb_blank != FB_BLANK_UNBLANK) + intensity = 0; + if (corgibl_flags & CORGIBL_SUSPENDED) + intensity = 0; + if (corgibl_flags & CORGIBL_BATTLOW) + intensity &= bl_machinfo->limit_mask; - spin_lock_irqsave(&bl_lock, flags); + mutex_lock(&bl_mutex); + bl_machinfo->set_bl_intensity(intensity); + mutex_unlock(&bl_mutex); - corgibl_mach_set_intensity(intensity); - - spin_unlock_irqrestore(&bl_lock, flags); + corgibl_intensity = intensity; corgi_kick_batt = symbol_get(sharpsl_battery_kick); if (corgi_kick_batt) { corgi_kick_batt(); symbol_put(sharpsl_battery_kick); } -} -static void corgibl_blank(int blank) -{ - switch(blank) { - - case FB_BLANK_NORMAL: - case FB_BLANK_VSYNC_SUSPEND: - case FB_BLANK_HSYNC_SUSPEND: - case FB_BLANK_POWERDOWN: - if (corgibl_powermode == FB_BLANK_UNBLANK) { - corgibl_send_intensity(0); - corgibl_powermode = blank; - } - break; - case FB_BLANK_UNBLANK: - if (corgibl_powermode != FB_BLANK_UNBLANK) { - corgibl_powermode = blank; - corgibl_send_intensity(current_intensity); - } - break; - } + return 0; } #ifdef CONFIG_PM static int corgibl_suspend(struct platform_device *dev, pm_message_t state) { - corgibl_blank(FB_BLANK_POWERDOWN); + corgibl_flags |= CORGIBL_SUSPENDED; + corgibl_send_intensity(corgi_backlight_device); return 0; } static int corgibl_resume(struct platform_device *dev) { - corgibl_blank(FB_BLANK_UNBLANK); + corgibl_flags &= ~CORGIBL_SUSPENDED; + corgibl_send_intensity(corgi_backlight_device); return 0; } #else @@ -96,30 +79,15 @@ static int corgibl_resume(struct platform_device *dev) #define corgibl_resume NULL #endif - -static int corgibl_set_power(struct backlight_device *bd, int state) -{ - corgibl_blank(state); - return 0; -} - -static int corgibl_get_power(struct backlight_device *bd) -{ - return corgibl_powermode; -} - -static int corgibl_set_intensity(struct backlight_device *bd, int intensity) -{ - if (intensity > corgibl_data.max_brightness) - intensity = corgibl_data.max_brightness; - corgibl_send_intensity(intensity); - current_intensity=intensity; - return 0; -} - static int corgibl_get_intensity(struct backlight_device *bd) { - return current_intensity; + return corgibl_intensity; +} + +static int corgibl_set_intensity(struct backlight_device *bd) +{ + corgibl_send_intensity(corgi_backlight_device); + return 0; } /* @@ -128,36 +96,38 @@ static int corgibl_get_intensity(struct backlight_device *bd) */ void corgibl_limit_intensity(int limit) { - corgibl_limit = (limit ? 1 : 0); - corgibl_send_intensity(current_intensity); + if (limit) + corgibl_flags |= CORGIBL_BATTLOW; + else + corgibl_flags &= ~CORGIBL_BATTLOW; + corgibl_send_intensity(corgi_backlight_device); } EXPORT_SYMBOL(corgibl_limit_intensity); static struct backlight_properties corgibl_data = { - .owner = THIS_MODULE, - .get_power = corgibl_get_power, - .set_power = corgibl_set_power, + .owner = THIS_MODULE, .get_brightness = corgibl_get_intensity, - .set_brightness = corgibl_set_intensity, + .update_status = corgibl_set_intensity, }; -static struct backlight_device *corgi_backlight_device; - static int __init corgibl_probe(struct platform_device *pdev) { struct corgibl_machinfo *machinfo = pdev->dev.platform_data; + bl_machinfo = machinfo; corgibl_data.max_brightness = machinfo->max_intensity; - corgibl_mach_set_intensity = machinfo->set_bl_intensity; + if (!machinfo->limit_mask) + machinfo->limit_mask = -1; corgi_backlight_device = backlight_device_register ("corgi-bl", NULL, &corgibl_data); if (IS_ERR (corgi_backlight_device)) return PTR_ERR (corgi_backlight_device); - corgibl_set_intensity(NULL, CORGI_DEFAULT_INTENSITY); - corgibl_limit_intensity(0); + corgibl_data.power = FB_BLANK_UNBLANK; + corgibl_data.brightness = machinfo->default_intensity; + corgibl_send_intensity(corgi_backlight_device); printk("Corgi Backlight Driver Initialized.\n"); return 0; @@ -167,8 +137,6 @@ static int corgibl_remove(struct platform_device *dev) { backlight_device_unregister(corgi_backlight_device); - corgibl_set_intensity(NULL, 0); - printk("Corgi Backlight Driver Unloaded\n"); return 0; } diff --git a/drivers/video/backlight/hp680_bl.c b/drivers/video/backlight/hp680_bl.c index 95da4c9ed1f1..a71e984c93d4 100644 --- a/drivers/video/backlight/hp680_bl.c +++ b/drivers/video/backlight/hp680_bl.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include @@ -25,66 +25,58 @@ #define HP680_MAX_INTENSITY 255 #define HP680_DEFAULT_INTENSITY 10 -static int hp680bl_powermode = FB_BLANK_UNBLANK; +static int hp680bl_suspended; static int current_intensity = 0; static spinlock_t bl_lock = SPIN_LOCK_UNLOCKED; +static struct backlight_device *hp680_backlight_device; -static void hp680bl_send_intensity(int intensity) +static void hp680bl_send_intensity(struct backlight_device *bd) { unsigned long flags; + u16 v; + int intensity = bd->props->brightness; - if (hp680bl_powermode != FB_BLANK_UNBLANK) + if (bd->props->power != FB_BLANK_UNBLANK) + intensity = 0; + if (bd->props->fb_blank != FB_BLANK_UNBLANK) + intensity = 0; + if (hp680bl_suspended) intensity = 0; spin_lock_irqsave(&bl_lock, flags); - sh_dac_output(255-(u8)intensity, DAC_LCD_BRIGHTNESS); - spin_unlock_irqrestore(&bl_lock, flags); -} - -static void hp680bl_blank(int blank) -{ - u16 v; - - switch(blank) { - - case FB_BLANK_NORMAL: - case FB_BLANK_VSYNC_SUSPEND: - case FB_BLANK_HSYNC_SUSPEND: - case FB_BLANK_POWERDOWN: - if (hp680bl_powermode == FB_BLANK_UNBLANK) { - hp680bl_send_intensity(0); - hp680bl_powermode = blank; - sh_dac_disable(DAC_LCD_BRIGHTNESS); - v = inw(HD64461_GPBDR); - v |= HD64461_GPBDR_LCDOFF; - outw(v, HD64461_GPBDR); - } - break; - case FB_BLANK_UNBLANK: - if (hp680bl_powermode != FB_BLANK_UNBLANK) { - sh_dac_enable(DAC_LCD_BRIGHTNESS); - v = inw(HD64461_GPBDR); - v &= ~HD64461_GPBDR_LCDOFF; - outw(v, HD64461_GPBDR); - hp680bl_powermode = blank; - hp680bl_send_intensity(current_intensity); - } - break; + if (intensity && current_intensity == 0) { + sh_dac_enable(DAC_LCD_BRIGHTNESS); + v = inw(HD64461_GPBDR); + v &= ~HD64461_GPBDR_LCDOFF; + outw(v, HD64461_GPBDR); + sh_dac_output(255-(u8)intensity, DAC_LCD_BRIGHTNESS); + } else if (intensity == 0 && current_intensity != 0) { + sh_dac_output(255-(u8)intensity, DAC_LCD_BRIGHTNESS); + sh_dac_disable(DAC_LCD_BRIGHTNESS); + v = inw(HD64461_GPBDR); + v |= HD64461_GPBDR_LCDOFF; + outw(v, HD64461_GPBDR); + } else if (intensity) { + sh_dac_output(255-(u8)intensity, DAC_LCD_BRIGHTNESS); } + spin_unlock_irqrestore(&bl_lock, flags); + + current_intensity = intensity; } + #ifdef CONFIG_PM -static int hp680bl_suspend(struct device *dev, pm_message_t state, u32 level) +static int hp680bl_suspend(struct platform_device *dev, pm_message_t state) { - if (level == SUSPEND_POWER_DOWN) - hp680bl_blank(FB_BLANK_POWERDOWN); + hp680bl_suspended = 1; + hp680bl_send_intensity(hp680_backlight_device); return 0; } -static int hp680bl_resume(struct device *dev, u32 level) +static int hp680bl_resume(struct platform_device *dev) { - if (level == RESUME_POWER_ON) - hp680bl_blank(FB_BLANK_UNBLANK); + hp680bl_suspended = 0; + hp680bl_send_intensity(hp680_backlight_device); return 0; } #else @@ -92,24 +84,9 @@ static int hp680bl_resume(struct device *dev, u32 level) #define hp680bl_resume NULL #endif - -static int hp680bl_set_power(struct backlight_device *bd, int state) +static int hp680bl_set_intensity(struct backlight_device *bd) { - hp680bl_blank(state); - return 0; -} - -static int hp680bl_get_power(struct backlight_device *bd) -{ - return hp680bl_powermode; -} - -static int hp680bl_set_intensity(struct backlight_device *bd, int intensity) -{ - if (intensity > HP680_MAX_INTENSITY) - intensity = HP680_MAX_INTENSITY; - hp680bl_send_intensity(intensity); - current_intensity = intensity; + hp680bl_send_intensity(bd); return 0; } @@ -120,65 +97,67 @@ static int hp680bl_get_intensity(struct backlight_device *bd) static struct backlight_properties hp680bl_data = { .owner = THIS_MODULE, - .get_power = hp680bl_get_power, - .set_power = hp680bl_set_power, .max_brightness = HP680_MAX_INTENSITY, .get_brightness = hp680bl_get_intensity, - .set_brightness = hp680bl_set_intensity, + .update_status = hp680bl_set_intensity, }; -static struct backlight_device *hp680_backlight_device; - -static int __init hp680bl_probe(struct device *dev) +static int __init hp680bl_probe(struct platform_device *dev) { hp680_backlight_device = backlight_device_register ("hp680-bl", NULL, &hp680bl_data); if (IS_ERR (hp680_backlight_device)) return PTR_ERR (hp680_backlight_device); - hp680bl_set_intensity(NULL, HP680_DEFAULT_INTENSITY); + hp680_backlight_device->props->brightness = HP680_DEFAULT_INTENSITY; + hp680bl_send_intensity(hp680_backlight_device); return 0; } -static int hp680bl_remove(struct device *dev) +static int hp680bl_remove(struct platform_device *dev) { backlight_device_unregister(hp680_backlight_device); return 0; } -static struct device_driver hp680bl_driver = { - .name = "hp680-bl", - .bus = &platform_bus_type, +static struct platform_driver hp680bl_driver = { .probe = hp680bl_probe, .remove = hp680bl_remove, .suspend = hp680bl_suspend, .resume = hp680bl_resume, + .driver = { + .name = "hp680-bl", + }, }; -static struct platform_device hp680bl_device = { - .name = "hp680-bl", - .id = -1, -}; +static struct platform_device *hp680bl_device; static int __init hp680bl_init(void) { int ret; - ret=driver_register(&hp680bl_driver); + ret = platform_driver_register(&hp680bl_driver); if (!ret) { - ret = platform_device_register(&hp680bl_device); - if (ret) - driver_unregister(&hp680bl_driver); + hp680bl_device = platform_device_alloc("hp680-bl", -1); + if (!hp680bl_device) + return -ENOMEM; + + ret = platform_device_add(hp680bl_device); + + if (ret) { + platform_device_put(hp680bl_device); + platform_driver_unregister(&hp680bl_driver); + } } return ret; } static void __exit hp680bl_exit(void) { - platform_device_unregister(&hp680bl_device); - driver_unregister(&hp680bl_driver); + platform_device_unregister(hp680bl_device); + platform_driver_unregister(&hp680bl_driver); } module_init(hp680bl_init); diff --git a/drivers/video/cfbimgblt.c b/drivers/video/cfbimgblt.c index 910e2338a27e..8ba6152db2fd 100644 --- a/drivers/video/cfbimgblt.c +++ b/drivers/video/cfbimgblt.c @@ -169,7 +169,7 @@ static inline void slow_imageblit(const struct fb_image *image, struct fb_info * while (j--) { l--; - color = (*s & 1 << (FB_BIT_NR(l))) ? fgcolor : bgcolor; + color = (*s & (1 << l)) ? fgcolor : bgcolor; val |= FB_SHIFT_HIGH(color, shift); /* Did the bitshift spill bits to the next long? */ diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 041d06987861..ca020719d20b 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -466,7 +466,7 @@ static int __init fb_console_setup(char *this_opt) int i, j; if (!this_opt || !*this_opt) - return 0; + return 1; while ((options = strsep(&this_opt, ",")) != NULL) { if (!strncmp(options, "font:", 5)) @@ -481,10 +481,10 @@ static int __init fb_console_setup(char *this_opt) options++; } if (*options != ',') - return 0; + return 1; options++; } else - return 0; + return 1; } if (!strncmp(options, "map:", 4)) { @@ -496,7 +496,7 @@ static int __init fb_console_setup(char *this_opt) con2fb_map_boot[i] = (options[j++]-'0') % FB_MAX; } - return 0; + return 1; } if (!strncmp(options, "vc:", 3)) { @@ -518,7 +518,7 @@ static int __init fb_console_setup(char *this_opt) rotate = 0; } } - return 0; + return 1; } __setup("fbcon=", fb_console_setup); @@ -1142,6 +1142,7 @@ static void fbcon_init(struct vc_data *vc, int init) set_blitting_type(vc, info); } + ops->p = &fb_display[fg_console]; } static void fbcon_deinit(struct vc_data *vc) diff --git a/drivers/video/console/sticore.c b/drivers/video/console/sticore.c index 0339f5640a78..74ac2acaf72c 100644 --- a/drivers/video/console/sticore.c +++ b/drivers/video/console/sticore.c @@ -275,7 +275,7 @@ static int __init sti_setup(char *str) if (str) strlcpy (default_sti_path, str, sizeof (default_sti_path)); - return 0; + return 1; } /* Assuming the machine has multiple STI consoles (=graphic cards) which @@ -321,7 +321,7 @@ static int __init sti_font_setup(char *str) i++; } - return 0; + return 1; } /* The optional linux kernel parameter "sti_font" defines which font @@ -373,7 +373,7 @@ sti_dump_globcfg(struct sti_glob_cfg *glob_cfg, unsigned int sti_mem_request) glob_cfg->save_addr)); /* dump extended cfg */ - cfg = PTR_STI(glob_cfg->ext_ptr); + cfg = PTR_STI((unsigned long)glob_cfg->ext_ptr); DPRINTK(( KERN_INFO "monitor %d\n" "in friendly mode: %d\n" @@ -453,25 +453,11 @@ sti_init_glob_cfg(struct sti_struct *sti, sti->regions_phys[i] = REGION_OFFSET_TO_PHYS(sti->regions[i], newhpa); - /* remap virtually */ - /* FIXME: add BTLB support if btlb==1 */ len = sti->regions[i].region_desc.length * 4096; - -/* XXX: Enabling IOREMAP debugging causes a crash, so we must be passing - * a virtual address to something expecting a physical address that doesn't - * go through a readX macro */ -#if 0 - if (len) - glob_cfg->region_ptrs[i] = (unsigned long) ( - sti->regions[i].region_desc.cache ? - ioremap(sti->regions_phys[i], len) : - ioremap_nocache(sti->regions_phys[i], len) ); -#else if (len) glob_cfg->region_ptrs[i] = sti->regions_phys[i]; -#endif - DPRINTK(("region #%d: phys %08lx, virt %08x, len=%lukB, " + DPRINTK(("region #%d: phys %08lx, region_ptr %08x, len=%lukB, " "btlb=%d, sysonly=%d, cache=%d, last=%d\n", i, sti->regions_phys[i], glob_cfg->region_ptrs[i], len/1024, diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index b1a8dca76430..8d8eadb64853 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c @@ -435,6 +435,11 @@ int fb_prepare_logo(struct fb_info *info, int rotate) depth = info->var.green.length; } + if (info->fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) { + /* assume console colormap */ + depth = 4; + } + if (depth >= 8) { switch (info->fix.visual) { case FB_VISUAL_TRUECOLOR: @@ -1588,7 +1593,7 @@ static int __init video_setup(char *options) } } - return 0; + return 1; } __setup("video=", video_setup); #endif diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index 53ad61f1038c..809fc5eefc15 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -232,9 +232,9 @@ static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) if (var->yres < MIN_YRES) var->yres = MIN_YRES; if (var->xres > fbi->max_xres) - var->xres = fbi->max_xres; + return -EINVAL; if (var->yres > fbi->max_yres) - var->yres = fbi->max_yres; + return -EINVAL; var->xres_virtual = max(var->xres_virtual, var->xres); var->yres_virtual = @@ -781,7 +781,7 @@ static void pxafb_disable_controller(struct pxafb_info *fbi) LCCR0 &= ~LCCR0_LDM; /* Enable LCD Disable Done Interrupt */ LCCR0 |= LCCR0_DIS; /* Disable LCD Controller */ - schedule_timeout(20 * HZ / 1000); + schedule_timeout(200 * HZ / 1000); remove_wait_queue(&fbi->ctrlr_wait, &wait); /* disable LCD controller clock */ @@ -1274,7 +1274,7 @@ int __init pxafb_probe(struct platform_device *dev) struct pxafb_mach_info *inf; int ret; - dev_dbg(dev, "pxafb_probe\n"); + dev_dbg(&dev->dev, "pxafb_probe\n"); inf = dev->dev.platform_data; ret = -ENOMEM; diff --git a/drivers/video/radeonfb.c b/drivers/video/radeonfb.c deleted file mode 100644 index afb6c2ead599..000000000000 --- a/drivers/video/radeonfb.c +++ /dev/null @@ -1,3167 +0,0 @@ -/* - * drivers/video/radeonfb.c - * framebuffer driver for ATI Radeon chipset video boards - * - * Copyright 2000 Ani Joshi - * - * - * ChangeLog: - * 2000-08-03 initial version 0.0.1 - * 2000-09-10 more bug fixes, public release 0.0.5 - * 2001-02-19 mode bug fixes, 0.0.7 - * 2001-07-05 fixed scrolling issues, engine initialization, - * and minor mode tweaking, 0.0.9 - * 2001-09-07 Radeon VE support, Nick Kurshev - * blanking, pan_display, and cmap fixes, 0.1.0 - * 2001-10-10 Radeon 7500 and 8500 support, and experimental - * flat panel support, 0.1.1 - * 2001-11-17 Radeon M6 (ppc) support, Daniel Berlin, 0.1.2 - * 2001-11-18 DFP fixes, Kevin Hendricks, 0.1.3 - * 2001-11-29 more cmap, backlight fixes, Benjamin Herrenschmidt - * 2002-01-18 DFP panel detection via BIOS, Michael Clark, 0.1.4 - * 2002-06-02 console switching, mode set fixes, accel fixes - * 2002-06-03 MTRR support, Peter Horton, 0.1.5 - * 2002-09-21 rv250, r300, m9 initial support, - * added mirror option, 0.1.6 - * - * Special thanks to ATI DevRel team for their hardware donations. - * - */ - - -#define RADEON_VERSION "0.1.6" - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#if defined(__powerpc__) -#include -#include -#include "macmodes.h" - -#ifdef CONFIG_NVRAM -#include -#endif - -#ifdef CONFIG_PMAC_BACKLIGHT -#include -#endif - -#ifdef CONFIG_BOOTX_TEXT -#include -#endif - -#ifdef CONFIG_ADB_PMU -#include -#include -#endif - -#endif /* __powerpc__ */ - -#ifdef CONFIG_MTRR -#include -#endif - -#include