diff -urN orig/Documentation/arm/00-INDEX linux/Documentation/arm/00-INDEX --- orig/Documentation/arm/00-INDEX Mon Jan 13 22:32:15 2003 +++ linux/Documentation/arm/00-INDEX Sun Apr 27 10:53:20 2003 @@ -2,19 +2,27 @@ - this file Booting - requirements for booting +ConfigVars + - random descriptions of various config variables Interrupts - ARM Interrupt subsystem documentation +MEMC + - memc-related functions Netwinder - Netwinder specific documentation README - General ARM documentation SA1100 - SA1100 documentation +Setup + - description of old param_struct. OBSOLETE. XScale - XScale documentation empeg - Empeg documentation mem_alignment - alignment abort handler documentation +memory.txt + - description of the virtual memory layout nwfpe - NWFPE floating point emulator documentation diff -urN orig/Documentation/arm/ConfigVars linux/Documentation/arm/ConfigVars --- orig/Documentation/arm/ConfigVars Thu Jan 1 01:00:00 1970 +++ linux/Documentation/arm/ConfigVars Thu Jul 20 18:28:19 2000 @@ -0,0 +1,26 @@ +CONFIG_ARCH_CO285 - co-ebsa285 +CONFIG_ARCH_FOOTBRIDGE - all other footbridge systems + + CONFIG_ARCH_CATS - CATS support + CONFIG_ARCH_PERSONAL_SERVER - personal server support + CONFIG_ARCH_EBSA285_ADDIN - add-in ebsa285 support + CONFIG_ARCH_EBSA285_HOST - host ebsa285 support + CONFIG_ARCH_NETWINDER - netwinder support + +CONFIG_FOOTBRIDGE - any system with a footbridge chip + |- (CONFIG_ARCH_CO285) + \- (CONFIG_ARCH_FOOTBRIDGE) + +CONFIG_FOOTBRIDGE_HOST - any system with host mode footbridge support + |- (CONFIG_ARCH_CATS) + |- (CONFIG_ARCH_EBSA285_HOST) + |- (CONFIG_ARCH_NETWINDER) + \- (CONFIG_ARCH_PERSONAL_SERVER) + +CONFIG_FOOTBRIDGE_ADDIN - any system with addin mode footbridge support + |- (CONFIG_ARCH_CO285) + \- (CONFIG_ARCH_EBSA285_ADDIN) + +CONFIG_ARCH_EBSA285 - either host or add-in ebsa285 support, but + |- (CONFIG_ARCH_EBSA285_ADDIN) not co-ebsa285 + \- (CONFIG_ARCH_EBSA285_HOST) diff -urN orig/Documentation/arm/MEMC linux/Documentation/arm/MEMC --- orig/Documentation/arm/MEMC Thu Jan 1 01:00:00 1970 +++ linux/Documentation/arm/MEMC Mon May 3 19:51:25 1999 @@ -0,0 +1,57 @@ +MEMC enhancements for Linux 2.3 +------------------------------- + +The current interface: + + There is a cache of the MEMC settings held in tsk->tss.memcmap, which is + kept up to date by the following functions (from the page tables): + + update_memc_all() hits: 2 + Updates all MEMC caches on all processes. Update the real MEMC + to reflect the `current' tasks page tables. + + update_memc_tsk(tsk) hits: 0 + Update the MEMC cache for the specified task. If tsk is the + `current' task, then update the real MEMC as well. + + update_memc_mm(mm) hits: 16 + Update the MEMC cache for any task which has a mm_struct + corresponding to `mm'. If the `current' tasks mm_struct + includes this, then update the real MEMC as well. + + update_memc_addr(mm, addr, pte) hits: 8 + Update the MEMC cache entry defined by the physical address + in pte for any task which has a mm_struct corresponding to `mm'. + If the `current' tasks mm_struct includes this, then update the + real MEMC as well. + +The proposed interface: + + Couple the MEMC cache into the mm_struct so that we only have to + keep one copy per mm_struct. This also allows us to reduce the + number of loops through all existing tasks on each MEMC change. + + memc_clear(mm, physaddr) hits: 6 + Clear the MEMC mapping associated with the physical address + `physaddr'. If the `current' tasks mm_struct is `mm', then + update the real MEMC as well. (should equate to a possible + two writes and zero reads). + + memc_update_addr(mm, pte, logaddr) hits: 10 + Change the MEMC mapping for the physical address specified + in `pte' to point to the logical address `logaddr', with the + protection specified in `pte'. If the `current' tasks mm_struct + is `mm', then update the real MEMC as well. (should again equate + to a possible two writes and zero reads). + + memc_update_mm(mm) hits: 7 + Rebuild the MEMC mappings for the specified `mm' in the same way + that update_memc_mm used to. If the `current' tasks mm_struct + is `mm', update the real MEMC as well. + + memc_update_all() hits: 2 + Rebuild the MEMC mappings for all mm_structs, including the real + MEMC. + +The hit numbers are approximate usage of each function in the 2.2.7 +memory management (mm) code, and there are other uses outside this area. diff -urN orig/Documentation/arm/SA1100/SA1100_USB linux/Documentation/arm/SA1100/SA1100_USB --- orig/Documentation/arm/SA1100/SA1100_USB Thu Jan 1 01:00:00 1970 +++ linux/Documentation/arm/SA1100/SA1100_USB Tue Oct 16 20:02:23 2001 @@ -0,0 +1,344 @@ +StrongARM SA-1100 USB function Driver +Ward Willats - 08Mar01 + +1. History +'''''''''' +Brad Parker ported the DEC/Compaq "Itsy" +SA-1100 USB Function driver to the 2.4.x code base in late 2000, for +use as an "ethernet over usb" link. His original notes are here in +section 4. Nicolas Pitre rewrote the transmitter and +reciver (endpoints 1 and 2) in late 2000 to use the standard SA DMA +API and I added a bulk character interface and reworked the control +control module code and rewrote endpoint zero in early 2001. + +This release (22Feb01) is the first that completely separates +client modules (usb-eth.c and usb-char.c) from the SA-1100 USB core. +(usb_ctl, usb_ep0, usb_send and usb_receive)and makes the whole +mess a module. Oleg Drokin has done a huge amount of work, fixing +things I break and adding support for the generic usbnet driver +from the AC tree. + +2. Usage +'''''''' +Turn on CONFIG_SA1100_USB_NETLINK to use the "ethernet over usb" +functionality. Turn it off to use the character oriented +interface. The character driver currently uses mknod c 10 240. + +Programming: +The public interface is in sa1100_usb.h. For a client USB service +to use the SA-1100 USB core driver it should: + +1. Call sa1100_usb_open() to get the usb core assigned to it. + +2. Setup descriptors as appropriate for the task at hand. Esp. +important are endpoint max packet lengths, vendor and product IDs, +and type of endpoint (bulk or interrupt). Call +sa1100_get_descriptor_ptr() to get this. + +3. Call sa1100_usb_start() to actually start the usb hardware. +At this time the host will configure the device. + +...at shutdown... + +4. Call sa1100_usb_stop() to stop the USB core. +5. Call sa1100_usb_close() to free the core for use by another +client. + +3. Netlink Usage +'''''''''''''''' +StrongARM SA-1100 USB function "ethernet over usb driver" +Brad Parker + +I ported the DEC "Itsy" usb "ethernet over usb driver" code to the 2.4.x +base and made some enhacements and bug fixes. This code has 2 sides and +implements a simple "ethernet over usb" functionality. + +function (SA1100) side: +- the driver has two endpoints and uses interrupt and bulk transfer to +receive/send packets. the driver does not require any other usb code +and should work on most any sa1100. + +host (SA1111) side: +- because the SA1111 usb host is not working yet I tested this driver +(usb-net-host.c) on a 2.2.14 based PC with the latest usb backport. +It has been fully converted to use URBs and worked well with my UHCI +based controller. + +TESTING: + +To test you need an assabet on the 'function' side, a PC on the 'host' +side and a USB A-B cable to connect them together. + +Boot a kernel on the assabet with "USB function and net-host support" +(CONFIG_SA1100_USB) turned on. This will define an interface named +"usbf". Once it's booted you can setup the interface with + + mount -t proc /proc /proc + /sbin/ifconfig usbf 1.1.1.2 + +I used a 2.2.14 kernel on a x86 PC for the host side. It has a built +in UHCI usb controller chip. I installed the latest USB backport from +http://www.linux-usb.org onto the 2.2.14 kernel sources and turned on +"USB net-host" (CONFIG_USB_NET_HOST) as a module. Load the module +"usb-net-host.o" and connect the USB cable to the assabet. Configure +the usb network interface with + + /sbin/ifconfig usb0 1.1.1.1 + +You should be able to "ping" the assabet now with + + ping -c 1 1.1.1.2 + +If the assabet is running inetd the usual network services such as +telnet and ftp should work. + +Oleg Drokin in 2.4.2-rmk1-np2 (08Mar01) added module config params for +read and write size to the usb-eth.c client to allow dynamic setting +of the DATA0/DATA1 packet size on the usb wire: + +usb_rsize - number of bytes in OUT packets from host to SA1100 +usb_wsize - number of bytes in IN packets from SA1100 to host + +This allows dynamic tuning for performance or to prevent overruning +the the host with data. + +4. Known Issues +''''''''''''''' +- We are fiddling with various ways to set the IMP register in +usb_send.c. A small percentage of the time, this value does not +"take." + +- I've started to bring back the /proc interface, but clients +of the sa-usb core currently don't have a directory or something +to put their stats into. + +- Only a useful subset of ep0 setup calls have been implemented. + + +5. Mysteries of the Universe +'''''''''''''''''''''''''''' +This driver has been hard to develop because the documentation +provided by Intel is incomplete, and the UDC itself seems to have a +variety of bugs. The errata for the part is particularly scary! This +section is an attempt to document some of the discoveries and +questions I have come across while working on this thing. + +pp 11-63 of the "Intel Strong ARM SA-1110 Microprocessor Advanced +Developer Information" give an ominous warning about how "due to +internal synchronization required by the UDC configuration registers, +it is possible for the procesor to write the UDC refisters and FIFOs +too fast." This has led to a variety of approaches that attempt to +bang on the hardware repeatedly and read it back until the write +"sticks." + +All of these approaches have been problematic. Currently some macros +in udc_ctl.h that Nicolas wrote are being used. My hardware guy told +me that writes would never be "lost" but stuck on some internal bus in +the UDC module and propagated to the rest of the circuit when the time +was right. Indeed this seemed to be the case, for example, it seems +impossible to reliably read back the interrupt mask register of the UDC when in +the interrupt service routine. Often times the state was not reflected +on a read until after pending interrupt sources were cleared. + +I was feeling prety good about this and was ripping out the looping +macros right and left until I came upon a situation where, while +receiving a continuous set of 1 character packets, ep1 (usb_receive.c) +could not clear receive packet complete (RPC). After much desperate +faliling about it turns out changing the UDC_flip() macro to bang like +crazy on the RPC bit did in fact clear it, and clear it +consistently. So go figure. + +Other items of interest: + +- Upon emerging from a reset, the UDC will clear the mask register except +for a mask on suspend. + +- USB 1.0 spec says maximum size of a DATA0/1 packet is 64 bytes, +which is what the character driver is using. However, the UDC can do +256 bytes and every host I've tried can handle it, even though they +are not required to. (Perhaps it is a problem when hubs are on the +line, but the SA UDC has other problems in a hub environment -- like +even getting the correct address -- per the errata). + +- Endpoint zero FIFOs: ARGHHH! Just leave those routines alone. +Believe me, I have tried every other variation you can think of. +Probably. + +- Sometimes I get a setup request of 0x80 from Windows hosts. I have +not determined if this is a read_fifo error (none is reported) or if +this is some undocumented secret Redmond handshake only known to +initiates of the inner-order. + +6. Test Program +''''''''''''''' +This is now in the /proc interface. (For good or ill, probably don't +actually need to dump all this stuff..) + +7. Errors and Notes on Intel's 1110 Documentation +''''''''''''''''''''''''''''''''''''''''''''''''' +These corrections apply to "Intel StrongARM SA-1110 Microprocessor, +Advanced Developer's Manual of December 1999" Some of these have been +corrected in later editions, some not. There have been several updates +to this document published through 2000. Always use the latest +available on http://developer.intel.com/design/strong/collateral.htm. + +pp 11-65 section 11.8.3.8 bit 2, reserved is now the resume interrupt +mask. SRM is now SUSIM on SA-1110, and masks only the suspend +interrupt. + +pp 11-67 section 11.8.6, Max IN register, end should be 9 _bytes_ +not 9 bits. + +pp 11-68 section 11.8.7.3, SST. This is set by the CPU _not_ the UDC. +And it looks like you don't get a SST if you FST yourself. + +pp 11-68 section 11.8.7.5, DE. This is set by the CPU _not_ the UDC. + +pp 11-73 section 11.8.9.7, UDCCS2 table, bit 2, Should be "valid only +when _TPC_ (not RPC) is set. + +pp 11-74 section 11.8.10, should end with a GET_DESCRIPTOR _or +similar_ command. (Like, for example, GET_CONFIGURATION). + + +8. Change History +''''''''''''''''' +Following are current chages 8Mar01 (released in 2.4.2-rmk1-np2?) + +- Resetting UDC when coming out of suspend helped enumeration get +going considerably. + +- Added support for client-supplied notify routine to be called +by the USB core when core reaches "configured" state. + +- Added error returns from interrupt reads and buffer flush ioctl +calls to usb-char. Added usb-char.h file for ioctl calls. + +- Fixed bug that kept usb-char transmitter from working the second +time the module was loaded. + +- Turned off a lot of the noise in /proc + +- Added specialty routines in ep0 to set and clear bits. + +- More enumeration fiddling. + +- There are horrible hacks to set max IN length in usb_send + that ARE GOING AWAY SOON! REALLY! + +*** Following changes 26Feb01 (released in 2.4.2-rmk1-np1) + +- usb-eth integration with generic usbnet from AC tree. + +- Creation of public interface for usb clients in sa1100_usb.h +and final separation into a "core" driver (usb_ctl.c, usb_ep0.c +usb_recv.c usb_send.c) and "client" services (usb-eth.c and +usb-char.c). Modularized. + +- Descriptor handling rewritten. Support for string descriptors +added. More bugs in ep0 fixed. More setup packets handled. + +- /proc interface in usb_ctl returning + +- removed client specific stuff from usbd_info_t and hid the +structure in usb_ctl. Removed RAM-backing of address and pktsize +in this structure. Now the descriptor values are gospel. + +- usb_dbg.h eliminated + +- Many bugs fixed in usb-char.c + +- Fiddled startup sequence so should start everytime. + +- Arch specific "soft connect" hook in usb_ctl.c + +- Bumped the interation count in write/set/clear macros +in usb_ctl.h up to 10000. This seemed to help various bit +setting in ep0 and usb_send.c. + +*** Following changes 10Feb01 release: + +- endpoint zero entirely rewitten + +- Various changes by Oleg to make Netlink work again after the + 2.4.1-rmk1-np1 release. + +- Resetting of new max packet length done after clearing TPC +in usb_send, per Nicolas Pitre. + + +*** Following changes 23Jan01 (came out in 2.4.1-rmk1-np1): + +- Moved host initiated SET/GET feature stall into endpoint code of +usb_send.c and usb_receive.c and removed stallep from usb_ctl.c +Opposite of a SET_FEATURE stall is a reset, so no code to unstall is +provided. + +- Added explicit USB state machine to usb_ctl so driver and device +state can be tracked closely and explicitly. Added hard-wired +notification routines in endpoints 1 and 2 so they can track device +state changes as required. State machine has notion of "zombie" state +the covers USB states NONATTACHED, ATTACHED and POWERED since these +are murky, and USB driver currently has no way to differentiate +between the two. + +- Reworked ISR in usb_ctl so reset has higher priority than any other +event. Stopped using sync macros to clear interrupt pending flags and set +mask registers since it appears mask register changes are not +always reflected on a mask register read until the pending flag is +cleared (yet other tests show they are always cleared +eventually). Toggle suspend/resume interrupt masks back and forth during +suspend and resume to debounce and keep UDC internal state machine in +sync per Intel documentation. + +- Flipped UDC flip, clear, write and set macros from do{}while to +while() loops. Theory is you might save a loop iteration if value +becomes valid immediately. Also, my hardware guy says writes are never +"lost", just pipelined and not executed immediately depending on +internal device conditions (like setting int masks when ints +pending), so moved write cycles in macros outside of loops. + +- Added #defines to SA-1110.h for suspend and resume interrupt mask +bits per Intel eratta. Submitted to ARM patch system (444/1). + +- Removed task queue and defered execution of configure() from +usb_ctl. + +- Removed usb_write_reg() from usb_ctl.c, and various cruft from +usb_ctl.h. + +- Added sa1100_usb_xmitter_avail() to usb_send.c. Makes implementing +poll() fileop easier. + +- Added sa1100_usb_send_reset() to usb_send.c. Makes implementing +transmitter timeout easier. + +- Added API to usb_ctl to set vendor and product ID + +- Changed BMATTR descriptor fron int to bulk, when not using netlink. (All +the docs say UDC does not support INT xfers -- though, at the protocol +level I don't see why not, since bulk and int are both just +IN-DATA-ACK. I figure netlink may rely on this, and not just a +continuous pending read from the host, but for "pure bulk" host +polling may not be generally correct.) + +- Removed unused rx_lock and tx_lock from usb_ctl + +- Converted everyone to SA-1100.h and nuked hardware defines in usb_ctl.h + +- Removed udc_init() in usb_ctl.c and folded functionality into udc_start(). + +- Clear force stall (FST) in udc_start and reset so UDC actually runs when +first turned on. + +- Emit NAK in receiver until ep1_start() for error (RPE) case too. + +- Remove enable/disable UDC from reset handler in udc_ctl. The UDC has +already been reset, so no need to do this again. + +- Explicitly set address to zero in ep0_reset() + +- Added "naking" boolean to usb_receiv.c. An attempt to solve a +hypothetical race condition where we are in the critical section +initiating a read from base-level code, a RPC happens, and start() +might clear the condition before the packet is handled by the ISR. diff -urN orig/Documentation/arm/memory.txt linux/Documentation/arm/memory.txt --- orig/Documentation/arm/memory.txt Thu Jan 1 01:00:00 1970 +++ linux/Documentation/arm/memory.txt Sun Apr 27 10:53:47 2003 @@ -0,0 +1,74 @@ + Kernel Memory Layout on ARM Linux + + Russell King + April 27, 2003 (2.5.68) + +This document describes the virtual memory layout which the Linux +kernel uses for ARM processors. It indicates which regions are +free for platforms to use, and which are used by generic code. + +The ARM CPU is capable of addressing a maximum of 4GB virtual memory +space, and this must be shared between user space processes, the +kernel, and hardware devices. + +As the ARM architecture matures, it becomes necessary to reserve +certain regions of VM space for use for new facilities; therefore +this document may reserve more VM space over time. + +Start End Use +-------------------------------------------------------------------------- +ffff8000 ffffffff copy_user_page / clear_user_page use. + For SA11xx and Xscale, this is used to + setup a minicache mapping. + +ffff1000 ffff7fff Reserved. + Platforms must not use this address range. + +ffff0000 ffff0fff CPU vector page. + The CPU vectors are mapped here if the + CPU supports vector relocation (control + register V bit.) + +ffe00000 fffeffff Free for platform use, not recommended. + +ffc00000 ffdfffff 2MB consistent memory mapping. + Memory returned by the consistent_alloc + low level function will be dynamically + mapped here. + +ff000000 ffbfffff Free for platform use, not recommended. + +VMALLOC_END ff000000 Free for platform use, recommended. + +VMALLOC_START VMALLOC_END vmalloc() / ioremap() space. + Memory returned by vmalloc/ioremap will + be dynamically placed in this region. + VMALLOC_START may be based upon the value + of the high_memory variable. + +PAGE_OFFSET high_memory Kernel direct-mapped RAM region. + This maps the platforms RAM, and typically + maps all platform RAM in a 1:1 relationship. + +TASK_SIZE PAGE_OFFSET Kernel module space + Kernel modules inserted via insmod are + placed here using dynamic mappings. + +00001000 TASK_SIZE User space mappings + Per-thread mappings are placed here via + the mmap() system call. + +00000000 00000fff CPU vector page / null pointer trap + CPUs which do not support vector remapping + place their vector page here. NULL pointer + dereferences by both the kernel and user + space are also caught via this mapping. + +Please note that mappings which collide with the above areas may result +in a non-bootable kernel, or may cause the kernel to (eventually) panic +at run time. + +Since future CPUs may impact the kernel mapping layout, user programs +must not access any memory which is not mapped inside their 0x0001000 +to TASK_SIZE address range. If they wish to access these areas, they +must set up their own mappings using open() and mmap(). diff -urN orig/Documentation/l3/structure linux/Documentation/l3/structure --- orig/Documentation/l3/structure Thu Jan 1 01:00:00 1970 +++ linux/Documentation/l3/structure Sun Aug 5 18:40:00 2001 @@ -0,0 +1,36 @@ +L3 Bus Driver +------------- + +The structure of the driver is as follows: + + +----------+ +----------+ +----------+ + | client 1 | | client 2 | | client 3 | + +-----^----+ +----^-----+ +----^-----+ + | | | + +-----v--------------v---------------v-----+ + | | + +-----^-------+ +-------^-----+ + | | core | | + +-----v----+ | | +----v-----+ + | device | | | | device | + | driver 1 | | | | driver 2 | + +-----^----+ | | +----^-----+ + | | services | | + +-----v-------+ +-------v-----+ + | | + +-----------------^----^-------------------+ + | | + | +-v---------+ + | | algorithm | + | | driver | + | +-v---------+ + | | + +-v----v-+ + | bus | + | driver | + +--------+ + +Clients talk to the core to attach device drivers and bus adapters, and +to instruct device drivers to perform actions. Device drivers then talk +to the core to perform L3 bus transactions via the algorithm driver and +ultimately bus driver. diff -urN orig/Documentation/sound/oss/WaveArtist linux/Documentation/sound/oss/WaveArtist --- orig/Documentation/sound/oss/WaveArtist Mon Mar 11 11:55:34 2002 +++ linux/Documentation/sound/oss/WaveArtist Thu May 8 00:59:57 2003 @@ -20,6 +20,7 @@ 02 | 0 | 0 1 1 1 | left mic mixer gain | left mic | left mixer gain |dith | ----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ 03 | 0 | 1 0 0 1 | left mixer input select |lrfg | left ADC gain | + | | | pcm | |micx |mic |aux1 | |line | | | ----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ 04 | 0 | 1 0 1 1 | right line mixer gain | right aux1 mixer gain |rmute| ----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ @@ -28,6 +29,7 @@ 06 | 0 | 1 1 1 1 | right mic mixer gain | right mic |right mixer gain |rbyps| ----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ 07 | 1 | 0 0 0 1 | right mixer select |rrfg | right ADC gain | + | | | pcm | |micx |mic |aux1 | |line | | | ----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ 08 | 1 | 0 0 1 1 | mono mixer gain |right ADC mux sel|left ADC mux sel | ----+---+------------+-----+-----+-----+----+-----+-----+-----+-----+-----+-----+-----+ @@ -91,59 +93,67 @@ L | | +----+ +------+ | | | | | | | \------\ Aux1 | | +----+ +------+ | | | - --------*----->mute>--> Gain >--> I | | - L | +----+ +------+ | | | + --------*----->mute>--> Gain >--> | | + L | +----+ +------+ | I | | | | | | - | +----+ +------+ | | +---+ | - *------->mute>--> Gain >--> X >-->AMP>--* + (Mic) | +----+ +------+ | | +---+ | + *------->mute>--> Gain >--> >-->AMP>--* | +----+ +------+ | | +-^-+ | - | | | | | - | +----+ +------+ | | r2b1-3 | - | /----->mute>--> Gain >--> E | | + | | X | | | + (Micx)| +----+ +------+ | | r2b1-3 | + | /----->mute>--> Gain >--> | | | | +----+ +------+ | | | | | | | | - | | +----+ +------+ | | | - | | /--->mute>--> Gain >--> R | | + (Mono)| | +----+ +------+ | E | | + | | /--->mute>--> Gain >--> | | | | | +----+ +------+ | | | + | | | | | | + FM | | | +------+ | | | + ----------------------> Gain >--> R | | + L | | | +------+ | | | | | | | | | r9b8-9 | | | +----+ +------+ | | | | | | | /->mute>--> Gain >--> | | +---v---+ | | | | +----+ +------+ +---+ /-*->0 | - DAC | | | | | | | + PCM | | | | | | | ------------*----------------------------------->? | +----+ L | | | | | Mux >-->mute>--> L output | | | | /->? | +--^-+ | | | | | | | | - | | | /--------->? | r0b0 - | | | | | | +-------+ - | | | | | | - Mono | | | | | | +-------+ - ----------* | \---> | +----+ - | | | | | | Mix >-->mute>--> Mono output - | | | | *-> | +--^-+ - | | | | | +-------+ | - | | | | | r1b0 - DAC | | | | | +-------+ - ------------*-------------------------*--------->1 | +----+ + | | | /----->? | r0b0 + | | | | | | +-------+ + | | | | | | + Mono | | | | | | +-------+ + ----------* | \---> | +----+ + | | | | | | Mix >-->mute>--> Mono output + | | | | *-> | +--^-+ + | | | | | +-------+ | + | | | | | r1b0 + PCM | | | | | +-------+ + ------------*-----------------------------*----->1 | +----+ R | | | | | | Mux >-->mute>--> R output | | | | +----+ +------+ +---+ *->0 | +--^-+ | | | \->mute>--> Gain >--> | | +---^---+ | - | | | +----+ +------+ | | | | r5b0 + | | | +----+ +------+ | | | | r4b0 | | | | | | r6b0 - | | | +----+ +------+ | | | - | | \--->mute>--> Gain >--> M | | - | | +----+ +------+ | | | - | | | | | - | | +----+ +------+ | | | - | *----->mute>--> Gain >--> I | | - | | +----+ +------+ | | | + FM | | | +------+ | | | + ----------------------> Gain >--> M | | + R | | | +------+ | | | + | | | | | | + (Mono)| | | +----+ +------+ | | | + | | \--->mute>--> Gain >--> | | + | | +----+ +------+ | I | | | | | | | - | | +----+ +------+ | | +---+ | - \------->mute>--> Gain >--> X >-->AMP>--* - | +----+ +------+ | | +-^-+ | - /--/ | | | | - Aux1 | +----+ +------+ | | r6b1-3 | - -------*------>mute>--> Gain >--> E | | + (Micx)| | +----+ +------+ | | | + \------->mute>--> Gain >--> | | + | +----+ +------+ | | | + | | X | | + (Mic) | +----+ +------+ | | +---+ | + /--*----->mute>--> Gain >--> >-->AMP>--* + | +----+ +------+ | | +-^-+ | + | | | | | + Aux1 | +----+ +------+ | E | r6b1-3 | + -------*------>mute>--> Gain >--> | | R | | +----+ +------+ | | | | | | | | Aux2 | | +----+ +------+ | | /------/ diff -urN orig/Makefile linux/Makefile --- orig/Makefile Tue May 27 10:04:12 2003 +++ linux/Makefile Thu Jun 12 15:45:39 2003 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 5 SUBLEVEL = 70 -EXTRAVERSION = +EXTRAVERSION =-rmk1 # *DOCUMENTATION* # To see a list of typical targets execute "make help" @@ -462,7 +462,7 @@ include/asm: @echo ' Making asm->asm-$(ARCH) symlink' - @ln -s asm-$(ARCH) $@ + @rm -f $@; ln -sf asm-$(ARCH) $@ # Split autoconf.h into include/linux/config/* diff -urN orig/arch/arm/Kconfig linux/arch/arm/Kconfig --- orig/arch/arm/Kconfig Mon May 5 17:38:44 2003 +++ linux/arch/arm/Kconfig Thu Jun 12 14:52:26 2003 @@ -175,6 +175,8 @@ source "arch/arm/mach-footbridge/Kconfig" +source "arch/arm/mach-integrator/Kconfig" + source "arch/arm/mach-iop3xx/Kconfig" source "arch/arm/mach-pxa/Kconfig" @@ -377,6 +379,15 @@ If you don't know what this all is, saying Y is a safe choice. +config CPU_BIG_ENDIAN + bool "Build big-endian kernel" + depends on ARCH_SUPPORTS_BIG_ENDIAN + help + Say Y if you plan on running a kernel in big-endian mode. + Note that your board must be properly built and your board + port must properly enable and big-endian related features + of your chipset/board/processor. + config CPU_ICACHE_DISABLE bool "Disable I-Cache" depends on CPU_ARM920T || CPU_ARM922T || CPU_ARM926T || CPU_ARM1020 @@ -423,9 +434,14 @@ menu "General setup" # Select various configuration options depending on the machine type + +config NUMA + bool "Numa Memory Allocation Support" + depends on ARCH_INTEGRATOR + config DISCONTIGMEM bool - depends on ARCH_EDB7211 || ARCH_SA1100 + depends on ARCH_EDB7211 || ARCH_SA1100 || NUMA default y help Say Y to upport efficient handling of discontiguous physical memory, @@ -474,6 +490,16 @@ depends on PCI && ARCH_SHARK default y +config ICST525 + bool + depends on ARCH_INTEGRATOR + default y + +config ARM_AMBA + bool + depends on ARCH_INTEGRATOR + default y + config ISA bool depends on FOOTBRIDGE_HOST || ARCH_SHARK || ARCH_CLPS7500 || ARCH_EBSA110 || ARCH_CDB89712 || ARCH_EDB7211 || ARCH_SA1100 @@ -573,7 +599,7 @@ config CPU_FREQ_INTEGRATOR tristate "CPUfreq driver for ARM Integrator CPUs" - depends on ARCH_INTEGRATOR && CPU_FREQ + depends on ARCH_INTEGRATOR && ICST525 && CPU_FREQ default y help This enables the CPUfreq driver for ARM Integrator CPUs. diff -urN orig/arch/arm/Makefile linux/arch/arm/Makefile --- orig/arch/arm/Makefile Mon May 5 17:38:44 2003 +++ linux/arch/arm/Makefile Wed Jun 11 22:27:03 2003 @@ -24,6 +24,12 @@ CFLAGS +=-g endif +ifeq ($(CONFIG_CPU_BIG_ENDIAN),y) +CFLAGS += -mbig-endian +AS += -EB +LD += -EB +endif + # Select CPU dependent flags. Note that order of declaration is important; # the options further down the list override previous items. # @@ -34,10 +40,7 @@ # Note that GCC does not numerically define an architecture version # macro, but instead defines a whole series of macros which makes # testing for a specific architecture or later rather impossible. -# -# Note - GCC does accept -march=armv5te, but someone messed up the assembler or the -# gcc specs file - this needs fixing properly - ie in gcc and/or binutils. -arch-$(CONFIG_CPU_32v5) :=-D__LINUX_ARM_ARCH__=5 -march=armv5t +arch-$(CONFIG_CPU_32v5) :=-D__LINUX_ARM_ARCH__=5 -march=armv5te arch-$(CONFIG_CPU_32v4) :=-D__LINUX_ARM_ARCH__=4 -march=armv4 arch-$(CONFIG_CPU_32v3) :=-D__LINUX_ARM_ARCH__=3 -march=armv3 @@ -50,13 +53,13 @@ tune-$(CONFIG_CPU_ARM926T) :=-mtune=arm9tdmi tune-$(CONFIG_CPU_SA110) :=-mtune=strongarm110 tune-$(CONFIG_CPU_SA1100) :=-mtune=strongarm1100 -tune-$(CONFIG_CPU_XSCALE) :=-mtune=strongarm -Wa,-mxscale #-mtune=xscale +tune-$(CONFIG_CPU_XSCALE) :=-mtune=xscale # Force -mno-fpu to be passed to the assembler. Some versions of gcc don't # do this with -msoft-float CFLAGS_BOOT :=$(apcs-y) $(arch-y) $(tune-y) -mshort-load-bytes -msoft-float -Wa,-mno-fpu -Uarm CFLAGS +=$(apcs-y) $(arch-y) $(tune-y) -mshort-load-bytes -msoft-float -Wa,-mno-fpu -Uarm -AFLAGS +=$(apcs-y) $(arch-y) $(tune-y) -mno-fpu -msoft-float -Wa,-mno-fpu +AFLAGS +=$(apcs-y) $(arch-y) $(tune-y) -msoft-float -Wa,-mno-fpu #Default value DATAADDR := . @@ -76,7 +79,11 @@ ifeq ($(CONFIG_CPU_32),y) PROCESSOR := armv head-y := arch/arm/kernel/head.o arch/arm/kernel/init_task.o -LDFLAGS_BLOB += --oformat elf32-littlearm + ifeq ($(CONFIG_CPU_BIG_ENDIAN),y) + LDFLAGS_BLOB += --oformat elf32-bigarm + else + LDFLAGS_BLOB += --oformat elf32-littlearm + endif textaddr-y := 0xC0008000 endif diff -urN orig/arch/arm/boot/bootp/init.S linux/arch/arm/boot/bootp/init.S --- orig/arch/arm/boot/bootp/init.S Thu Oct 3 12:45:50 2002 +++ linux/arch/arm/boot/bootp/init.S Fri Feb 7 12:06:21 2003 @@ -1,86 +1,69 @@ /* * linux/arch/arm/boot/bootp/init.S * - * Copyright (C) 2000-2002 Russell King + * Copyright (C) 2000-2003 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. * - * Header file for splitting kernel + initrd. Note that we pass + * "Header" file for splitting kernel + initrd. Note that we pass * r0 through to r3 straight through. + * + * This demonstrates how to append code to the start of the kernel + * zImage, and boot the kernel without copying it around. This + * example would be simpler; if we didn't have an object of unknown + * size immediately following the kernel, we could build this into + * a binary blob, and concatenate the zImage using the cat command. */ .section .start,#alloc,#execinstr .type _start, #function .globl _start -_start: adr r10, initdata - ldr r11, initdata - sub r11, r10, r11 @ work out exec offset - b splitify - .size _entry,. - _entry - - .type initdata, #object -initdata: .word initdata @ compiled address of this - .size initdata,. - initdata - -splitify: adr r13, data - ldmia r13!, {r4-r6} @ move the initrd - add r4, r4, r11 @ correction - bl move + +_start: adr r12, kernel_start @ offset of kernel zImage + ldr r4, [r12, #0x2c] @ length of zImage + adr r13, data + add r4, r4, r12 @ end of zImage, start of initrd + ldmia r13!, {r5-r6} @ r5 = dest, r6 = length + bl move @ move the initrd /* - * Setup the initrd parameters to pass to the kernel. This can either be - * passed in via a param_struct or a tag list. We spot the param_struct - * method by looking at the first word; this should either indicate a page - * size of 4K, 16K or 32K. + * Setup the initrd parameters to pass to the kernel. This can only be + * passed in via the tagged list. */ - ldmia r13, {r4-r8} @ get size and addr of initrd - @ r5 = ATAG_INITRD - @ r6 = initrd start - @ r7 = initrd end - @ r8 = param_struct address - ldr r9, [r8, #0] @ no param struct? - teq r9, #0x1000 @ 4K? - teqne r9, #0x4000 @ 16K? - teqne r9, #0x8000 @ 32K? - beq param_struct - - ldr r9, [r8, #4] @ get first tag - teq r9, r4 - bne taglist @ ok, we have a tag list + ldmia r13, {r5-r9} @ get size and addr of initrd + @ r5 = ATAG_CORE + @ r6 = ATAG_INITRD2 + @ r7 = initrd start + @ r8 = initrd end + @ r9 = param_struct address + ldr r10, [r9, #4] @ get first tag + teq r10, r5 @ is it ATAG_CORE? /* - * We didn't find a valid tag list - create one. + * If we didn't find a valid tag list, create a dummy ATAG_CORE entry. */ - str r4, [r8, #4] - mov r4, #8 - str r4, [r8, #0] - mov r4, #0 - str r4, [r8, #8] + movne r10, #0 @ terminator + movne r4, #2 @ Size of this entry (2 words) + stmneia r8, {r4, r5, r10} @ Size, ATAG_CORE, terminator /* * find the end of the tag list, and then add an INITRD tag on the end. * If there is already an INITRD tag, then we ignore it; the last INITRD * tag takes precidence. */ -taglist: ldr r9, [r8, #0] @ tag length - teq r9, #0 @ last tag? - addne r8, r8, r9 +taglist: ldr r10, [r9, #0] @ tag length + teq r10, #0 @ last tag (zero length)? + addne r9, r9, r10, lsl #2 bne taglist - mov r4, #16 @ length of initrd tag - mov r9, #0 @ end of tag list terminator - stmia r8, {r4, r5, r6, r7, r9} - adr r12, kernel_start + mov r5, #4 @ Size of initrd tag (4 words) + stmia r9, {r5, r6, r7, r8, r10} mov pc, r12 @ call kernel /* - * We found a param struct. Modify the param struct for the initrd + * Move the block of memory length r6 from address r4 to address r5 */ -param_struct: add r8, r8, #16*4 - stmia r8, {r6,r7} @ save in param_struct - mov pc, r12 @ call kernel - move: ldmia r4!, {r7 - r10} @ move 32-bytes at a time stmia r5!, {r7 - r10} ldmia r4!, {r7 - r10} @@ -89,16 +72,20 @@ bcs move mov pc, lr -data: .word initrd_start - .word initrd_addr - .word initrd_len + .size _start, . - _start + + .type data,#object +data: .word initrd_addr @ destination initrd address + .word initrd_len @ initrd size .word 0x54410001 @ r4 = ATAG_CORE - .word 0x54420005 @ r5 = ATAG_INITRD + .word 0x54420005 @ r5 = ATAG_INITRD2 .word initrd_addr @ r6 .word initrd_len @ r7 .word params @ r8 + .size data, . - _data + .type initrd_start,#object kernel_start: diff -urN orig/arch/arm/boot/compressed/misc.c linux/arch/arm/boot/compressed/misc.c --- orig/arch/arm/boot/compressed/misc.c Sun Oct 20 00:30:53 2002 +++ linux/arch/arm/boot/compressed/misc.c Sun Apr 20 23:19:04 2003 @@ -113,7 +113,7 @@ * gzip delarations */ #define OF(args) args -#define STATIC static +#define STATIC typedef unsigned char uch; typedef unsigned short ush; @@ -122,12 +122,12 @@ #define WSIZE 0x8000 /* Window size must be at least 32k, */ /* and a power of two */ -static uch *inbuf; /* input buffer */ -static uch window[WSIZE]; /* Sliding window buffer */ +unsigned char *inbuf; /* input buffer */ +unsigned char window[WSIZE]; /* Sliding window buffer */ -static unsigned insize; /* valid bytes in inbuf */ -static unsigned inptr; /* index of next byte to be processed in inbuf */ -static unsigned outcnt; /* bytes in output buffer */ +unsigned int insize; /* valid bytes in inbuf */ +unsigned int inptr; /* index of next byte to be processed in inbuf */ +unsigned int outcnt; /* bytes in output buffer */ /* gzip flag byte */ #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ @@ -166,9 +166,9 @@ extern char input_data[]; extern char input_data_end[]; -static uch *output_data; -static ulg output_ptr; -static ulg bytes_out; +unsigned char *output_data; +unsigned long output_ptr; +unsigned long bytes_out; static void *malloc(int size); static void free(void *where); @@ -179,8 +179,8 @@ static void puts(const char *); extern int end; -static ulg free_mem_ptr; -static ulg free_mem_ptr_end; +unsigned long free_mem_ptr; +unsigned long free_mem_ptr_end; #define HEAP_SIZE 0x2000 diff -urN orig/arch/arm/common/Makefile linux/arch/arm/common/Makefile --- orig/arch/arm/common/Makefile Tue Feb 11 16:09:37 2003 +++ linux/arch/arm/common/Makefile Thu Jun 12 14:52:16 2003 @@ -2,7 +2,9 @@ # Makefile for the linux kernel. # -obj-$(CONFIG_SA1111) += sa1111.o sa1111-pcibuf.o sa1111-pcipool.o - +obj-y += platform.o +obj-$(CONFIG_ARM_AMBA) += amba.o +obj-$(CONFIG_ICST525) += icst525.o +obj-$(CONFIG_SA1111) += sa1111.o sa1111-pcibuf.o sa1111-pcipool.o obj-$(CONFIG_PCI_HOST_PLX90X0) += plx90x0.o obj-$(CONFIG_PCI_HOST_VIA82C505) += via82c505.o diff -urN orig/arch/arm/common/amba.c linux/arch/arm/common/amba.c --- orig/arch/arm/common/amba.c Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/common/amba.c Thu Jun 12 14:51:54 2003 @@ -0,0 +1,243 @@ +/* + * linux/arch/arm/common/amba.c + * + * Copyright (C) 2003 Deep Blue Solutions Ltd, 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. + */ +#include +#include +#include + +#include +#include +#include + +#define to_amba_device(d) container_of(d, struct amba_device, dev) +#define to_amba_driver(d) container_of(d, struct amba_driver, drv) + +static struct amba_id * +amba_lookup(struct amba_id *table, struct amba_device *dev) +{ + int ret = 0; + + while (table->mask) { + ret = (dev->periphid & table->mask) == table->id; + if (ret) + break; + table++; + } + + return ret ? table : NULL; +} + +static int amba_match(struct device *dev, struct device_driver *drv) +{ + struct amba_device *pcdev = to_amba_device(dev); + struct amba_driver *pcdrv = to_amba_driver(drv); + + return amba_lookup(pcdrv->id_table, pcdev) != NULL; +} + +/* + * Primecells are part of the Advanced Microcontroller Bus Architecture, + * so we call the bus "amba". + */ +struct bus_type amba_bustype = { + .name = "amba", + .match = amba_match, +}; + +static int __init amba_init(void) +{ + return bus_register(&amba_bustype); +} + +postcore_initcall(amba_init); + +/* + * These are the device model conversion veneers; they convert the + * device model structures to our more specific structures. + */ +static int amba_probe(struct device *dev) +{ + struct amba_device *pcdev = to_amba_device(dev); + struct amba_driver *pcdrv = to_amba_driver(dev->driver); + struct amba_id *id; + + id = amba_lookup(pcdrv->id_table, pcdev); + + return pcdrv->probe(pcdev, id); +} + +static int amba_remove(struct device *dev) +{ + struct amba_driver *drv = to_amba_driver(dev->driver); + return drv->remove(to_amba_device(dev)); +} + +static void amba_shutdown(struct device *dev) +{ + struct amba_driver *drv = to_amba_driver(dev->driver); + drv->shutdown(to_amba_device(dev)); +} + +static int amba_suspend(struct device *dev, u32 state, u32 level) +{ + struct amba_driver *drv = to_amba_driver(dev->driver); + return drv->suspend(to_amba_device(dev), state, level); +} + +static int amba_resume(struct device *dev, u32 level) +{ + struct amba_driver *drv = to_amba_driver(dev->driver); + return drv->resume(to_amba_device(dev), level); +} + +/** + * amba_driver_register - register an AMBA device driver + * @drv: amba device driver structure + * + * Register an AMBA device driver with the Linux device model + * core. If devices pre-exist, the drivers probe function will + * be called. + */ +int amba_driver_register(struct amba_driver *drv) +{ + drv->drv.bus = &amba_bustype; + +#define SETFN(fn) if (drv->fn) drv->drv.fn = amba_##fn + SETFN(probe); + SETFN(remove); + SETFN(shutdown); + SETFN(suspend); + SETFN(resume); + + return driver_register(&drv->drv); +} + +/** + * amba_driver_unregister - remove an AMBA device driver + * @drv: AMBA device driver structure to remove + * + * Unregister an AMBA device driver from the Linux device + * model. The device model will call the drivers remove function + * for each device the device driver is currently handling. + */ +void amba_driver_unregister(struct amba_driver *drv) +{ + driver_unregister(&drv->drv); +} + + +static void amba_device_release(struct device *dev) +{ + struct amba_device *d = to_amba_device(dev); + + if (d->res.parent) + release_resource(&d->res); + kfree(d); +} + +static ssize_t show_id(struct device *_dev, char *buf) +{ + struct amba_device *dev = to_amba_device(_dev); + return sprintf(buf, "%08x\n", dev->periphid); +} +static DEVICE_ATTR(id, S_IRUGO, show_id, NULL); + +static ssize_t show_irq(struct device *_dev, char *buf) +{ + struct amba_device *dev = to_amba_device(_dev); + return sprintf(buf, "%u\n", dev->irq); +} +static DEVICE_ATTR(irq, S_IRUGO, show_irq, NULL); + +static ssize_t show_res(struct device *_dev, char *buf) +{ + struct amba_device *dev = to_amba_device(_dev); + return sprintf(buf, "\t%08lx\t%08lx\t%08lx\n", + dev->res.start, dev->res.end, dev->res.flags); +} +static DEVICE_ATTR(resource, S_IRUGO, show_res, NULL); + +/** + * amba_device_register - register an AMBA device + * @dev: AMBA device to register + * @parent: parent memory resource + * + * Setup the AMBA device, reading the cell ID if present. + * Claim the resource, and register the AMBA device with + * the Linux device manager. + */ +int amba_device_register(struct amba_device *dev, struct resource *parent) +{ + u32 pid, cid; + void *tmp; + int i, ret; + + dev->dev.release = amba_device_release; + dev->dev.bus = &amba_bustype; + dev->res.name = dev->dev.name; + + ret = request_resource(parent, &dev->res); + if (ret == 0) { + tmp = ioremap(dev->res.start, SZ_4K); + if (!tmp) { + ret = -ENOMEM; + goto out; + } + + for (pid = 0, i = 0; i < 4; i++) + pid |= (readl(tmp + 0xfe0 + 4 * i) & 255) << (i * 8); + for (cid = 0, i = 0; i < 4; i++) + cid |= (readl(tmp + 0xff0 + 4 * i) & 255) << (i * 8); + + iounmap(tmp); + + if (cid == 0xb105f00d) + dev->periphid = pid; + + if (dev->periphid) + snprintf(dev->dev.name, sizeof(dev->dev.name), + "AMBA PL%03X", + dev->periphid & 0xfff); + else + strlcpy(dev->dev.name, "AMBA unknown", + sizeof(dev->dev.name)); + + ret = device_register(&dev->dev); + if (ret == 0) { + device_create_file(&dev->dev, &dev_attr_id); + device_create_file(&dev->dev, &dev_attr_irq); + device_create_file(&dev->dev, &dev_attr_resource); + } else { + out: + release_resource(&dev->res); + } + } + return ret; +} + +/** + * amba_device_unregister - unregister an AMBA device + * @dev: AMBA device to remove + * + * Remove the specified AMBA device from the Linux device + * manager. All files associated with this object will be + * destroyed, and device drivers notified that the device has + * been removed. The AMBA device's resources including + * the amba_device structure will be freed once all + * references to it have been dropped. + */ +void amba_device_unregister(struct amba_device *dev) +{ + device_unregister(&dev->dev); +} + +EXPORT_SYMBOL(amba_driver_register); +EXPORT_SYMBOL(amba_driver_unregister); +EXPORT_SYMBOL(amba_device_register); +EXPORT_SYMBOL(amba_device_unregister); diff -urN orig/arch/arm/common/icst525.c linux/arch/arm/common/icst525.c --- orig/arch/arm/common/icst525.c Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/common/icst525.c Sat May 31 09:26:15 2003 @@ -0,0 +1,160 @@ +/* + * linux/arch/arm/common/icst525.c + * + * Copyright (C) 2003 Deep Blue Solutions, Ltd, 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. + * + * Support functions for calculating clocks/divisors for the ICST525 + * clock generators. See http://www.icst.com/ for more information + * on these devices. + */ +#include +#include + +#include + +/* + * Divisors for each OD setting. + */ +static unsigned char s2div[8] = { 10, 2, 8, 4, 5, 7, 9, 6 }; + +unsigned long icst525_khz(const struct icst525_params *p, struct icst525_vco vco) +{ + return p->ref * 2 * (vco.v + 8) / ((vco.r + 2) * s2div[vco.s]); +} + +EXPORT_SYMBOL(icst525_khz); + +/* + * Ascending divisor S values. + */ +static unsigned char idx2s[] = { 1, 3, 4, 7, 5, 2, 6, 0 }; + +struct icst525_vco +icst525_khz_to_vco(const struct icst525_params *p, unsigned long freq) +{ + struct icst525_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max }; + unsigned long f; + unsigned int i = 0, rd, best = (unsigned int)-1; + + /* + * First, find the PLL output divisor such + * that the PLL output is within spec. + */ + do { + f = freq * s2div[idx2s[i]]; + + /* + * f must be between 10MHz and + * 320MHz (5V) or 200MHz (3V) + */ + if (f > 10000 && f <= p->vco_max) + break; + } while (i < ARRAY_SIZE(idx2s)); + + if (i > ARRAY_SIZE(idx2s)) + return vco; + + vco.s = idx2s[i]; + + /* + * Now find the closest divisor combination + * which gives a PLL output of 'f'. + */ + for (rd = p->rd_min; rd <= p->rd_max; rd++) { + unsigned long fref_div, f_pll; + unsigned int vd; + int f_diff; + + fref_div = (2 * p->ref) / rd; + + vd = (f + fref_div / 2) / fref_div; + if (vd < p->vd_min || vd > p->vd_max) + continue; + + f_pll = fref_div * vd; + f_diff = f_pll - f; + if (f_diff < 0) + f_diff = -f_diff; + + if ((unsigned)f_diff < best) { + vco.v = vd - 8; + vco.r = rd - 2; + if (f_diff == 0) + break; + best = f_diff; + } + } + + return vco; +} + +EXPORT_SYMBOL(icst525_khz_to_vco); + +struct icst525_vco +icst525_ps_to_vco(const struct icst525_params *p, unsigned long period) +{ + struct icst525_vco vco = { .s = 1, .v = p->vd_max, .r = p->rd_max }; + unsigned long f, ps; + unsigned int i = 0, rd, best = (unsigned int)-1; + + ps = 1000000000UL / p->vco_max; + + /* + * First, find the PLL output divisor such + * that the PLL output is within spec. + */ + do { + f = period / s2div[idx2s[i]]; + + /* + * f must be between 10MHz and + * 320MHz (5V) or 200MHz (3V) + */ + if (f >= ps && f < 100000) + break; + } while (i < ARRAY_SIZE(idx2s)); + + if (i > ARRAY_SIZE(idx2s)) + return vco; + + vco.s = idx2s[i]; + + ps = 500000000UL / p->ref; + + /* + * Now find the closest divisor combination + * which gives a PLL output of 'f'. + */ + for (rd = p->rd_min; rd <= p->rd_max; rd++) { + unsigned long f_in_div, f_pll; + unsigned int vd; + int f_diff; + + f_in_div = ps * rd; + + vd = (f_in_div + f / 2) / f; + if (vd < p->vd_min || vd > p->vd_max) + continue; + + f_pll = (f_in_div + vd / 2) / vd; + f_diff = f_pll - f; + if (f_diff < 0) + f_diff = -f_diff; + + if ((unsigned)f_diff < best) { + vco.v = vd - 8; + vco.r = rd - 2; + if (f_diff == 0) + break; + best = f_diff; + } + } + + return vco; +} + +EXPORT_SYMBOL(icst525_ps_to_vco); diff -urN orig/arch/arm/common/platform.c linux/arch/arm/common/platform.c --- orig/arch/arm/common/platform.c Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/common/platform.c Sat May 31 15:50:45 2003 @@ -0,0 +1,35 @@ +#include +#include +#include + +int __init platform_add_device(struct platform_device *dev) +{ + int i; + + for (i = 0; i < dev->num_resources; i++) { + struct resource *r = &dev->resource[i]; + + r->name = dev->dev.name; + + if (r->flags & IORESOURCE_MEM && + request_resource(&iomem_resource, r)) { + printk(KERN_ERR + "%s%d: failed to claim resource %d\n", + dev->name, dev->id, i); + break; + } + } + if (i == dev->num_resources) + platform_device_register(dev); + return 0; +} + +int __init platform_add_devices(struct platform_device **devs, int num) +{ + int i; + + for (i = 0; i < num; i++) + platform_add_device(devs[i]); + + return 0; +} diff -urN orig/arch/arm/common/sa1111-ohci.c linux/arch/arm/common/sa1111-ohci.c --- orig/arch/arm/common/sa1111-ohci.c Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/common/sa1111-ohci.c Sun Nov 17 12:13:09 2002 @@ -0,0 +1,141 @@ +#include +#include +#include + +#include + +#ifdef CONFIG_USB_OHCI + +/* + * The SA-1111 errata says that the DMA hardware needs to be exercised + * before the clocks are turned on to work properly. This code does + * a tiny dma transfer to prime to hardware. + * + * What DMA errata? I've checked October 1999 and February 2001, both + * of which do not mention such a bug, let alone any details of this + * work-around. + */ +static void __init sa1111_dma_setup(void) +{ + dma_addr_t dma_buf; + void * vbuf; + + /* DMA init & setup */ + + /* WARNING: The SA-1111 L3 function is used as part of this + * SA-1111 DMA errata workaround. + * + * N.B., When the L3 function is enabled, it uses GPIO_B<4:5> + * and takes precedence over the PS/2 mouse and GPIO_B + * functions. Refer to "Intel StrongARM SA-1111 Microprocessor + * Companion Chip, Sect 10.2" for details. So this "fix" may + * "break" support of either PS/2 mouse or GPIO_B if + * precautions are not taken to avoid collisions in + * configuration and use of these pins. AFAIK, no precautions + * are taken at this time. So it is likely that the action + * taken here may cause problems in PS/2 mouse and/or GPIO_B + * pin use elsewhere. + * + * But wait, there's more... What we're doing here is + * obviously altogether a bad idea. We're indiscrimanately bit + * flipping config for a few different functions here which + * are "owned" by other drivers. This needs to be handled + * better than it is being done here at this time. */ + + /* prime the dma engine with a tiny dma */ + SKPCR |= SKPCR_I2SCLKEN; + SKAUD |= SKPCR_L3CLKEN | SKPCR_SCLKEN; + + SACR0 |= 0x00003305; + SACR1 = 0x00000000; + + /* + * We need memory below 1MB. + * NOTE: consistent_alloc gives you some random virtual + * address as its return value, and the DMA address via + * the dma_addr_t pointer. + */ + vbuf = consistent_alloc(GFP_KERNEL | GFP_DMA, 4, &dma_buf); + + SADTSA = (unsigned long)dma_buf; + SADTCA = 4; + + SADTCS |= 0x00000011; + SKPCR |= SKPCR_DCLKEN; + + /* wait */ + udelay(100); + + /* clear reserved but, then disable SAC */ + SACR0 &= ~(0x00000002); + SACR0 &= ~(0x00000001); + + /* toggle bit clock direction */ + SACR0 |= 0x00000004; + SACR0 &= ~(0x00000004); + + SKAUD &= ~(SKPCR_L3CLKEN | SKPCR_SCLKEN); + + SKPCR &= ~SKPCR_I2SCLKEN; + + consistent_free(vbuf, 4, dma_buf); +} + +/* + * reset the SA-1111 usb controller and turn on it's clocks + */ +int __init sa1111_ohci_hcd_init(void) +{ + unsigned int usb_reset = 0; + + if (machine_is_xp860() || + machine_is_assabet() || + machine_is_pfs168() || + machine_is_badge4()) + usb_reset = USB_RESET_PWRSENSELOW | USB_RESET_PWRCTRLLOW; + + /* + * turn on USB clocks + */ + SKPCR |= SKPCR_UCLKEN; + udelay(100); + + /* + * Force USB reset + */ + USB_RESET = USB_RESET_FORCEIFRESET; + USB_RESET |= USB_RESET_FORCEHCRESET; + udelay(100); + + /* + * Take out of reset + */ + USB_RESET = 0; + + /* + * set power sense and control lines (this from the diags code) + */ + USB_RESET = usb_reset; + + /* + * Huh? This is a _read only_ register --rmk + */ + USB_STATUS = 0; + + udelay(10); + + /* + * compensate for dma bug + */ + sa1111_dma_setup(); + + return 0; +} + +void sa1111_ohci_hcd_cleanup(void) +{ + /* turn the USB clock off */ + SKPCR &= ~SKPCR_UCLKEN; +} + +#endif /* CONFIG_USB_OHCI */ diff -urN orig/arch/arm/common/sa1111.c linux/arch/arm/common/sa1111.c --- orig/arch/arm/common/sa1111.c Tue May 27 10:04:13 2003 +++ linux/arch/arm/common/sa1111.c Tue May 27 23:12:34 2003 @@ -42,7 +42,7 @@ */ struct sa1111 { struct device *dev; - struct resource res; + unsigned long phys; int irq; spinlock_t lock; void *base; @@ -60,7 +60,6 @@ }, .skpcr_mask = SKPCR_UCLKEN, .devid = SA1111_DEVID_USB, - .dma_mask = 0xffffffffLL, .irq = { IRQ_USBPWR, IRQ_HCIM, @@ -470,6 +469,17 @@ #ifdef CONFIG_ARCH_SA1100 +static u32 sa1111_dma_mask[] = { + ~0, + ~(1 << 20), + ~(1 << 23), + ~(1 << 24), + ~(1 << 25), + ~(1 << 20), + ~(1 << 20), + 0, +}; + /* * Configure the SA1111 shared memory controller. */ @@ -483,26 +493,43 @@ smcr |= SMCR_CLAT; sa1111_writel(smcr, sachip->base + SA1111_SMCR); + + /* + * Now clear the bits in the DMA mask to work around the SA1111 + * DMA erratum (Intel StrongARM SA-1111 Microprocessor Companion + * Chip Specification Update, June 2000, Erratum #7). + */ + if (sachip->dev->dma_mask) + *sachip->dev->dma_mask &= sa1111_dma_mask[drac >> 2]; } #endif static void -sa1111_init_one_child(struct sa1111 *sachip, struct sa1111_dev *sadev, unsigned int offset) +sa1111_init_one_child(struct sa1111 *sachip, struct resource *parent, + struct sa1111_dev *sadev, unsigned int offset) { snprintf(sadev->dev.bus_id, sizeof(sadev->dev.bus_id), "%4.4x", offset); + /* + * If the parent device has a DMA mask associated with it, + * propagate it down to the children. + */ + if (sachip->dev->dma_mask) { + sadev->dma_mask = *sachip->dev->dma_mask; + sadev->dev.dma_mask = &sadev->dma_mask; + } + sadev->dev.parent = sachip->dev; sadev->dev.bus = &sa1111_bus_type; - sadev->dev.dma_mask = &sadev->dma_mask; - sadev->res.start = sachip->res.start + offset; + sadev->res.start = sachip->phys + offset; sadev->res.end = sadev->res.start + 511; sadev->res.name = sadev->dev.name; sadev->res.flags = IORESOURCE_MEM; sadev->mapbase = sachip->base + offset; - if (request_resource(&sachip->res, &sadev->res)) { + if (request_resource(parent, &sadev->res)) { printk("SA1111: failed to allocate resource for %s\n", sadev->res.name); return; @@ -524,7 +551,7 @@ * %0 successful. */ static int __init -__sa1111_probe(struct device *me, unsigned long phys_addr, int irq) +__sa1111_probe(struct device *me, struct resource *mem, int irq) { struct sa1111 *sachip; unsigned long id; @@ -542,24 +569,17 @@ sachip->dev = me; dev_set_drvdata(sachip->dev, sachip); - sachip->res.name = me->name; - sachip->res.start = phys_addr; - sachip->res.end = phys_addr + 0x2000; + sachip->phys = mem->start; sachip->irq = irq; - if (request_resource(&iomem_resource, &sachip->res)) { - ret = -EBUSY; - goto out; - } - /* * Map the whole region. This also maps the * registers for our children. */ - sachip->base = ioremap(phys_addr, PAGE_SIZE * 2); + sachip->base = ioremap(mem->start, PAGE_SIZE * 2); if (!sachip->base) { ret = -ENOMEM; - goto release; + goto out; } /* @@ -611,9 +631,11 @@ * The interrupt controller must be initialised before any * other device to ensure that the interrupts are available. */ - int_dev.irq[0] = irq; - sa1111_init_one_child(sachip, &int_dev, SA1111_INTC); - sa1111_init_irq(&int_dev); + if (irq != NO_IRQ) { + int_dev.irq[0] = irq; + sa1111_init_one_child(sachip, mem, &int_dev, SA1111_INTC); + sa1111_init_irq(&int_dev); + } g_sa1111 = sachip; @@ -626,14 +648,12 @@ for (i = 0; i < ARRAY_SIZE(devs); i++) if (has_devs & (1 << i)) - sa1111_init_one_child(sachip, devs[i], dev_offset[i]); + sa1111_init_one_child(sachip, mem, devs[i], dev_offset[i]); return 0; unmap: iounmap(sachip->base); - release: - release_resource(&sachip->res); out: kfree(sachip); return ret; @@ -649,7 +669,6 @@ } iounmap(sachip->base); - release_resource(&sachip->res); kfree(sachip); } @@ -874,7 +893,17 @@ static int sa1111_probe(struct device *dev) { - return -ENODEV; + struct platform_device *pdev = to_platform_device(dev); + struct resource *mem = NULL, *irq = NULL; + int i; + + for (i = 0; i < pdev->num_resources; i++) { + if (pdev->resource[i].flags & IORESOURCE_MEM) + mem = &pdev->resource[i]; + if (pdev->resource[i].flags & IORESOURCE_IRQ) + irq = &pdev->resource[i]; + } + return __sa1111_probe(dev, mem, irq ? irq->start : NO_IRQ); } static int sa1111_remove(struct device *dev) @@ -903,7 +932,7 @@ */ static struct device_driver sa1111_device_driver = { .name = "sa1111", - .bus = &system_bus_type, + .bus = &platform_bus_type, .probe = sa1111_probe, .remove = sa1111_remove, .suspend = sa1111_suspend, @@ -921,29 +950,6 @@ arch_initcall(sa1111_driver_init); -static struct sys_device sa1111_device = { - .name = "SA1111", - .id = 0, - .root = NULL, - .dev = { - .name = "Intel Corporation SA1111", - .driver = &sa1111_device_driver, - }, -}; - -int sa1111_init(unsigned long phys, unsigned int irq) -{ - int ret; - - snprintf(sa1111_device.dev.bus_id, sizeof(sa1111_device.dev.bus_id), "%8.8lx", phys); - - ret = sys_device_register(&sa1111_device); - if (ret) - printk("sa1111 device_register failed: %d\n", ret); - - return __sa1111_probe(&sa1111_device.dev, phys, irq); -} - /* * Get the parent device driver (us) structure * from a child function device @@ -1055,6 +1061,83 @@ return __sa1111_pll_clock(sachip) / (256 * div); } +/** + * sa1111_pwm_enable - enable a PWM channel + * @channel: PWM channel + */ +void sa1111_pwm_enable(int channel) +{ + struct sa1111 *sachip = g_sa1111; + unsigned long flags; + unsigned int val; + char *reg = sachip->base; + + switch (channel) { + case 0: + reg += SA1111_SKPEN0; + break; + case 1: + reg += SA1111_SKPEN1; + break; + default: + return; + } + sa1111_writel(1, reg); + + spin_lock_irqsave(&sachip->lock, flags); + val = sa1111_readl(sachip->base + SA1111_SKPCR); + sa1111_writel(val | SKPCR_PWMCLKEN, sachip->base + SA1111_SKPCR); + spin_unlock_irqrestore(&sachip->lock, flags); +} + +/** + * sa1111_pwm_disable - disable a PWM channel + * @channel: PWM channel + */ +void sa1111_pwm_disable(int channel) +{ + struct sa1111 *sachip = g_sa1111; + char *reg = sachip->base; + + switch (channel) { + case 0: + reg += SA1111_SKPEN0; + break; + case 1: + reg += SA1111_SKPEN1; + break; + default: + return; + } + sa1111_writel(0, reg); +} + +/** + * sa1111_pwm_dutycycle - set the PWM duty cycle + * @channel: PWM channel + * @duty: duty cycle, 0 to 127 + * + * Note: the duty cycle is preserved over a sleep, + * however, the enable is NOT preserved. + */ +void sa1111_pwm_dutycycle(int channel, int duty) +{ + struct sa1111 *sachip = g_sa1111; + char *reg = sachip->base; + + switch (channel) { + case 0: + reg += SA1111_SKPWM0; + break; + case 1: + reg += SA1111_SKPWM1; + break; + default: + return; + } + sa1111_writel(duty, reg); +} + /* * Individual device operations. */ @@ -1121,6 +1204,9 @@ EXPORT_SYMBOL(sa1111_select_audio_mode); EXPORT_SYMBOL(sa1111_set_audio_rate); EXPORT_SYMBOL(sa1111_get_audio_rate); +EXPORT_SYMBOL(sa1111_pwm_enable); /* not happy about the interface */ +EXPORT_SYMBOL(sa1111_pwm_disable); /* not happy about the interface */ +EXPORT_SYMBOL(sa1111_pwm_dutycycle); /* not happy about the interface */ EXPORT_SYMBOL(sa1111_enable_device); EXPORT_SYMBOL(sa1111_disable_device); EXPORT_SYMBOL(sa1111_pll_clock); diff -urN orig/arch/arm/fastfpe/CPDO.S linux/arch/arm/fastfpe/CPDO.S --- orig/arch/arm/fastfpe/CPDO.S Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/fastfpe/CPDO.S Thu Dec 13 11:23:14 2001 @@ -0,0 +1,682 @@ +/* +The FP structure has 4 words reserved for each register, the first is used just +for the sign in bit 31, the second and third are for the mantissa (unsigned +integer, high 32 bit first) and the fourth is the exponent (signed integer). +The mantissa is always normalized. + +If the exponent is 0x80000000, that is the most negative value, the number +represented is 0 and both mantissa words are also 0. + +If the exponent is 0x7fffffff, that is the biggest positive value, the number +represented is infinity if the high 32 mantissa bit are also 0, otherwise it is +a NaN. The low 32 mantissa bit are 0 if the number represented is infinity. + +Decimal and packed decimal numbers are not supported yet. + +The parameters to these functions are r0=destination pointer, r1 and r2 +source pointers. r4 is the instruction. They may use r0-r8 and r14. They return +to fastfpe_next, except CPDO_rnf_core which expects the return address in r14. +*/ + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_adf +CPDO_adf: + ldmia r1,{r1,r3,r5,r7} + ldmia r2,{r2,r4,r6,r8} + + cmp r7,#0x7fffffff + cmpne r8,#0x7fffffff + beq CPDO_adf_extra + + cmp r1,r2 + bne CPDO_suf_s + +CPDO_adf_s: + subs r2,r7,r8 + bge CPDO_adf_2nd + + mov r7,r8 + rsb r2,r2,#0 + cmp r2,#32 + ble CPDO_adf_1st2 + + sub r2,r2,#32 + cmp r2,#32 + movgt r2,#32 + mov r5,r3,lsr r2 + mov r3,#0 + b CPDO_adf_add + +CPDO_adf_1st2: + rsb r8,r2,#32 + mov r5,r5,lsr r2 + orr r5,r5,r3,lsl r8 + mov r3,r3,lsr r2 @ 1. op normalized + b CPDO_adf_add + +CPDO_adf_2nd: + cmp r2,#32 + ble CPDO_adf_2nd2 + + sub r2,r2,#32 + cmp r2,#32 + movgt r2,#32 + mov r6,r4,lsr r2 + mov r4,#0 + b CPDO_adf_add + +CPDO_adf_2nd2: + rsb r8,r2,#32 + mov r6,r6,lsr r2 + orr r6,r6,r4,lsl r8 + mov r4,r4,lsr r2 @ 2. op normalized + +CPDO_adf_add: + adds r5,r5,r6 + adcs r3,r3,r4 @ do addition + bcc CPDO_adf_end + + add r7,r7,#1 + movs r3,r3,rrx + mov r5,r5,rrx @ correct for overflow + +CPDO_adf_end: + cmp r7,#0x20000000 + bge CPDO_inf + + stmia r0,{r1,r3,r5,r7} + b fastfpe_next + +CPDO_adf_extra: + cmp r7,#0x7fffffff @ was it the 1st ? + bne CPDO_infnan_2 @ no it was the 2nd + cmp r8,#0x7fffffff @ if 1st, 2nd too ? + bne CPDO_infnan_1 @ no only 1st + cmp r3,#0 + cmpeq r4,#0 + bne CPDO_nan_12 + b CPDO_inf + +/*---------------------------------------------------------------------------*/ + +CPDO_infnan_1: + stmia r0,{r1,r3,r5,r7} + b fastfpe_next + +CPDO_infnan_2: + stmia r0,{r2,r4,r6,r8} + b fastfpe_next + +CPDO_nan_12: + orr r2,r3,r4 + b CPDO_inf_1 + +CPDO_nan: + mov r2,#0x40000000 @ create non signalling NaN + b CPDO_inf_1 + +CPDO_inf: + mov r2,#0 +CPDO_inf_1: + mov r3,#0 + mov r4,#0x7fffffff +CPDO_store_1234: + stmia r0,{r1,r2,r3,r4} + b fastfpe_next + +CPDO_zero: + mov r1,#0 +CPDO_zero_1: + mov r2,#0 + mov r3,#0 + mov r4,#0x80000000 + stmia r0,{r1,r2,r3,r4} + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_suf +CPDO_suf: + ldmia r1,{r1,r3,r5,r7} + ldmia r2,{r2,r4,r6,r8} + +CPDO_suf_l: + cmp r7,#0x7fffffff + cmpne r8,#0x7fffffff + beq CPDO_suf_extra + + cmp r1,r2 + bne CPDO_adf_s + +CPDO_suf_s: + subs r2,r7,r8 @ determine greater number + bgt CPDO_suf_2nd @ first number is greater + blt CPDO_suf_1st @ second number is greater + cmp r3,r4 @ also mantissa is important + cmpeq r5,r6 + bhi CPDO_suf_2nd @ first number is greater + beq CPDO_zero + +CPDO_suf_1st: + eor r1,r1,#0x80000000 @ second number is greater, invert sign + mov r7,r8 + rsb r2,r2,#0 + cmp r2,#32 + ble CPDO_suf_1st2 + + sub r2,r2,#32 + cmp r2,#32 + movgt r2,#32 + mov r5,r3,lsr r2 + mov r3,#0 + b CPDO_suf_1st_sub + +CPDO_suf_1st2: + rsb r8,r2,#32 + mov r5,r5,lsr r2 + orr r5,r5,r3,lsl r8 + mov r3,r3,lsr r2 @ 1. op normalized + +CPDO_suf_1st_sub: + subs r5,r6,r5 @ do subtraction + sbc r3,r4,r3 + b CPDO_suf_norm + +CPDO_suf_2nd: + cmp r2,#32 + ble CPDO_suf_2nd2 + + sub r2,r2,#32 + cmp r2,#32 + movgt r2,#32 + mov r6,r4,lsr r2 + mov r4,#0 + b CPDO_suf_2nd_sub + +CPDO_suf_2nd2: + rsb r8,r2,#32 + mov r6,r6,lsr r2 + orr r6,r6,r4,lsl r8 + mov r4,r4,lsr r2 @ 2. op normalized + +CPDO_suf_2nd_sub: + subs r5,r5,r6 + sbc r3,r3,r4 @ do subtraction + +CPDO_suf_norm: + teq r3,#0 @ normalize 32bit + moveq r3,r5 + moveq r5,#0 + subeq r7,r7,#32 + + cmp r3,#0x00010000 @ 16bit + movcc r3,r3,lsl#16 + orrcc r3,r3,r5,lsr#16 + movcc r5,r5,lsl#16 + subcc r7,r7,#16 + + cmp r3,#0x01000000 @ 8bit + movcc r3,r3,lsl#8 + orrcc r3,r3,r5,lsr#24 + movcc r5,r5,lsl#8 + subcc r7,r7,#8 + + cmp r3,#0x10000000 @ 4bit + movcc r3,r3,lsl#4 + orrcc r3,r3,r5,lsr#28 + movcc r5,r5,lsl#4 + subcc r7,r7,#4 + + cmp r3,#0x40000000 @ 2bit + movcc r3,r3,lsl#2 + orrcc r3,r3,r5,lsr#30 + movcc r5,r5,lsl#2 + subcc r7,r7,#2 + + cmp r3,#0x80000000 @ 1bit + movcc r3,r3,lsl#1 + orrcc r3,r3,r5,lsr#31 + movcc r5,r5,lsl#1 + subcc r7,r7,#1 + + cmp r7,#0xe0000000 + ble CPDO_zero_1 + + stmia r0,{r1,r3,r5,r7} + b fastfpe_next + +CPDO_suf_extra: + cmp r7,#0x7fffffff @ was it the 1st ? + eorne r2,r2,#0x80000000 @ change sign, might have been INF + bne CPDO_infnan_2 @ no it was the 2nd + cmp r8,#0x7fffffff @ if 1st, 2nd too ? + bne CPDO_infnan_1 @ no only 1st + cmp r3,#0 + cmpeq r4,#0 + bne CPDO_nan_12 + b CPDO_nan @ here is difference with adf ! + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_rsf +CPDO_rsf: + mov r3,r2 + ldmia r1,{r2,r4,r6,r8} + ldmia r3,{r1,r3,r5,r7} + b CPDO_suf_l + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_muf +CPDO_muf: + ldmia r1,{r1,r3,r5,r7} + ldmia r2,{r2,r4,r6,r8} + + cmp r7,#0x7fffffff + cmpne r8,#0x7fffffff + beq CPDO_muf_extra + + eor r1,r1,r2 + adds r8,r7,r8 + bvs CPDO_zero_1 + + umull r7,r2,r3,r4 + umull r14,r3,r6,r3 + adds r7,r7,r3 @ r2|r7|r14 = r2|r7|#0 + #0|r3|r14 + adc r2,r2,#0 + umull r4,r3,r5,r4 + adds r14,r14,r4 @ r2|r7|r14 += #0|r3|r4 + adcs r7,r7,r3 + adc r2,r2,#0 + umull r4,r3,r5,r6 + adds r14,r14,r3 @ r2|r7|r14 += #0|#0|r3 + adcs r7,r7,#0 + adcs r2,r2,#0 + + bpl CPDO_muf_norm + + add r8,r8,#1 + b CPDO_muf_end + +CPDO_muf_norm: + adds r14,r14,r14 + adcs r7,r7,r7 + adcs r2,r2,r2 + +CPDO_muf_end: + cmp r8,#0x20000000 + bge CPDO_inf + cmp r8,#0xe0000000 + ble CPDO_zero_1 + stmia r0,{r1,r2,r7,r8} + b fastfpe_next + +CPDO_muf_extra: + cmp r7,#0x7fffffff @ was it the first? + bne CPDO_muf_extra_2nd @ no, so it was the second + cmp r8,#0x7fffffff @ yes, second too? + bne CPDO_muf_extra_1st @ no, only first + orr r3,r3,r4 @ if both inf -> inf, otherwise nan + eor r1,r1,r2 @ sign for the inf case + b CPDO_infnan_1 + +CPDO_muf_extra_1st: + cmp r3,#0 @ is it a nan? + bne CPDO_infnan_1 + cmp r8,#0x80000000 @ is the second 0? + beq CPDO_nan + eor r1,r1,r2 @ correct sign for inf + b CPDO_inf + +CPDO_muf_extra_2nd: + cmp r4,#0 @ is it a nan? + bne CPDO_infnan_2 + cmp r7,#0x80000000 @ is the first 0? + beq CPDO_nan + eor r1,r1,r2 @ correct sign for inf + b CPDO_inf + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_dvf +CPDO_dvf: + ldmia r1,{r1,r3,r5,r7} + ldmia r2,{r2,r4,r6,r8} + +CPDO_dvf_l: + cmp r7,#0x7fffffff + cmpne r8,#0x7fffffff + beq CPDO_dvf_extra + cmp r8,#0x80000000 + beq CPDO_dvf_by0 + + eor r1,r1,r2 + cmp r7,#0x80000000 + beq CPDO_zero_1 + + sub r8,r7,r8 + + mov r2,#0 + mov r7,#1 + + cmp r3,r4 + cmpeq r5,r6 + bcs CPDO_dvf_loop_ + + sub r8,r8,#1 + +CPDO_dvf_loop: + adds r5,r5,r5 + adcs r3,r3,r3 + bcs CPDO_dvf_anyway +CPDO_dvf_loop_: + subs r5,r5,r6 + sbcs r3,r3,r4 + bcs CPDO_dvf_okay + + adds r5,r5,r6 + adc r3,r3,r4 + adds r7,r7,r7 + adcs r2,r2,r2 + bcc CPDO_dvf_loop + b CPDO_dvf_end + +CPDO_dvf_anyway: + adcs r7,r7,r7 + adcs r2,r2,r2 + bcs CPDO_dvf_end + subs r5,r5,r6 + sbc r3,r3,r4 + b CPDO_dvf_loop + +CPDO_dvf_okay: + adcs r7,r7,r7 + adcs r2,r2,r2 + bcc CPDO_dvf_loop + +CPDO_dvf_end: + b CPDO_muf_end + +CPDO_dvf_by0: + cmp R7,#0x80000000 + beq CPDO_nan @ first also 0 -> nan + eor r1,r1,r2 @ otherwise calculatesign for inf + b CPDO_inf + +CPDO_dvf_extra: + cmp r7,#0x7fffffff @ was it the first? + bne CPDO_dvf_extra_2nd @ no, so it was the second + cmp r8,#0x7fffffff @ yes, second too? + bne CPDO_dvf_extra_1st @ no, only first + orrs r3,r3,r4 + beq CPDO_nan @ if both inf -> create nan + b CPDO_nan_12 @ otherwise keep nan + +CPDO_dvf_extra_1st: + eor r1,r1,r2 @ correct sign for inf + b CPDO_infnan_1 + +CPDO_dvf_extra_2nd: + cmp r4,#0 @ is it a nan? + bne CPDO_infnan_2 + eor r1,r1,r2 @ correct sign for zero + b CPDO_zero_1 + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_rdf +CPDO_rdf: + mov r3,r2 + ldmia r1,{r2,r4,r6,r8} + ldmia r3,{r1,r3,r5,r7} + b CPDO_dvf_l + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_rmf +CPDO_rmf: + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_mvf +CPDO_mvf: + ldmia r2,{r1,r2,r3,r4} + stmia r0,{r1,r2,r3,r4} + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_mnf +CPDO_mnf: + ldmia r2,{r1,r2,r3,r4} + eor r1,r1,#0x80000000 + stmia r0,{r1,r2,r3,r4} + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_abs +CPDO_abs: + ldmia r2,{r1,r2,r3,r4} + bic r1,r1,#0x80000000 + stmia r0,{r1,r2,r3,r4} + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_sqt +CPDO_sqt: + ldmia r2,{r1,r2,r3,r4} + cmp r1,#0 + bne CPDO_nan + cmp r4,#0x7fffffff + beq CPDO_store_1234 + + tst r4,r4,lsr#1 @carry=exponent bit 0 + bcc CPDO_sqt_exponenteven + adds r3,r3,r3 + adcs r2,r2,r2 @carry is needed in loop! +CPDO_sqt_exponenteven: + mov r4,r4,asr #1 + str r4,[r0,#12] + + mov r4,#0x80000000 + mov r5,#0 + sub r2,r2,#0x80000000 + + mov r8,#0x40000000 + mov r14,#0x80000000 + + mov r1,#1 + b CPDO_sqt_loop1_first +CPDO_sqt_loop1: + adds r3,r3,r3 + adcs r2,r2,r2 +CPDO_sqt_loop1_first: + add r6,r4,r8,lsr r1 @r7 const = r5 + bcs CPDO_sqt_loop1_1 + cmp r2,r6 + cmpeq r3,r5 @r5 for r7 + bcc CPDO_sqt_loop1_0 +CPDO_sqt_loop1_1: + orr r4,r4,r14,lsr r1 + subs r3,r3,r5 @r5 for r7 + sbc r2,r2,r6 +CPDO_sqt_loop1_0: + add r1,r1,#1 + cmp r1,#30 + ble CPDO_sqt_loop1 + + adds r3,r3,r3 + adcs r2,r2,r2 + bcs CPDO_sqt_between_1 + adds r7,r5,#0x80000000 + adc r6,r4,#0 + cmp r2,r6 + cmpeq r3,r7 + bcc CPDO_sqt_between_0 +CPDO_sqt_between_1: + orr r4,r4,#0x00000001 + subs r3,r3,r5 + sbc r2,r2,r4 + subs r3,r3,#0x80000000 + sbc r2,r2,#0 +CPDO_sqt_between_0: + mov r1,#0 + +CPDO_sqt_loop2: + adds r3,r3,r3 + adcs r2,r2,r2 + bcs CPDO_sqt_loop2_1 + adds r7,r5,r8,lsr r1 + adc r6,r4,#0 + cmp r2,r6 + cmpeq r3,r7 + bcc CPDO_sqt_loop2_0 +CPDO_sqt_loop2_1: + orr r5,r5,r14,lsr r1 + subs r3,r3,r5 + sbc r2,r2,r4 + subs r3,r3,r8,lsr r1 + sbc r2,r2,#0 +CPDO_sqt_loop2_0: + add r1,r1,#1 + cmp r1,#30 + ble CPDO_sqt_loop2 + + adds r3,r3,r3 + adcs r2,r2,r2 + bcs CPDO_sqt_after_1 + cmp r2,r6 + cmpeq r3,r7 + bcc CPDO_sqt_after_0 +CPDO_sqt_after_1: + orr r5,r5,#0x00000001 +CPDO_sqt_after_0: + + mov r1,#0 + stmia r0,{r1,r4,r5} + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_rnd +CPDO_rnd: + ldmia r2,{r1,r2,r3,r5} + bl CPDO_rnd_core + +CPDO_rnd_store: + stmia r0,{r1,r2,r3,r5} + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDO_rnd_core +CPDO_rnd_core: + and r4,r4,#0x00000060 + add pc,pc,r4,lsr#3 + mov r0,r0 + b CPDO_rnd_N + b CPDO_rnd_P + b CPDO_rnd_M + b CPDO_rnd_Z + +CPDO_rnd_N: + cmp r5,#-1 + blt CPDO_rnd_zero + cmp r5,#63 + movge pc,r14 + mov r4,#0x40000000 + cmp r5,#31 + bge CPDO_rnd_N_2 + + adds r2,r2,r4,lsr r5 + bcc CPDO_rnd_end + b CPDO_rnd_end_norm + +CPDO_rnd_N_2: +CPDO_rnd_P_2: + sub r6,r5,#32 + adds r3,r3,r4,ror r6 @ror ist needed to handle a -1 correctly + adcs r2,r2,#0 + bcc CPDO_rnd_end + b CPDO_rnd_end_norm + +CPDO_rnd_P: + tst r1,#0x80000000 + bne CPDO_rnd_M_entry +CPDO_rnd_P_entry: + cmp r5,#0 + blt CPDO_rnd_P_small + cmp r5,#63 + movge pc,r14 + mov r4,#0x7fffffff + cmp r5,#32 + bge CPDO_rnd_P_2 + + adds r3,r3,#0xffffffff + adcs r2,r2,r4,lsr r5 + bcc CPDO_rnd_end + b CPDO_rnd_end_norm + +CPDO_rnd_P_small: + cmp r5,#0x80000000 + moveq pc,r14 + b CPDO_rnd_one + +CPDO_rnd_M: + tst r1,#0x80000000 + bne CPDO_rnd_P_entry +CPDO_rnd_M_entry: + cmp r5,#0 + blt CPDO_rnd_zero + cmp r5,#63 + movge pc,r14 + + b CPDO_rnd_end + +CPDO_rnd_Z: + cmp r5,#0 + blt CPDO_rnd_zero + cmp r5,#63 + movge pc,r14 + b CPDO_rnd_end + +CPDO_rnd_end_norm: + add r5,r5,#1 + movs r2,r2,rrx + mov r3,r3,rrx +CPDO_rnd_end: + rsbs r4,r5,#31 + bmi CPDO_rnd_end_2 + mov r3,#0 + mov r2,r2,lsr r4 + mov r2,r2,lsl r4 + mov pc,r14 + +CPDO_rnd_end_2: + rsb r4,r5,#63 + mov r3,r3,lsr r4 + mov r3,r3,lsl r4 + mov pc,r14 + +CPDO_rnd_one: + mov r2,#0x80000000 + mov r3,#0 + mov r5,#0 + mov pc,r14 + +CPDO_rnd_zero: + mov r1,#0 + mov r2,#0 + mov r3,#0 + mov r5,#0x80000000 + mov pc,r14 + +/*---------------------------------------------------------------------------*/ diff -urN orig/arch/arm/fastfpe/CPDT.S linux/arch/arm/fastfpe/CPDT.S --- orig/arch/arm/fastfpe/CPDT.S Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/fastfpe/CPDT.S Thu Oct 24 13:58:23 2002 @@ -0,0 +1,430 @@ +/* +The FP structure has 4 words reserved for each register, the first is used just +for the sign in bit 31, the second and third are for the mantissa (unsigned +integer, high 32 bit first) and the fourth is the exponent (signed integer). +The mantissa is always normalized. + +If the exponent is 0x80000000, that is the most negative value, the number +represented is 0 and both mantissa words are also 0. + +If the exponent is 0x7fffffff, that is the biggest positive value, the number +represented is infinity if the high 32 mantissa bit are also 0, otherwise it is +a NaN. The low 32 mantissa bit are 0 if the number represented is infinity. + +Decimal and packed decimal numbers are not supported yet. +*/ + +/*---------------------------------------------------------------------------*/ + + .globl CPDT_load_single +CPDT_load_single: + ldr r1,[r6] + + and r2,r1,#0x80000000 @ r2 = sign + + mov r5,r1,lsr#23 + bics r5,r5,#0x100 + beq CPDT_ls_e0 @ exponent = 0; zero/denormalized + teq r5,#255 + beq CPDT_ls_e255 @ exponent = 255; infinity/NaN + + sub r5,r5,#127 @ r5 = exponent, remove normalized bias + + mov r3,r1,lsl#8 + orr r3,r3,#0x80000000 + mov r4,#0 @ r3,r4 = mantissa + + stmia r0,{r2-r5} + b fastfpe_next + +CPDT_ls_e0: + movs r3,r1,lsl#9 + beq CPDT_load_zero + + mov r5,#-127 + +CPDT_ls_e0_norm: + tst r3,#0x80000000 + subeq r5,r5,#1 + moveq r3,r3,lsl#1 + beq CPDT_ls_e0_norm + + mov r4,#0 + stmia r0,{r2-r5} + b fastfpe_next + +CPDT_ls_e255: + mov r3,r1,lsl#9 + mov r4,#0 + mov r5,#0x7fffffff + stmia r0,{r2-r5} + b fastfpe_next + +CPDT_load_zero: + mov r3,#0 + mov r4,#0 + mov r5,#0x80000000 + stmia r0,{r2-r5} + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDT_load_double +CPDT_load_double: + ldr r1,[r6] + ldr r6,[r6,#4] + + and r2,r1,#0x80000000 @ r2 = sign + + mov r5,r1,lsr#20 + bics r5,r5,#0x800 + beq CPDT_ld_e0 @ exponent = 0; zero/denormalized + add r4,r5,#1 + teq r4,#2048 + beq CPDT_ld_e2047 @ exponent = 2047; infinity/NaN + + add r5,r5,#1 + sub r5,r5,#1024 @ r5 = exponent, remove normalized bias + + mov r3,r1,lsl#11 + orr r3,r3,#0x80000000 + orr r3,r3,r6,lsr #21 + mov r4,r6,lsl#11 @ r3,r4 = mantissa + + stmia r0,{r2-r5} + b fastfpe_next + +CPDT_ld_e0: + mov r3,r1,lsl#12 + orr r3,r3,r6,lsr#20 + movs r4,r6,lsl#12 + teqeq r3,#0 + beq CPDT_load_zero + + mov r5,#1 + sub r5,r5,#1024 + +CPDT_ld_e0_norm: + tst r3,#0x80000000 + subeq r5,r5,#1 + moveqs r4,r4,lsl#1 + adceq r3,r3,r3 + beq CPDT_ld_e0_norm + + stmia r0,{r2-r5} + b fastfpe_next + +CPDT_ld_e2047: + mov r3,r1,lsl#12 + orr r3,r3,r6,lsr#1 + bic r6,r6,#0x80000000 + orr r3,r3,r6 @ to get all fraction bits ! + mov r4,#0 + mov r5,#0x7fffffff + stmia r0,{r2-r5} + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDT_load_extended +CPDT_load_extended: + ldr r1,[r6] + ldr r3,[r6,#4] + ldr r4,[r6,#8] + + and r2,r1,#0x80000000 + bics r5,r1,#0x80000000 + beq CPDT_le_e0 + add r1,r5,#1 + teq r4,#32768 + beq CPDT_le_e32767 + + add r5,r5,#1 + sub r5,r5,#16384 + + stmia r0,{r2-r5} + b fastfpe_next + +CPDT_le_e0: + teq r3,#0 + teqeq r4,#0 + beq CPDT_load_zero + + mov r5,#2 + sub r5,r5,#16384 + b CPDT_ld_e0_norm + +CPDT_le_e32767: + mov r3,r3,lsl#1 + orr r3,r3,r4,lsr#1 + bic r4,r4,#0x80000000 + orr r3,r3,r4 + mov r5,#0x7fffffff + stmia r0,{r2-r5} + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDT_load_decimal +CPDT_load_decimal: + + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDT_store_single +CPDT_store_single: + ldmia r0,{r1-r4} + + cmp r4,#-127 + ble CPDT_ss_e0 + cmp r4,#128 + bge CPDT_ss_e255 + + adds r2,r2,#1<<7 @ round to nearest + bcs CPDT_ss_rnd_ovfl @ very very seldom taken + +CPDT_ss_store: + add r4,r4,#127 + orr r1,r1,r4,lsl#23 + + bic r2,r2,#0x80000000 + orr r1,r1,r2,lsr#8 + + str r1,[r6] + b fastfpe_next + +CPDT_ss_rnd_ovfl: + add r4,r4,#1 + cmp r4,#128 + bge CPDT_ss_e255 + + mov r2,#0x80000000 + mov r3,#0 + b CPDT_ss_store + +CPDT_ss_e0: + cmp r4,#-150 + ble CPDT_ss_zero + + add r4,r4,#126 +CPDT_ss_unnormalize: + mov r2,r2,lsr#1 + adds r4,r4,#1 + bne CPDT_ss_unnormalize + + orr r1,r1,r2,lsr#8 + +CPDT_ss_zero: + str r1,[r6] + b fastfpe_next + +CPDT_ss_e255: + cmp r4,#0x7fffffff + bne CPDT_ss_inf + cmp r2,#0 + beq CPDT_ss_inf + + orr r1,r1,#0x00200000 @ for safety so that it is not INF + orr r1,r1,r2,lsr#9 @ get highest bit of mantissa + +CPDT_ss_inf: + orr r1,r1,#0x7f000000 + orr r1,r1,#0x00800000 + str r1,[r6] + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDT_store_double +CPDT_store_double: + ldmia r0,{r1-r4} + + cmp r4,#1024 @ this check has to be first, or + bge CPDT_sd_e2047 @ overflow can occur on second ! + add r0,r4,#3 + cmp r0,#-1023+3 @ cmp with -1023 + ble CPDT_sd_e0 + + adds r3,r3,#1<<10 @ round to nearest + adcs r2,r2,#0 + bcs CPDT_sd_rnd_ovfl @ very very seldom taken + +CPDT_sd_store: + sub r4,r4,#1 + add r4,r4,#1024 + orr r1,r1,r4,lsl#20 + + bic r2,r2,#0x80000000 + orr r1,r1,r2,lsr#11 + + mov r2,r2,lsl#21 + orr r2,r2,r3,lsr#11 + + stmia r6,{r1,r2} + b fastfpe_next + +CPDT_sd_rnd_ovfl: + add r4,r4,#1 + cmp r4,#1024 + bge CPDT_sd_e2047 + + mov r2,#0x80000000 + mov r3,#0 + b CPDT_sd_store + +CPDT_sd_e0: + add r0,r4,#1075-1024 + cmp r0,#-1024 + ble CPDT_sd_zero + + add r4,r4,#1024 + sub r4,r4,#2 +CPDT_sd_unnormalize: + movs r2,r2,lsr#1 + mov r3,r3,rrx + adds r4,r4,#1 + bne CPDT_sd_unnormalize + + orr r1,r1,r2,lsr#11 + mov r2,r2,lsl#21 + orr r2,r2,r3,lsr#11 + + stmia r6,{r1,r2} + b fastfpe_next + +CPDT_sd_zero: + mov r2,#0 + stmia r6,{r1,r2} + b fastfpe_next + +CPDT_sd_e2047: + cmp r4,#0x7fffffff + bne CPDT_sd_inf + cmp r2,#0 + beq CPDT_sd_inf + + orr r1,r1,#0x00040000 @ for safety so that it is not INF + orr r1,r1,r2,lsr#12 @ get highest bit of mantissa + +CPDT_sd_inf: + orr r1,r1,#0x7f000000 + orr r1,r1,#0x00f00000 + stmia r6,{r1,r2} + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDT_store_extended +CPDT_store_extended: + ldmia r0,{r1-r4} + + cmp r4,#16384 @ this check has to be first, or + bge CPDT_se_e32767 @ overflow can occur with second ! + add r0,r4,#63 + cmp r0,#-16383+63 + ble CPDT_se_e0 + + sub r4,r4,#1 + add r4,r4,#16384 + orr r1,r1,r4 + + stmia r6,{r1-r3} + b fastfpe_next + +CPDT_se_e0: + add r0,r4,#16446-16384 + cmp r0,#-16384 + ble CPDT_se_zero + + add r4,r4,#16384 + sub r4,r4,#2 +CPDT_se_unnormalize: + movs r2,r2,lsr#1 + mov r3,r3,rrx + adds r4,r4,#1 + bne CPDT_se_unnormalize + + stmia r6,{r1-r3} + b fastfpe_next + +CPDT_se_zero: + mov r2,#0 + mov r3,#0 + stmia r6,{r1-r3} + b fastfpe_next + +CPDT_se_e32767: + cmp r4,#0x7fffffff + bne CPDT_se_inf + cmp r2,#0 + beq CPDT_se_inf + + mov r2,r2,lsl#1 + orr r2,r2,#0x20000000 + +CPDT_se_inf: + orr r1,r1,#0x00007f00 + orr r1,r1,#0x000000ff + stmia r6,{r1-r3} + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDT_store_decimal +CPDT_store_decimal: + + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDT_sfm +CPDT_sfm: + add r2,r10,r0,lsr#8 + ldr r4,[r2,#0] + ldr r3,[r2,#4] + bic r3,r3,#0x80000000 + orr r3,r3,r4 + str r3,[r6],#4 + ldr r3,[r2,#8] + str r3,[r6],#4 + ldr r3,[r2,#12] + str r3,[r6],#4 + + add r0,r0,#1<<12 + and r0,r0,#7<<12 + subs r1,r1,#1 + bne CPDT_sfm + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPDT_lfm +CPDT_lfm: + add r2,r10,r0,lsr#8 + ldr r4,[r6],#4 + and r3,r4,#0x80000000 + str r3,[r2,#0] + ldr r3,[r6],#4 + str r3,[r2,#8] + ldr r3,[r6],#4 + str r3,[r2,#12] + + cmp r3,#0x80000000 @ does the exp indicate zero? + biceq r4,r4,#0x80000000 @ if so, indicate 'denormalized' + beq CPDT_lfm_storer4 + cmp r3,#0x7fffffff @ does the exp indicate inf or NaN? + biceq r4,r4,#0x80000000 @ if so, indicate 'denormalized' + beq CPDT_lfm_storer4 + orrne r4,r4,#0x80000000 @ otherwise, set normalized bit + +CPDT_lfm_storer4: + str r4,[r2,#4] + + add r0,r0,#1<<12 + and r0,r0,#7<<12 + subs r1,r1,#1 + bne CPDT_lfm + b fastfpe_next + +/*---------------------------------------------------------------------------*/ diff -urN orig/arch/arm/fastfpe/CPRT.S linux/arch/arm/fastfpe/CPRT.S --- orig/arch/arm/fastfpe/CPRT.S Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/fastfpe/CPRT.S Thu Oct 24 17:49:52 2002 @@ -0,0 +1,185 @@ +/* +The FP structure has 4 words reserved for each register, the first is used +just +for the sign in bit 31, the second and third are for the mantissa (unsigned +integer, high 32 bit first) and the fourth is the exponent (signed integer). +The mantissa is always normalized. + +If the exponent is 0x80000000, that is the most negative value, the number +represented is 0 and both mantissa words are also 0. + +If the exponent is 0x7fffffff, that is the biggest positive value, the +number +represented is infinity if the high 32 mantissa bit are also 0, otherwise it +is +a NaN. The low 32 mantissa bit are 0 if the number represented is infinity. + +Decimal and packed decimal numbers are not supported yet. +*/ + +/*---------------------------------------------------------------------------*/ + + .text + .globl CPRT_flt +CPRT_flt: + add r0,r13,r0,lsr#10 + ldr r2,[r0] + mov r3,#0 + cmp r2,#0 + beq CPRT_flt_zero + + ands r0,r2,#0x80000000 + rsbne r2,r2,#0 + mov r4,#31 + + cmp r2,#0x00010000 + movcc r2,r2,lsl#16 + subcc r4,r4,#16 + + cmp r2,#0x01000000 + movcc r2,r2,lsl#8 + subcc r4,r4,#8 + + cmp r2,#0x10000000 + movcc r2,r2,lsl#4 + subcc r4,r4,#4 + + cmp r2,#0x40000000 + movcc r2,r2,lsl#2 + subcc r4,r4,#2 + + cmp r2,#0x80000000 + movcc r2,r2,lsl#1 + subcc r4,r4,#1 + + stmia r1,{r0,r2,r3,r4} + b fastfpe_next + +CPRT_flt_zero: + mov r0,#0 + mov r4,#0x80000000 + stmia r1,{r0,r2,r3,r4} + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPRT_fix +CPRT_fix: + ldmia r2,{r1,r2,r3,r5} + bl CPDO_rnd_core + +CPRT_back: + add r0,r13,r0,lsr#10 + cmp r5,#0 + blt CPRT_int_zero + cmp r5,#30 + bgt CPRT_overflow + + rsb r5,r5,#31 + mov r2,r2,lsr r5 + tst r1,#0x80000000 + rsbne r2,r2,#0 + + str r2,[r0] + b fastfpe_next + +CPRT_int_zero: + mov r2,#0 + str r2,[r0] + b fastfpe_next + +CPRT_overflow: + mov r2,#0x80000000 + tst r1,#0x80000000 + subeq r2,r2,#1 + str r2,[r0] + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPRT_wfs +CPRT_wfs: + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPRT_rfs +CPRT_rfs: + add r0,r13,r0,lsr#10 + mov r1,#0x02000000 @ Software Emulation, not Acorn FPE + str r1,[r0] + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPRT_cmf +CPRT_cmf: + ldmia r1,{r1,r3,r5,r7} + ldmia r2,{r2,r4,r6,r8} + +CPRT_cmf_e: + ldr r0,[r13,#16*4] + + cmp r7,#0x7fffffff + bic r0,r0,#0xf0000000 + + cmpeq r3,#0xffffffff + beq CPRT_cmf_unordered + cmp r8,#0x7fffffff + cmpeq r4,#0xffffffff + beq CPRT_cmf_unordered + + cmp r1,r2 + beq CPRT_cmf_equalsign + b CPRT_cmf_sign + +CPRT_cmf_equalsign: + cmp r7,r8 + beq CPRT_cmf_equalexponent + bgt CPRT_cmf_sign + b CPRT_cmf_signb + +CPRT_cmf_equalexponent: + cmp r3,r4 + cmpeq r5,r6 + beq CPRT_cmf_equal + bhi CPRT_cmf_sign + b CPRT_cmf_signb + +CPRT_cmf_sign: + cmp r7,#0x80000000 @ (0.0 == -0.0)? + cmpeq r7,r8 + beq CPRT_cmf_equal + tst r1,#0x80000000 + orreq r0,r0,#0x20000000 + orrne r0,r0,#0x80000000 + str r0,[r13,#16*4] + b fastfpe_next + +CPRT_cmf_signb: + tst r1,#0x80000000 + orrne r0,r0,#0x20000000 + orreq r0,r0,#0x80000000 + str r0,[r13,#16*4] + b fastfpe_next + +CPRT_cmf_equal: + orr r0,r0,#0x60000000 + str r0,[r13,#16*4] + b fastfpe_next + +CPRT_cmf_unordered: + orr r0,r0,#0x10000000 + str r0,[r13,#16*4] + b fastfpe_next + +/*---------------------------------------------------------------------------*/ + + .globl CPRT_cnf +CPRT_cnf: + ldmia r1,{r1,r3,r5,r7} + ldmia r2,{r2,r4,r6,r8} + eor r2,r2,#0x80000000 + b CPRT_cmf_e + +/*---------------------------------------------------------------------------*/ diff -urN orig/arch/arm/fastfpe/Makefile linux/arch/arm/fastfpe/Makefile --- orig/arch/arm/fastfpe/Makefile Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/fastfpe/Makefile Sun Dec 29 11:29:19 2002 @@ -0,0 +1,14 @@ +# +# linux/arch/arm/fastfpe/Makefile +# +# Copyright (C) Peter Teichmann +# + +obj-y := +obj-m := +obj-n := +obj- := + +fastfpe-objs := module.o entry.o CPDO.o CPRT.o CPDT.o + +obj-$(CONFIG_FPE_FASTFPE) += fastfpe.o diff -urN orig/arch/arm/fastfpe/entry.S linux/arch/arm/fastfpe/entry.S --- orig/arch/arm/fastfpe/entry.S Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/fastfpe/entry.S Fri Aug 16 20:27:36 2002 @@ -0,0 +1,294 @@ +/* +At entry the registers contain the following information: + +r14 return address for undefined exception return +r9 return address for return from exception +r13 user registers on stack, offset 0 up to offset 4*15 contains + registers r0..15, then the psr +r10 FP workspace 35 words (init, reg[8][4], fpsr, fpcr) + +*/ + +/*---------------------------------------------------------------------------*/ + + .data +fp_const: + .word 0, 0x00000000, 0, 0x80000000 @ 0 + .word 0, 0x80000000, 0, 0 @ 1 + .word 0, 0x80000000, 0, 1 @ 2 + .word 0, 0xc0000000, 0, 1 @ 3 + .word 0, 0x80000000, 0, 2 @ 4 + .word 0, 0xa0000000, 0, 2 @ 5 + .word 0, 0x80000000, 0, -1 @ 0.5 + .word 0, 0xa0000000, 0, 3 @ 10 +fp_undef: + .word 0 +fp_cond: + .word 0xf0f0 @ eq + .word 0x0f0f @ ne + .word 0xcccc @ cs + .word 0x3333 @ cc + .word 0xff00 @ mi + .word 0x00ff @ pl + .word 0xaaaa @ vs + .word 0x5555 @ vc + .word 0x0c0c @ hi + .word 0xf3f3 @ ls + .word 0xaa55 @ ge + .word 0x55aa @ lt + .word 0x0a05 @ gt + .word 0xf5fa @ le + .word 0xffff @ al + .word 0x0000 @ nv + +/*---------------------------------------------------------------------------*/ + + .text + .globl fastfpe_enter +fastfpe_enter: + ldr r4,=fp_undef + str r14,[r4] @ to free one register + add r10,r10,#4 @ to make the code simpler + mov r4, r0 @ r4=trapped instruction + and r1,r4,#0x00000f00 @ r1=coprocessor << 8 +next_enter: + cmp r1,#1<<8 @ copro 1 ? + beq copro_1 + cmp r1,#2<<8 + movne pc,r14 + +copro_2: + and r1,r4,#0x0f000000 + cmp r1,#0x0c000000 @ CPDT with post indexing + cmpne r1,#0x0d000000 @ CPDT with pre indexing + beq CPDT_M_enter + mov pc,r14 + +copro_1: + and r1,r4,#0x0f000000 + cmp r1,#0x0e000000 @ CPDO + beq CPDO_CPRT_enter + cmp r1,#0x0c000000 @ CPDT with post indexing + cmpne r1,#0x0d000000 @ CPDT with pre indexing + beq CPDT_1_enter + mov pc,r14 + +/*---------------------------------------------------------------------------*/ + + .globl fastfpe_next +fastfpe_next: + ldr r5,[r13,#60] +next_after_cond: +__x1: + ldrt r4,[r5],#4 + + ldr r0,=fp_cond @ check condition of next instruction + ldr r1,[r13,#64] @ psr containing flags + mov r2,r4,lsr#28 + mov r1,r1,lsr#28 + ldr r0,[r0,r2,lsl#2] + mov r0,r0,lsr r1 + tst r0,#1 + beq next_after_cond @ must not necessarily have been an + @ FP instruction ! + and r1,r4,#0x0f000000 @ Test for copro instruction + cmp r1,#0x0c000000 + rsbgts r0,r1,#0x0e000000 @ cmpgt #0x0e000000,r1 + movlt pc,r9 @ next is no copro instruction, return + + ands r1,r4,#0x00000f00 @ r1 = coprocessor << 8 + cmpne r1,#3<<8 + movge pc,r9 @ copro = 0 or >=3, return + + str r5,[r13,#60] @ save updated pc + b next_enter + +/*---------------------------------------------------------------------------*/ + +undefined: + ldr r4,=fp_undef + ldr pc,[r4] + +/*---------------------------------------------------------------------------*/ + +CPDT_1_enter: + and r5,r4,#0x000f0000 @ r5=base register number << 16 + ldr r6,[r13,r5,lsr#14] @ r6=base address + cmp r5,#0x000f0000 @ base register = pc ? + addeq r6,r6,#4 + and r7,r4,#0x000000ff @ r7=offset value + + tst r4,#0x00800000 @ up or down? + addne r7,r6,r7,lsl#2 + subeq r7,r6,r7,lsl#2 @ r6=base address +/- offset + tst r4,#0x01000000 @ preindexing ? + movne r6,r7 + tst r4,#0x00200000 @ write back ? + cmpne r5,#0x000f0000 @ base register = pc ? + strne r7,[r13,r5,lsr#14] + + and r0,r4,#0x00007000 @ r0=fp register number << 12 + add r0,r10,r0,lsr#8 @ r0=address of fp register + mov r1,#0 + tst r4,#0x00008000 + orrne r1,r1,#1 @ T0 + tst r4,#0x00400000 + orrne r1,r1,#2 @ T1 + tst r4,#0x00100000 + orrne r1,r1,#4 @ L/S + + add pc,pc,r1,lsl#2 + mov r0,r0 + b CPDT_store_single @ these functions get + b CPDT_store_double @ r0=address of fp register + b CPDT_store_extended @ r6=address of data + b undefined @ CPDT_store_decimal + b CPDT_load_single + b CPDT_load_double + b CPDT_load_extended + b undefined @ CPDT_load_decimal + +/*---------------------------------------------------------------------------*/ + +CPDT_M_enter: + and r5,r4,#0x000f0000 @ r5=base register number << 16 + ldr r6,[r13,r5,lsr#14] @ r6=base address + cmp r5,#0x000f0000 @ base register = pc ? + addeq r6,r6,#4 + and r7,r4,#0x000000ff @ r7=offset value + + tst r4,#0x00800000 @ up or down? + addne r7,r6,r7,lsl#2 + subeq r7,r6,r7,lsl#2 @ r7=base address +/- offset + tst r4,#0x01000000 @ preindexing ? + movne r6,r7 + tst r4,#0x00200000 @ write back ? + cmpne r5,#0x000f0000 @ base register = pc ? + strne r7,[r13,r5,lsr#14] + + and r0,r4,#0x00007000 @ r0=fp register number << 12 + and r1,r4,#0x00008000 + mov r1,r1,lsr#15 @ N0 + and r2,r4,#0x00400000 + orrs r1,r1,r2,lsr#21 @ N1 + addeq r1,r1,#4 @ r1=register count + + tst r4,#0x00100000 @ load/store + beq CPDT_sfm + b CPDT_lfm + +/*---------------------------------------------------------------------------*/ + +CPDO_CPRT_enter: + tst r4,#0x00000010 + bne CPRT_enter + + and r0,r4,#0x00007000 + add r0,r10,r0,lsr#8 @ r0=address of Fd + and r1,r4,#0x00070000 + add r1,r10,r1,lsr#12 @ r1=address of Fn + tst r4,#0x00000008 + bne CPDO_const + and r2,r4,#0x00000007 + add r2,r10,r2,lsl#4 @ r2=address of Fm + +CPDO_constback: + and r3,r4,#0x00f00000 + tst r4,#0x00008000 + orrne r3,r3,#0x01000000 + + add pc,pc,r3,lsr#18 + mov r0,r0 + b CPDO_adf + b CPDO_muf + b CPDO_suf + b CPDO_rsf + b CPDO_dvf + b CPDO_rdf + b undefined + b undefined + b undefined @ CPDO_rmf + b CPDO_muf + b CPDO_dvf + b CPDO_rdf + b undefined + b undefined + b undefined + b undefined + b CPDO_mvf + b CPDO_mnf + b CPDO_abs + b CPDO_rnd + b CPDO_sqt + b undefined + b undefined + b undefined + b undefined + b undefined + b undefined + b undefined + b undefined + b undefined + b CPDO_rnd + b fastfpe_next + +CPDO_const: + ldr r2,=fp_const + and r3,r4,#0x00000007 + add r2,r2,r3,lsl#4 + b CPDO_constback + +/*---------------------------------------------------------------------------*/ + +CPRT_enter: + and r0,r4,#0x0000f000 @ r0=Rd<<12 + and r1,r4,#0x00070000 + add r1,r10,r1,lsr#12 @ r1=address of Fn + tst r4,#0x00000008 + bne CPRT_const + and r2,r4,#0x00000007 + add r2,r10,r2,lsl#4 @ r2=address of Fm + +CPRT_constback: + and r3,r4,#0x00f00000 + + add pc,pc,r3,lsr#18 + mov r0,r0 + b CPRT_flt + b CPRT_fix + b CPRT_wfs + b CPRT_rfs + b undefined + b undefined + b undefined + b undefined + b undefined + b CPRT_cmf + b undefined + b CPRT_cnf + b undefined + b CPRT_cmf + b undefined + b CPRT_cnf + +CPRT_const: + ldr r2,=fp_const + and r3,r4,#0x00000007 + add r2,r2,r3,lsl#4 + b CPRT_constback + +/*---------------------------------------------------------------------------*/ + + @ The fetch of the next instruction to emulate could fault + + .section .fixup,"ax" + .align +__f1: + mov pc,r9 + .previous + .section __ex_table,"a" + .align 3 + .long __x1,__f1 + .previous + +/*---------------------------------------------------------------------------*/ diff -urN orig/arch/arm/fastfpe/module.c linux/arch/arm/fastfpe/module.c --- orig/arch/arm/fastfpe/module.c Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/fastfpe/module.c Wed Mar 19 15:11:36 2003 @@ -0,0 +1,62 @@ +/* + Fast Floating Point Emulator + (c) Peter Teichmann + + 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 + +#ifndef MODULE +#define kern_fp_enter fp_enter + +extern char fpe_type[]; +#endif + +static void (*orig_fp_enter)(void); /* old kern_fp_enter value */ +extern void (*kern_fp_enter)(void); /* current FP handler */ +extern void fastfpe_enter(void); /* forward declarations */ + +static int __init fpe_init(void) +{ + if (fpe_type[0] && strcmp(fpe_type, "fastfpe")) + return 0; + + printk("Fast Floating Point Emulator V0.9 (c) Peter Teichmann.\n"); + + /* Save pointer to the old FP handler and then patch ourselves in */ + orig_fp_enter = kern_fp_enter; + kern_fp_enter = fastfpe_enter; + + return 0; +} + +static void __exit fpe_exit(void) +{ + /* Restore the values we saved earlier. */ + kern_fp_enter = orig_fp_enter; +} + +module_init(fpe_init); +module_exit(fpe_exit); + +MODULE_AUTHOR("Peter Teichmann "); +MODULE_DESCRIPTION("Fast floating point emulator with full precision"); diff -urN orig/arch/arm/kernel/bios32.c linux/arch/arm/kernel/bios32.c --- orig/arch/arm/kernel/bios32.c Mon Mar 24 23:47:12 2003 +++ linux/arch/arm/kernel/bios32.c Mon Mar 17 23:26:04 2003 @@ -20,11 +20,11 @@ static int debug_pci; -void pcibios_report_status(u_int status_mask, int warn) +static void pcibios_bus_report_status(struct pci_bus *bus, u_int status_mask, int warn) { struct pci_dev *dev; - pci_for_each_dev(dev) { + list_for_each_entry(dev, &bus->devices, bus_list) { u16 status; /* @@ -35,18 +35,34 @@ continue; pci_read_config_word(dev, PCI_STATUS, &status); + if (status == 0xffff) + continue; - status &= status_mask; - if (status == 0) + if ((status & status_mask) == 0) continue; /* clear the status errors */ - pci_write_config_word(dev, PCI_STATUS, status); + pci_write_config_word(dev, PCI_STATUS, status & status_mask); + + if (!warn) + continue; + + printk("(%02x:%02x.%d: %04X) ", dev->bus->number, + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), + status); + } + + list_for_each_entry(dev, &bus->devices, bus_list) + if (dev->subordinate) + pcibios_bus_report_status(dev->subordinate, status_mask, warn); +} + +void pcibios_report_status(u_int status_mask, int warn) +{ + struct pci_bus *bus; - if (warn) - printk("(%02x:%02x.%d: %04X) ", dev->bus->number, - PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), - status); + pci_for_each_bus(bus) { + pcibios_bus_report_status(bus, status_mask, warn); } } @@ -362,6 +378,19 @@ isa_bridge = dev; break; #endif + case PCI_CLASS_BRIDGE_PCI: + pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &status); + status |= PCI_BRIDGE_CTL_PARITY|PCI_BRIDGE_CTL_MASTER_ABORT; + status &= ~(PCI_BRIDGE_CTL_BUS_RESET|PCI_BRIDGE_CTL_FAST_BACK); + pci_write_config_word(dev, PCI_BRIDGE_CONTROL, status); + break; + + case PCI_CLASS_BRIDGE_CARDBUS: + pci_read_config_word(dev, PCI_CB_BRIDGE_CONTROL, &status); + status |= PCI_CB_BRIDGE_CTL_PARITY|PCI_CB_BRIDGE_CTL_MASTER_ABORT; + pci_write_config_word(dev, PCI_CB_BRIDGE_CONTROL, status); + break; + } } /* @@ -384,6 +413,8 @@ if (bus->self && bus->self->hdr_type == PCI_HEADER_TYPE_BRIDGE) { if (features & PCI_COMMAND_FAST_BACK) bus->bridge_ctl |= PCI_BRIDGE_CTL_FAST_BACK; + if (features & PCI_COMMAND_SERR) + bus->bridge_ctl |= PCI_BRIDGE_CTL_SERR; if (features & PCI_COMMAND_PARITY) bus->bridge_ctl |= PCI_BRIDGE_CTL_PARITY; } @@ -534,8 +565,6 @@ if (hw->postinit) hw->postinit(); - pci_fixup_irqs(pcibios_swizzle, pcibios_map_irq); - list_for_each_entry(sys, &hw->buses, node) { struct pci_bus *bus = sys->bus; @@ -550,6 +579,11 @@ pci_bus_assign_resources(bus); /* + * Fixup IRQs. + */ + pci_bus_fixup_irqs(bus, pcibios_swizzle, pcibios_map_irq); + + /* * Tell drivers about devices found. */ pci_bus_add_devices(bus); diff -urN orig/arch/arm/kernel/debug.S linux/arch/arm/kernel/debug.S --- orig/arch/arm/kernel/debug.S Sun Apr 20 16:31:36 2003 +++ linux/arch/arm/kernel/debug.S Fri May 30 12:06:18 2003 @@ -192,6 +192,20 @@ @ if all ports are inactive, then there is nothing we can do moveq pc, lr + ldr r1, [\rx, #UTCR2] + teq r1, #5 + movne r1, #0 + strne r1, [\rx, #UTCR3] + movne r1, #8 + strne r1, [\rx, #UTCR0] + movne r1, #5 + strne r1, [\rx, #UTCR2] + movne r1, #0 + strne r1, [\rx, #UTCR1] + movne r1, #3 + strne r1, [\rx, #UTCR3] + movne r1, #255 + strne r1, [\rx, #UTSR0] .endm .macro senduart,rd,rx @@ -287,7 +301,7 @@ #elif defined(CONFIG_ARCH_INTEGRATOR) -#include +#include .macro addruart,rx mrc p15, 0, \rx, c1, c0 @@ -298,7 +312,7 @@ .endm .macro senduart,rd,rx - strb \rd, [\rx, #AMBA_UARTDR] + strb \rd, [\rx, #UART01x_DR] .endm .macro waituart,rd,rx diff -urN orig/arch/arm/kernel/entry-armv.S linux/arch/arm/kernel/entry-armv.S --- orig/arch/arm/kernel/entry-armv.S Tue May 27 10:04:13 2003 +++ linux/arch/arm/kernel/entry-armv.S Tue May 13 15:08:17 2003 @@ -966,6 +966,27 @@ teqne r10, #0x0f000000 @ SWI (ARM6/7 bug)? #endif moveq pc, lr + and r10, r0, #0x00000f00 @ mask out CP number + add pc, pc, r10, lsr #6 + mov r0, r0 + + mov pc, lr @ CP#0 + b do_fpe @ CP#1 (FPE) + b do_fpe @ CP#2 (FPE) + mov pc, lr @ CP#3 + mov pc, lr @ CP#4 + mov pc, lr @ CP#5 + mov pc, lr @ CP#6 + mov pc, lr @ CP#7 + mov pc, lr @ CP#8 + mov pc, lr @ CP#9 + mov pc, lr @ CP#10 + mov pc, lr @ CP#11 + mov pc, lr @ CP#12 + mov pc, lr @ CP#13 + mov pc, lr @ CP#14 (Debug) + mov pc, lr @ CP#15 (Control) + do_fpe: get_thread_info r10 @ get current thread ldr r4, [r10, #TI_TASK] @ get current task mov r8, #1 @@ -1219,6 +1240,9 @@ * get out of that mode without clobbering one register. */ vector_FIQ: disable_fiq + mrs r13, spsr + orr r13, r13, #PSR_F_BIT + msr spsr, r13 subs pc, lr, #4 /*============================================================================= diff -urN orig/arch/arm/kernel/irq.c linux/arch/arm/kernel/irq.c --- orig/arch/arm/kernel/irq.c Tue May 27 10:04:13 2003 +++ linux/arch/arm/kernel/irq.c Tue May 13 14:43:26 2003 @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -505,6 +506,11 @@ } desc = irq_desc + irq; + if (!desc->action && desc->handle != do_bad_IRQ) { + printk(KERN_ERR "Setting type of unclaimed IRQ%d from ", irq); + print_symbol("%s\n", (unsigned long)__builtin_return_address(0)); + } + if (desc->chip->type) { spin_lock_irqsave(&irq_controller_lock, flags); ret = desc->chip->type(irq, type); diff -urN orig/arch/arm/kernel/pm.c linux/arch/arm/kernel/pm.c --- orig/arch/arm/kernel/pm.c Tue May 27 10:04:13 2003 +++ linux/arch/arm/kernel/pm.c Wed May 14 00:07:39 2003 @@ -12,6 +12,7 @@ #include #include #include +#include #include #include diff -urN orig/arch/arm/kernel/setup.c linux/arch/arm/kernel/setup.c --- orig/arch/arm/kernel/setup.c Tue May 27 10:04:13 2003 +++ linux/arch/arm/kernel/setup.c Mon Jun 9 19:58:59 2003 @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -28,6 +29,7 @@ #include #include #include +#include #include #include diff -urN orig/arch/arm/kernel/time.c linux/arch/arm/kernel/time.c --- orig/arch/arm/kernel/time.c Mon Mar 24 23:47:12 2003 +++ linux/arch/arm/kernel/time.c Sun Apr 6 00:01:59 2003 @@ -103,7 +103,7 @@ return; if (next_rtc_update && - time_before(xtime.tv_sec, next_rtc_update)) + time_before((unsigned long)xtime.tv_sec, next_rtc_update)) return; if (xtime.tv_nsec < 500000000 - ((unsigned) tick_nsec >> 1) && diff -urN orig/arch/arm/kernel/traps.c linux/arch/arm/kernel/traps.c --- orig/arch/arm/kernel/traps.c Tue May 27 10:04:13 2003 +++ linux/arch/arm/kernel/traps.c Tue May 13 18:02:00 2003 @@ -50,6 +50,8 @@ static const char *handler[]= { "prefetch abort", "data abort", "address exception", "interrupt" }; +static int noisy_user_debug; + void dump_backtrace_entry(unsigned long where, unsigned long from) { #ifdef CONFIG_KALLSYMS @@ -78,7 +80,7 @@ /* * Dump out the contents of some memory nicely... */ -static void dump_mem(const char *str, unsigned long bottom, unsigned long top) +/*static*/ void dump_mem(const char *str, unsigned long bottom, unsigned long top) { unsigned long p = bottom & ~31; mm_segment_t fs; @@ -113,7 +115,7 @@ set_fs(fs); } -static void dump_instr(struct pt_regs *regs) +/*static*/ void dump_instr(struct pt_regs *regs) { unsigned long addr = instruction_pointer(regs); const int thumb = thumb_mode(regs); @@ -150,12 +152,12 @@ set_fs(fs); } -static void __dump_stack(struct task_struct *tsk, unsigned long sp) +/*static*/ void __dump_stack(struct task_struct *tsk, unsigned long sp) { dump_mem("Stack: ", sp, 8192+(unsigned long)tsk->thread_info); } -static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) +/*static*/ void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) { unsigned int fp; int ok = 1; @@ -292,9 +294,11 @@ spin_unlock_irq(&undef_lock); #ifdef CONFIG_DEBUG_USER - printk(KERN_INFO "%s (%d): undefined instruction: pc=%p\n", - current->comm, current->pid, pc); - dump_instr(regs); + if (noisy_user_debug) { + printk(KERN_INFO "%s (%d): undefined instruction: pc=%p\n", + current->comm, current->pid, pc); + dump_instr(regs); + } #endif current->thread.error_code = 0; @@ -356,9 +360,11 @@ } #ifdef CONFIG_DEBUG_USER - printk(KERN_ERR "[%d] %s: obsolete system call %08x.\n", - current->pid, current->comm, n); - dump_instr(regs); + if (noisy_user_debug) { + printk(KERN_ERR "[%d] %s: obsolete system call %08x.\n", + current->pid, current->comm, n); + dump_instr(regs); + } #endif info.si_signo = SIGILL; @@ -464,11 +470,14 @@ * experience shows that these seem to indicate that * something catastrophic has happened */ - printk("[%d] %s: arm syscall %d\n", current->pid, current->comm, no); - dump_instr(regs); - if (user_mode(regs)) { - show_regs(regs); - c_backtrace(regs->ARM_fp, processor_mode(regs)); + if (noisy_user_debug) { + printk("[%d] %s: arm syscall %d\n", + current->pid, current->comm, no); + dump_instr(regs); + if (user_mode(regs)) { + show_regs(regs); + c_backtrace(regs->ARM_fp, processor_mode(regs)); + } } #endif info.si_signo = SIGILL; @@ -500,10 +509,12 @@ siginfo_t info; #ifdef CONFIG_DEBUG_USER - printk(KERN_ERR "[%d] %s: bad data abort: code %d instr 0x%08lx\n", - current->pid, current->comm, code, instr); - dump_instr(regs); - show_pte(current->mm, addr); + if (noisy_user_debug) { + printk(KERN_ERR "[%d] %s: bad data abort: code %d instr 0x%08lx\n", + current->pid, current->comm, code, instr); + dump_instr(regs); + show_pte(current->mm, addr); + } #endif info.si_signo = SIGILL; @@ -522,6 +533,7 @@ printk(" - extra data = %p", data); printk("\n"); *(int *)0 = 0; + while (1); } void __readwrite_bug(const char *fn) diff -urN orig/arch/arm/lib/div64.S linux/arch/arm/lib/div64.S --- orig/arch/arm/lib/div64.S Sat Apr 12 14:46:39 2003 +++ linux/arch/arm/lib/div64.S Wed Jun 11 12:32:40 2003 @@ -1,27 +1,42 @@ #include +#ifndef __ARMEB__ ql .req r0 @ quotient low qh .req r1 @ quotient high -dl .req r3 @ divisor low -dh .req r2 @ divisor high +onl .req r0 @ original dividend low +onh .req r1 @ original dividend high nl .req r4 @ dividend low nh .req r5 @ dividend high +res .req r4 @ result +#else +ql .req r1 +qh .req r0 +onl .req r1 +onh .req r0 +nl .req r5 +nh .req r4 +res .req r5 +#endif + +dl .req r3 @ divisor low +dh .req r2 @ divsor high + ENTRY(do_div64) stmfd sp!, {r4, r5, lr} - mov nl, r0 - movs nh, r1 @ if high bits are zero + mov nl, onl + movs nh, onh @ if high bits are zero movne lr, #33 moveq lr, #1 @ only divide low bits - moveq nh, r0 + moveq nh, onl 1: cmp nh, dh bls 2f add lr, lr, #1 - movs dh, dh, lsl #1 @ left justify divisor + movs dh, dh, lsl #1 @ left justify disor bpl 1b -2: movs nh, r1 +2: movs nh, onh moveq dl, dh moveq dh, #0 movne dl, #0 @@ -38,5 +53,5 @@ subs lr, lr, #1 bne 3b - mov r2, nl + mov r2, res ldmfd sp!, {r4, r5, pc} diff -urN orig/arch/arm/lib/io-readsb.S linux/arch/arm/lib/io-readsb.S --- orig/arch/arm/lib/io-readsb.S Mon Sep 3 14:14:40 2001 +++ linux/arch/arm/lib/io-readsb.S Tue Feb 18 19:47:55 2003 @@ -37,31 +37,31 @@ .insb_16_lp: ldrb r3, [r0] ldrb r4, [r0] + ldrb r5, [r0] + ldrb r6, [r0] orr r3, r3, r4, lsl #8 - ldrb r4, [r0] - orr r3, r3, r4, lsl #16 - ldrb r4, [r0] - orr r3, r3, r4, lsl #24 + orr r3, r3, r5, lsl #16 + orr r3, r3, r6, lsl #24 ldrb r4, [r0] ldrb r5, [r0] + ldrb r6, [r0] + ldrb ip, [r0] orr r4, r4, r5, lsl #8 - ldrb r5, [r0] - orr r4, r4, r5, lsl #16 - ldrb r5, [r0] - orr r4, r4, r5, lsl #24 + orr r4, r4, r6, lsl #16 + orr r4, r4, ip, lsl #24 ldrb r5, [r0] ldrb r6, [r0] + ldrb ip, [r0] + ldrb lr, [r0] orr r5, r5, r6, lsl #8 - ldrb r6, [r0] - orr r5, r5, r6, lsl #16 - ldrb r6, [r0] - orr r5, r5, r6, lsl #24 + orr r5, r5, ip, lsl #16 + orr r5, r5, lr, lsl #24 ldrb r6, [r0] ldrb ip, [r0] + ldrb lr, [r0] orr r6, r6, ip, lsl #8 ldrb ip, [r0] - orr r6, r6, ip, lsl #16 - ldrb ip, [r0] + orr r6, r6, lr, lsl #16 orr r6, r6, ip, lsl #24 stmia r1!, {r3 - r6} @@ -76,18 +76,18 @@ ldrb r3, [r0] ldrb r4, [r0] + ldrb r5, [r0] + ldrb r6, [r0] orr r3, r3, r4, lsl #8 - ldrb r4, [r0] - orr r3, r3, r4, lsl #16 - ldrb r4, [r0] - orr r3, r3, r4, lsl #24 + orr r3, r3, r5, lsl #16 + orr r3, r3, r6, lsl #24 ldrb r4, [r0] ldrb r5, [r0] + ldrb r6, [r0] + ldrb ip, [r0] orr r4, r4, r5, lsl #8 - ldrb r5, [r0] - orr r4, r4, r5, lsl #16 - ldrb r5, [r0] - orr r4, r4, r5, lsl #24 + orr r4, r4, r6, lsl #16 + orr r4, r4, ip, lsl #24 stmia r1!, {r3, r4} .insb_no_8: tst r2, #4 @@ -95,11 +95,11 @@ ldrb r3, [r0] ldrb r4, [r0] + ldrb r5, [r0] + ldrb r6, [r0] orr r3, r3, r4, lsl #8 - ldrb r4, [r0] - orr r3, r3, r4, lsl #16 - ldrb r4, [r0] - orr r3, r3, r4, lsl #24 + orr r3, r3, r5, lsl #16 + orr r3, r3, r6, lsl #24 str r3, [r1], #4 .insb_no_4: ands r2, r2, #3 diff -urN orig/arch/arm/lib/io-writesl.S linux/arch/arm/lib/io-writesl.S --- orig/arch/arm/lib/io-writesl.S Mon Oct 1 23:10:03 2001 +++ linux/arch/arm/lib/io-writesl.S Fri Jun 6 11:27:15 2003 @@ -17,10 +17,15 @@ ands ip, r1, #3 bne 2f -1: ldr r3, [r1], #4 - str r3, [r0] - subs r2, r2, #1 - bne 1b + tst r2, #1 + ldrne r3, [r1], #4 + strne r3, [r0, #0] +1: subs r2, r2, #2 + ldrcs r3, [r1], #4 + ldrcs ip, [r1], #4 + strcs r3, [r0, #0] + strcs ip, [r0, #0] + bcs 1b mov pc, lr 2: bic r1, r1, #3 @@ -31,25 +36,25 @@ 3: mov ip, r3, lsr #16 ldr r3, [r1], #4 - orr ip, ip, r3, lsl #16 - str ip, [r0] subs r2, r2, #1 + orr ip, ip, r3, lsl #16 + str ip, [r0, #0] bne 3b mov pc, lr 4: mov ip, r3, lsr #24 ldr r3, [r1], #4 - orr ip, ip, r3, lsl #8 - str ip, [r0] subs r2, r2, #1 + orr ip, ip, r3, lsl #8 + str ip, [r0, #0] bne 4b mov pc, lr 5: mov ip, r3, lsr #8 ldr r3, [r1], #4 - orr ip, ip, r3, lsl #24 - str ip, [r0] subs r2, r2, #1 + orr ip, ip, r3, lsl #24 + str ip, [r0, #0] bne 5b mov pc, lr diff -urN orig/arch/arm/lib/putuser.S linux/arch/arm/lib/putuser.S --- orig/arch/arm/lib/putuser.S Mon Mar 11 11:55:36 2002 +++ linux/arch/arm/lib/putuser.S Sat May 17 23:09:52 2003 @@ -31,11 +31,11 @@ .global __put_user_1 __put_user_1: - bic r2, sp, #0x1f00 - bic r2, r2, #0x00ff - ldr r2, [r2, #TI_ADDR_LIMIT] - sub r2, r2, #1 - cmp r0, r2 + bic ip, sp, #0x1f00 + bic ip, ip, #0x00ff + ldr ip, [ip, #TI_ADDR_LIMIT] + sub ip, ip, #1 + cmp r0, ip 1: strlsbt r1, [r0] movls r0, #0 movls pc, lr @@ -43,17 +43,17 @@ .global __put_user_2 __put_user_2: - bic r2, sp, #0x1f00 - bic r2, r2, #0x00ff - ldr r2, [r2, #TI_ADDR_LIMIT] - sub r2, r2, #2 - cmp r0, r2 - movls r2, r1, lsr #8 + bic ip, sp, #0x1f00 + bic ip, ip, #0x00ff + ldr ip, [ip, #TI_ADDR_LIMIT] + sub ip, ip, #2 + cmp r0, ip + movls ip, r1, lsr #8 #ifndef __ARMEB__ 2: strlsbt r1, [r0], #1 -3: strlsbt r2, [r0] +3: strlsbt ip, [r0] #else -2: strlsbt r2, [r0], #1 +2: strlsbt ip, [r0], #1 3: strlsbt r1, [r0] #endif movls r0, #0 @@ -62,11 +62,11 @@ .global __put_user_4 __put_user_4: - bic r2, sp, #0x1f00 - bic r2, r2, #0x00ff - ldr r2, [r2, #TI_ADDR_LIMIT] - sub r2, r2, #4 - cmp r0, r2 + bic ip, sp, #0x1f00 + bic ip, ip, #0x00ff + ldr ip, [ip, #TI_ADDR_LIMIT] + sub ip, ip, #4 + cmp r0, ip 4: strlst r1, [r0] movls r0, #0 movls pc, lr diff -urN orig/arch/arm/mach-arc/oldlatches.c linux/arch/arm/mach-arc/oldlatches.c --- orig/arch/arm/mach-arc/oldlatches.c Mon Nov 18 09:51:56 2002 +++ linux/arch/arm/mach-arc/oldlatches.c Sun Nov 17 11:34:13 2002 @@ -25,34 +25,32 @@ /* newval=(oldval & ~mask)|newdata */ void oldlatch_aupdate(unsigned char mask,unsigned char newdata) { - if (machine_is_archimedes()) { - unsigned long flags; + unsigned long flags; - local_save_flags(flags); - latch_a_copy = (latch_a_copy & ~mask) | newdata; - __raw_writeb(latch_a_copy, LATCHA_BASE); - local_restore_flags(flags); - - printk("Latch: A = 0x%02x\n", latch_a_copy); - } else - BUG(); + BUG_ON(!machine_is_archimedes()); + + local_save_flags(flags); + latch_a_copy = (latch_a_copy & ~mask) | newdata; + __raw_writeb(latch_a_copy, LATCHA_BASE); + local_restore_flags(flags); + + printk("Latch: A = 0x%02x\n", latch_a_copy); } /* newval=(oldval & ~mask)|newdata */ void oldlatch_bupdate(unsigned char mask,unsigned char newdata) { - if (machine_is_archimedes()) { - unsigned long flags; + unsigned long flags; + + BUG_ON(!machine_is_archimedes()); + + local_save_flags(flags); + latch_b_copy = (latch_b_copy & ~mask) | newdata; + __raw_writeb(latch_b_copy, LATCHB_BASE); + local_restore_flags(flags); - local_save_flags(flags); - latch_b_copy = (latch_b_copy & ~mask) | newdata; - __raw_writeb(latch_b_copy, LATCHB_BASE); - local_restore_flags(flags); - - printk("Latch: B = 0x%02x\n", latch_b_copy); - } else - BUG(); + printk("Latch: B = 0x%02x\n", latch_b_copy); } static int __init oldlatch_init(void) diff -urN orig/arch/arm/mach-footbridge/isa-irq.c linux/arch/arm/mach-footbridge/isa-irq.c --- orig/arch/arm/mach-footbridge/isa-irq.c Fri Feb 21 19:48:35 2003 +++ linux/arch/arm/mach-footbridge/isa-irq.c Tue May 6 19:18:45 2003 @@ -84,10 +84,6 @@ .unmask = isa_unmask_pic_hi_irq, }; -static void no_action(int irq, void *dev_id, struct pt_regs *regs) -{ -} - static void isa_irq_handler(unsigned int irq, struct irqdesc *desc, struct pt_regs *regs) { diff -urN orig/arch/arm/mach-integrator/Kconfig linux/arch/arm/mach-integrator/Kconfig --- orig/arch/arm/mach-integrator/Kconfig Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/mach-integrator/Kconfig Fri Jun 6 23:02:50 2003 @@ -0,0 +1,16 @@ +menu "Integrator Options" + depends on ARCH_INTEGRATOR + +config INTEGRATOR_IMPD1 + tristate "Include support for Integrator/IM-PD1" + help + The IM-PD1 is an add-on logic module for the Integrator which + allows ARM(R) Ltd PrimeCells to be developed and evaluated. + The IM-PD1 can be found on the Integrator/PP2 platform. + + If you want to compile this as a module (=code which can be + inserted into and removed from the running kernel), say M + here and read . The module + will be called impd1. + +endmenu diff -urN orig/arch/arm/mach-integrator/Makefile linux/arch/arm/mach-integrator/Makefile --- orig/arch/arm/mach-integrator/Makefile Mon Mar 17 23:16:08 2003 +++ linux/arch/arm/mach-integrator/Makefile Fri Jun 6 22:32:43 2003 @@ -4,11 +4,9 @@ # Object file lists. -obj-y := arch.o irq.o mm.o time.o -obj-m := -obj-n := -obj- := +obj-y := core.o time.o obj-$(CONFIG_LEDS) += leds.o obj-$(CONFIG_PCI) += pci_v3.o pci.o obj-$(CONFIG_CPU_FREQ_INTEGRATOR) += cpu.o +obj-$(CONFIG_INTEGRATOR_IMPD1) += impd1.o diff -urN orig/arch/arm/mach-integrator/arch.c linux/arch/arm/mach-integrator/arch.c --- orig/arch/arm/mach-integrator/arch.c Mon Nov 18 09:51:56 2002 +++ linux/arch/arm/mach-integrator/arch.c Thu Jan 1 01:00:00 1970 @@ -1,40 +0,0 @@ -/* - * linux/arch/arm/mach-integrator/arch.c - * - * Copyright (C) 2000 Deep Blue Solutions 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#include -#include -#include - -#include -#include -#include -#include - -#include - -extern void integrator_map_io(void); -extern void integrator_init_irq(void); - -MACHINE_START(INTEGRATOR, "ARM-Integrator") - MAINTAINER("ARM Ltd/Deep Blue Solutions Ltd") - BOOT_MEM(0x00000000, 0x16000000, 0xf1600000) - BOOT_PARAMS(0x00000100) - MAPIO(integrator_map_io) - INITIRQ(integrator_init_irq) -MACHINE_END diff -urN orig/arch/arm/mach-integrator/core.c linux/arch/arm/mach-integrator/core.c --- orig/arch/arm/mach-integrator/core.c Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/mach-integrator/core.c Thu Jun 12 15:02:55 2003 @@ -0,0 +1,265 @@ +/* + * linux/arch/arm/mach-integrator/arch.c + * + * Copyright (C) 2000 Deep Blue Solutions 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/* + * All IO addresses are mapped onto VA 0xFFFx.xxxx, where x.xxxx + * is the (PA >> 12). + * + * Setup a VA for the Integrator interrupt controller (for header #0, + * just for now). + */ +#define VA_IC_BASE IO_ADDRESS(INTEGRATOR_IC_BASE) +#define VA_SC_BASE IO_ADDRESS(INTEGRATOR_SC_BASE) +#define VA_CMIC_BASE IO_ADDRESS(INTEGRATOR_HDR_BASE) + INTEGRATOR_HDR_IC_OFFSET + +/* + * Logical Physical + * e8000000 40000000 PCI memory PHYS_PCI_MEM_BASE (max 512M) + * ec000000 61000000 PCI config space PHYS_PCI_CONFIG_BASE (max 16M) + * ed000000 62000000 PCI V3 regs PHYS_PCI_V3_BASE (max 64k) + * ee000000 60000000 PCI IO PHYS_PCI_IO_BASE (max 16M) + * ef000000 Cache flush + * f1000000 10000000 Core module registers + * f1100000 11000000 System controller registers + * f1200000 12000000 EBI registers + * f1300000 13000000 Counter/Timer + * f1400000 14000000 Interrupt controller + * f1500000 15000000 RTC + * f1600000 16000000 UART 0 + * f1700000 17000000 UART 1 + * f1a00000 1a000000 Debug LEDs + * f1b00000 1b000000 GPIO + */ + +static struct map_desc integrator_io_desc[] __initdata = { + { IO_ADDRESS(INTEGRATOR_HDR_BASE), INTEGRATOR_HDR_BASE, SZ_4K, MT_DEVICE }, + { IO_ADDRESS(INTEGRATOR_SC_BASE), INTEGRATOR_SC_BASE, SZ_4K, MT_DEVICE }, + { IO_ADDRESS(INTEGRATOR_EBI_BASE), INTEGRATOR_EBI_BASE, SZ_4K, MT_DEVICE }, + { IO_ADDRESS(INTEGRATOR_CT_BASE), INTEGRATOR_CT_BASE, SZ_4K, MT_DEVICE }, + { IO_ADDRESS(INTEGRATOR_IC_BASE), INTEGRATOR_IC_BASE, SZ_4K, MT_DEVICE }, + { IO_ADDRESS(INTEGRATOR_RTC_BASE), INTEGRATOR_RTC_BASE, SZ_4K, MT_DEVICE }, + { IO_ADDRESS(INTEGRATOR_UART0_BASE), INTEGRATOR_UART0_BASE, SZ_4K, MT_DEVICE }, + { IO_ADDRESS(INTEGRATOR_UART1_BASE), INTEGRATOR_UART1_BASE, SZ_4K, MT_DEVICE }, + { IO_ADDRESS(INTEGRATOR_DBG_BASE), INTEGRATOR_DBG_BASE, SZ_4K, MT_DEVICE }, + { IO_ADDRESS(INTEGRATOR_GPIO_BASE), INTEGRATOR_GPIO_BASE, SZ_4K, MT_DEVICE }, + { PCI_MEMORY_VADDR, PHYS_PCI_MEM_BASE, SZ_16M, MT_DEVICE }, + { PCI_CONFIG_VADDR, PHYS_PCI_CONFIG_BASE, SZ_16M, MT_DEVICE }, + { PCI_V3_VADDR, PHYS_PCI_V3_BASE, SZ_64K, MT_DEVICE }, + { PCI_IO_VADDR, PHYS_PCI_IO_BASE, SZ_64K, MT_DEVICE } +}; + +static void __init integrator_map_io(void) +{ + iotable_init(integrator_io_desc, ARRAY_SIZE(integrator_io_desc)); +} + +#define ALLPCI ( (1 << IRQ_PCIINT0) | (1 << IRQ_PCIINT1) | (1 << IRQ_PCIINT2) | (1 << IRQ_PCIINT3) ) + +static void sc_mask_irq(unsigned int irq) +{ + writel(1 << irq, VA_IC_BASE + IRQ_ENABLE_CLEAR); +} + +static void sc_unmask_irq(unsigned int irq) +{ + writel(1 << irq, VA_IC_BASE + IRQ_ENABLE_SET); +} + +static struct irqchip sc_chip = { + .ack = sc_mask_irq, + .mask = sc_mask_irq, + .unmask = sc_unmask_irq, +}; + +static void __init integrator_init_irq(void) +{ + unsigned int i; + + /* Disable all interrupts initially. */ + /* Do the core module ones */ + writel(-1, VA_CMIC_BASE + IRQ_ENABLE_CLEAR); + + /* do the header card stuff next */ + writel(-1, VA_IC_BASE + IRQ_ENABLE_CLEAR); + writel(-1, VA_IC_BASE + FIQ_ENABLE_CLEAR); + + for (i = 0; i < NR_IRQS; i++) { + if (((1 << i) && INTEGRATOR_SC_VALID_INT) != 0) { + set_irq_chip(i, &sc_chip); + set_irq_handler(i, do_level_IRQ); + set_irq_flags(i, IRQF_VALID | IRQF_PROBE); + } + } +} + +/* + * These are our on-board devices. + */ +static struct amba_device rtc_device = { + .dev = { + .bus_id = "mb:15", + }, + .res = { + .start = INTEGRATOR_RTC_BASE, + .end = INTEGRATOR_RTC_BASE + SZ_4K, + .flags = IORESOURCE_MEM, + }, + .irq = IRQ_RTCINT, + .periphid = 0x00041030, +}; + +static struct amba_device uart0_device = { + .dev = { + .bus_id = "mb:16", + }, + .res = { + .start = INTEGRATOR_UART0_BASE, + .end = INTEGRATOR_UART0_BASE + SZ_4K, + .flags = IORESOURCE_MEM, + }, + .irq = IRQ_UARTINT0, + .periphid = 0x0041010, +}; + +static struct amba_device uart1_device = { + .dev = { + .bus_id = "mb:17", + }, + .res = { + .start = INTEGRATOR_UART1_BASE, + .end = INTEGRATOR_UART1_BASE + SZ_4K, + .flags = IORESOURCE_MEM, + }, + .irq = IRQ_UARTINT1, + .periphid = 0x0041010, +}; + +static struct amba_device kmi0_device = { + .dev = { + .bus_id = "mb:18", + }, + .res = { + .start = KMI0_BASE, + .end = KMI0_BASE + KMI_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + .irq = IRQ_KMIINT0, + .periphid = 0x00041050, +}; + +static struct amba_device kmi1_device = { + .dev = { + .bus_id = "mb:19", + }, + .res = { + .start = KMI1_BASE, + .end = KMI1_BASE + KMI_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + .irq = IRQ_KMIINT1, + .periphid = 0x00041050, +}; + +static struct amba_device *devs[] __initdata = { + &rtc_device, + &uart0_device, + &uart1_device, + &kmi0_device, + &kmi1_device, +}; + +struct integrator_exp_device { + struct platform_device pdev; + struct resource res[2]; +}; + +static int __init plat_register_devices(void) +{ + unsigned long sc_dec; + int i; + + for (i = 0; i < ARRAY_SIZE(devs); i++) { + struct amba_device *d = devs[i]; + + amba_device_register(d, &iomem_resource); + } + + sc_dec = readl(VA_SC_BASE + INTEGRATOR_SC_DEC_OFFSET); + for (i = 0; i < 4; i++) { + struct integrator_exp_device *exp; + + if ((sc_dec & (16 << i)) == 0) + continue; + + exp = kmalloc(sizeof(struct integrator_exp_device), GFP_KERNEL); + if (!exp) + continue; + + memset(exp, 0, sizeof(struct integrator_exp_device)); + exp->pdev.name = "lm"; + exp->pdev.id = i; + snprintf(exp->pdev.dev.name, sizeof(exp->pdev.dev.name), + "Integrator Logic Module %d", i); + exp->pdev.num_resources = 2; + exp->pdev.resource = &exp->res[0]; + + exp->res[0].start = 0xc0000000 + 0x10000000 * i; + exp->res[0].end = exp->res[0].start + 0x0fffffff; + exp->res[0].flags = IORESOURCE_MEM; + exp->res[1].start = IRQ_EXPINT0 + i; + exp->res[1].end = exp->res[1].start; + exp->res[1].flags = IORESOURCE_IRQ; + + platform_add_device(&exp->pdev); + } + + return 0; +} + +arch_initcall(plat_register_devices); + +MACHINE_START(INTEGRATOR, "ARM-Integrator") + MAINTAINER("ARM Ltd/Deep Blue Solutions Ltd") + BOOT_MEM(0x00000000, 0x16000000, 0xf1600000) + BOOT_PARAMS(0x00000100) + MAPIO(integrator_map_io) + INITIRQ(integrator_init_irq) +MACHINE_END diff -urN orig/arch/arm/mach-integrator/impd1.c linux/arch/arm/mach-integrator/impd1.c --- orig/arch/arm/mach-integrator/impd1.c Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/mach-integrator/impd1.c Thu Jun 12 14:58:44 2003 @@ -0,0 +1,237 @@ +/* + * linux/arch/arm/mach-integrator/impd1.c + * + * Copyright (C) 2003 Deep Blue Solutions Ltd, 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. + * + * This file provides the core support for the IM-PD1 module. + * + * Module / boot parameters. + * id=n impd1.id=n - set the logic module position in stack to 'n' + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static int module_id; + +module_param_named(lmid, module_id, int, 0); +MODULE_PARM_DESC(lmid, "logic module stack position"); + +#define ROM_OFFSET 0x0fffff00 +#define ROM_SIZE 256 + +struct impd1_module { + void *base; +}; + +static const struct icst525_params impd1_vco_params = { + .ref = 24000, /* 24 MHz */ + .vco_max = 200000, /* 200 MHz */ + .vd_min = 12, + .vd_max = 519, + .rd_min = 3, + .rd_max = 120, +}; + +void impd1_set_vco(struct device *dev, int vconr, unsigned long period) +{ + struct impd1_module *impd1 = dev_get_drvdata(dev); + struct icst525_vco vco; + u32 val; + + vco = icst525_ps_to_vco(&impd1_vco_params, period); + + pr_debug("Guessed VCO reg params: S=%d R=%d V=%d\n", + vco.s, vco.r, vco.v); + + val = vco.v | (vco.r << 9) | (vco.s << 16); + + writel(0xa05f, impd1->base + IMPD1_LOCK); + switch (vconr) { + case 1: + writel(val, impd1->base + IMPD1_OSC1); + break; + case 2: + writel(val, impd1->base + IMPD1_OSC2); + break; + } + writel(0, impd1->base + IMPD1_LOCK); + +#if DEBUG + vco.v = val & 0x1ff; + vco.r = (val >> 9) & 0x7f; + vco.s = (val >> 16) & 7; + + pr_debug("IM-PD1: VCO%d clock is %ld kHz\n", + vconr, icst525_khz(&impd1_vco_params, vco)); +#endif +} + +EXPORT_SYMBOL(impd1_set_vco); + +void impd1_tweak_control(struct device *dev, u32 mask, u32 val) +{ + struct impd1_module *impd1 = dev_get_drvdata(dev); + u32 cur; + + val &= mask; + cur = readl(impd1->base + IMPD1_CTRL) & ~mask; + writel(cur | val, impd1->base + IMPD1_CTRL); +} + +EXPORT_SYMBOL(impd1_tweak_control); + +static int impd1_probe(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct resource *res = &pdev->resource[0]; + struct impd1_module *impd1; + void *tmp; + int i, ret; + + if (pdev->id != module_id) + return -EINVAL; + + if (!request_mem_region(res->start + ROM_OFFSET, ROM_SIZE, "ROM")) + return -EBUSY; + if (!request_mem_region(res->start, SZ_4K, "LM registers")) { + ret = -EBUSY; + goto release_rom; + } + + impd1 = kmalloc(sizeof(struct impd1_module), GFP_KERNEL); + if (!impd1) { + ret = -ENOMEM; + goto release_lm; + } + memset(impd1, 0, sizeof(struct impd1_module)); + + impd1->base = ioremap(res->start, SZ_4K); + if (!impd1->base) { + ret = -ENOMEM; + goto free_impd1; + } + + dev_set_drvdata(dev, impd1); + + tmp = ioremap(res->start + 0x0fffff00, SZ_4K); + if (!tmp) { + ret = -ENOMEM; + goto free_impd1; + } + + printk("IM-PD1 found at 0x%08lx\n", res->start); + + for (i = 0; i < 256; i += 4) { + struct amba_device *d; + unsigned long pc_base; + u32 pibval, pc_id; + + pibval = readl(tmp + i); + if (!pibval) + continue; + + pc_base = res->start + ((pibval >> 4) & 0x0ff00000); + pc_id = (pibval >> 8) & 65535; + + if ((pc_id >> 12) != 0) + continue; + + printk(" Device at 0x%08lx is PL%04x\n", pc_base, pc_id); + + d = kmalloc(sizeof(struct amba_device), GFP_KERNEL); + if (!d) + continue; + + memset(d, 0, sizeof(struct amba_device)); + + snprintf(d->dev.bus_id, sizeof(d->dev.bus_id), + "lm%x:%2.2x", pdev->id, pibval >> 24); + + d->dev.parent = &pdev->dev; + d->res.start = pc_base; + d->res.end = pc_base + SZ_4K - 1; + d->res.flags = IORESOURCE_MEM; + d->irq = pdev->resource[1].start; + d->periphid = pc_id | 0x41 << 12; + + ret = amba_device_register(d, res); + if (ret) { + printk("unable to register device %s: %d\n", + d->dev.bus_id, ret); + kfree(d); + } + } + + return 0; + + free_impd1: + if (impd1 && impd1->base) + iounmap(impd1->base); + if (impd1) + kfree(impd1); + release_lm: + release_mem_region(res->start, SZ_4K); + release_rom: + release_mem_region(res->start + ROM_OFFSET, ROM_SIZE); + return ret; +} + +static int impd1_remove(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct resource *res = &pdev->resource[0]; + struct impd1_module *impd1 = dev_get_drvdata(dev); + struct list_head *l, *n; + + list_for_each_safe(l, n, &dev->children) { + struct device *d = list_to_dev(l); + + device_unregister(d); + } + + dev_set_drvdata(dev, NULL); + + iounmap(impd1->base); + kfree(impd1); + release_mem_region(res->start, SZ_4K); + release_mem_region(res->start + ROM_OFFSET, ROM_SIZE); + + return 0; +} + +static struct device_driver impd1_driver = { + .name = "lm", + .bus = &platform_bus_type, + .probe = impd1_probe, + .remove = impd1_remove, +}; + +static int __init impd1_init(void) +{ + return driver_register(&impd1_driver); +} + +static void __exit impd1_exit(void) +{ + driver_unregister(&impd1_driver); +} + +module_init(impd1_init); +module_exit(impd1_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Integrator/IM-PD1 logic module core driver"); +MODULE_AUTHOR("Deep Blue Solutions Ltd"); diff -urN orig/arch/arm/mach-integrator/irq.c linux/arch/arm/mach-integrator/irq.c --- orig/arch/arm/mach-integrator/irq.c Mon May 5 17:38:44 2003 +++ linux/arch/arm/mach-integrator/irq.c Thu Jan 1 01:00:00 1970 @@ -1,76 +0,0 @@ -/* - * linux/arch/arm/mach-integrator/irq.c - * - * Copyright (C) 1999 ARM Limited - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#include -#include - -#include -#include -#include - -#include - -/* - * All IO addresses are mapped onto VA 0xFFFx.xxxx, where x.xxxx - * is the (PA >> 12). - * - * Setup a VA for the Integrator interrupt controller (for header #0, - * just for now). - */ -#define VA_IC_BASE IO_ADDRESS(INTEGRATOR_IC_BASE) -#define VA_CMIC_BASE IO_ADDRESS(INTEGRATOR_HDR_BASE) + INTEGRATOR_HDR_IC_OFFSET - -#define ALLPCI ( (1 << IRQ_PCIINT0) | (1 << IRQ_PCIINT1) | (1 << IRQ_PCIINT2) | (1 << IRQ_PCIINT3) ) - -static void sc_mask_irq(unsigned int irq) -{ - __raw_writel(1 << irq, VA_IC_BASE + IRQ_ENABLE_CLEAR); -} - -static void sc_unmask_irq(unsigned int irq) -{ - __raw_writel(1 << irq, VA_IC_BASE + IRQ_ENABLE_SET); -} - -static struct irqchip sc_chip = { - .ack = sc_mask_irq, - .mask = sc_mask_irq, - .unmask = sc_unmask_irq, -}; - -void __init integrator_init_irq(void) -{ - unsigned int i; - - /* Disable all interrupts initially. */ - /* Do the core module ones */ - __raw_writel(-1, VA_CMIC_BASE + IRQ_ENABLE_CLEAR); - - /* do the header card stuff next */ - __raw_writel(-1, VA_IC_BASE + IRQ_ENABLE_CLEAR); - __raw_writel(-1, VA_IC_BASE + FIQ_ENABLE_CLEAR); - - for (i = 0; i < NR_IRQS; i++) { - if (((1 << i) && INTEGRATOR_SC_VALID_INT) != 0) { - set_irq_chip(i, &sc_chip); - set_irq_handler(i, do_level_IRQ); - set_irq_flags(i, IRQF_VALID | IRQF_PROBE); - } - } -} diff -urN orig/arch/arm/mach-integrator/mm.c linux/arch/arm/mach-integrator/mm.c --- orig/arch/arm/mach-integrator/mm.c Tue May 27 10:04:13 2003 +++ linux/arch/arm/mach-integrator/mm.c Thu Jan 1 01:00:00 1970 @@ -1,70 +0,0 @@ -/* - * linux/arch/arm/mach-integrator/mm.c - * - * Extra MM routines for the ARM Integrator board - * - * Copyright (C) 1999,2000 Arm Limited - * Copyright (C) 2000 Deep Blue Solutions 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. - * - * 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 - -/* - * Logical Physical - * e8000000 40000000 PCI memory PHYS_PCI_MEM_BASE (max 512M) - * ec000000 61000000 PCI config space PHYS_PCI_CONFIG_BASE (max 16M) - * ed000000 62000000 PCI V3 regs PHYS_PCI_V3_BASE (max 64k) - * ee000000 60000000 PCI IO PHYS_PCI_IO_BASE (max 16M) - * ef000000 Cache flush - * f1000000 10000000 Core module registers - * f1100000 11000000 System controller registers - * f1200000 12000000 EBI registers - * f1300000 13000000 Counter/Timer - * f1400000 14000000 Interrupt controller - * f1500000 15000000 RTC - * f1600000 16000000 UART 0 - * f1700000 17000000 UART 1 - * f1a00000 1a000000 Debug LEDs - * f1b00000 1b000000 GPIO - */ - -static struct map_desc integrator_io_desc[] __initdata = { - { IO_ADDRESS(INTEGRATOR_HDR_BASE), INTEGRATOR_HDR_BASE, SZ_4K, MT_DEVICE }, - { IO_ADDRESS(INTEGRATOR_SC_BASE), INTEGRATOR_SC_BASE, SZ_4K, MT_DEVICE }, - { IO_ADDRESS(INTEGRATOR_EBI_BASE), INTEGRATOR_EBI_BASE, SZ_4K, MT_DEVICE }, - { IO_ADDRESS(INTEGRATOR_CT_BASE), INTEGRATOR_CT_BASE, SZ_4K, MT_DEVICE }, - { IO_ADDRESS(INTEGRATOR_IC_BASE), INTEGRATOR_IC_BASE, SZ_4K, MT_DEVICE }, - { IO_ADDRESS(INTEGRATOR_RTC_BASE), INTEGRATOR_RTC_BASE, SZ_4K, MT_DEVICE }, - { IO_ADDRESS(INTEGRATOR_UART0_BASE), INTEGRATOR_UART0_BASE, SZ_4K, MT_DEVICE }, - { IO_ADDRESS(INTEGRATOR_UART1_BASE), INTEGRATOR_UART1_BASE, SZ_4K, MT_DEVICE }, - { IO_ADDRESS(INTEGRATOR_DBG_BASE), INTEGRATOR_DBG_BASE, SZ_4K, MT_DEVICE }, - { IO_ADDRESS(INTEGRATOR_GPIO_BASE), INTEGRATOR_GPIO_BASE, SZ_4K, MT_DEVICE }, - { PCI_MEMORY_VADDR, PHYS_PCI_MEM_BASE, SZ_16M, MT_DEVICE }, - { PCI_CONFIG_VADDR, PHYS_PCI_CONFIG_BASE, SZ_16M, MT_DEVICE }, - { PCI_V3_VADDR, PHYS_PCI_V3_BASE, SZ_64K, MT_DEVICE }, - { PCI_IO_VADDR, PHYS_PCI_IO_BASE, SZ_64K, MT_DEVICE } -}; - -void __init integrator_map_io(void) -{ - iotable_init(integrator_io_desc, ARRAY_SIZE(integrator_io_desc)); -} diff -urN orig/arch/arm/mach-integrator/pci_v3.c linux/arch/arm/mach-integrator/pci_v3.c --- orig/arch/arm/mach-integrator/pci_v3.c Tue Feb 11 16:09:38 2003 +++ linux/arch/arm/mach-integrator/pci_v3.c Fri May 30 11:12:12 2003 @@ -441,7 +441,7 @@ return 1; } -static void v3_irq(int irq, void *devid, struct pt_regs *regs) +static irqreturn_t v3_irq(int irq, void *devid, struct pt_regs *regs) { #ifdef CONFIG_DEBUG_LL unsigned long pc = instruction_pointer(regs); @@ -469,6 +469,7 @@ printascii(buf); } #endif + return IRQ_HANDLED; } int __init pci_v3_setup(int nr, struct pci_sys_data *sys) diff -urN orig/arch/arm/mach-integrator/time.c linux/arch/arm/mach-integrator/time.c --- orig/arch/arm/mach-integrator/time.c Mon Nov 18 09:51:56 2002 +++ linux/arch/arm/mach-integrator/time.c Fri May 9 23:35:02 2003 @@ -7,12 +7,19 @@ * 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 #define RTC_DR (IO_ADDRESS(INTEGRATOR_RTC_BASE) + 0) #define RTC_MR (IO_ADDRESS(INTEGRATOR_RTC_BASE) + 4) @@ -23,7 +30,10 @@ #define RTC_CR_MIE 0x00000001 +#define rtc_epoch 1900UL + extern int (*set_rtc)(void); +static void rtc_update(unsigned long events); static int integrator_set_rtc(void) { @@ -31,16 +41,468 @@ return 1; } -static int integrator_rtc_init(void) +static const unsigned char days_in_month[] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400) +#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400)) + +static int month_days(unsigned int month, unsigned int year) +{ + return days_in_month[month] + (LEAP_YEAR(year) && month == 1); +} + +/* + * Convert seconds since 01-01-1970 00:00:00 to Gregorian date. + */ +static void rtc_time_to_tm(unsigned long time, struct rtc_time *tm) +{ + int days, month, year; + + days = (time / 86400); + time -= days * 86400; + + tm->tm_wday = (time + 4) % 7; + + year = 1970 + days / 365; + days -= (year - 1970) * 365 + + LEAPS_THRU_END_OF(year - 1) + - LEAPS_THRU_END_OF(1970 - 1); + if (days < 0) { + year -= 1; + days += 365 + LEAP_YEAR(year); + } + tm->tm_year = year - rtc_epoch; + tm->tm_yday = days + 1; + + for (month = 0; month < 11; month++) { + int newdays; + + newdays = days - month_days(month, year); + if (newdays < 0) + break; + days = newdays; + } + tm->tm_mon = month; + tm->tm_mday = days; + + tm->tm_hour = time / 3600; + time -= tm->tm_hour * 3600; + tm->tm_min = time / 60; + tm->tm_sec = time - tm->tm_min * 60; +} + +/* + * Convert Gregorian date to seconds since 01-01-1970 00:00:00. + */ +static int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time) +{ + unsigned int yrs = tm->tm_year + rtc_epoch; + + if (yrs < 1970 || + tm->tm_mon >= 12 || + tm->tm_mday < 1 || + tm->tm_mday > month_days(tm->tm_mon, yrs) || + tm->tm_hour >= 24 || + tm->tm_min >= 60 || + tm->tm_sec >= 60) + return -EINVAL; + + *time = mktime(yrs, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + return 0; +} + +static inline void rtc_alarm_read(struct rtc_time *tm) +{ + rtc_time_to_tm(readl(RTC_MR), tm); +} + +static inline int rtc_alarm_set(struct rtc_time *tm) +{ + unsigned long time; + int ret; + + ret = rtc_tm_to_time(tm, &time); + if (ret == 0) + writel(time, RTC_MR); + return ret; +} + +static inline void rtc_time_read(struct rtc_time *tm) +{ + rtc_time_to_tm(readl(RTC_DR), tm); +} + +/* + * Set the RTC time. Unfortunately we can't acurately set + * the point at which the counter updates. + * + * Also, since RTC_LR is transferred to RTC_CR on next rising + * edge of the 1Hz clock, we must write the time one second + * in advance. + */ +static inline int rtc_time_set(struct rtc_time *tm) { + unsigned long time; + int ret; + + ret = rtc_tm_to_time(tm, &time); + if (ret == 0) + writel(time + 1, RTC_LR); + + return ret; +} + +#define rtc_alarm_off() -EINVAL +#define rtc_alarm_on() -EINVAL + +static unsigned long rtc_enable; + +static inline int rtc_update_on(void) +{ + rtc_enable |= RTC_UIE; + + writel(0, RTC_EOI); + writel(readl(RTC_DR) + 1, RTC_MR); + writel(RTC_CR_MIE, RTC_CR); + + return 0; +} + +static inline int rtc_update_off(void) +{ + rtc_enable &= ~RTC_UIE; + + return 0; +} + +static irqreturn_t rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned long alarm = readl(RTC_MR); + unsigned long now, match, events = 0; + + now = readl(RTC_DR); + match = readl(RTC_MR); + + if (rtc_enable & RTC_UIE) { + writel(now + 1, RTC_MR); + events |= RTC_UF; + } + + writel(0, RTC_EOI); + + if (rtc_enable == 0) + writel(0, RTC_CR); + + if (events) + rtc_update(events); + + return IRQ_HANDLED; +} + +static inline int rtc_request_irqs(void) +{ + return request_irq(IRQ_RTCINT, rtc_interrupt, SA_INTERRUPT, "rtc", rtc_interrupt); +} + +static inline void rtc_free_irqs(void) +{ + writel(0, RTC_CR); + free_irq(IRQ_RTCINT, rtc_interrupt); +} + + +/* + * What follows here is a generic RTC driver implementation. + */ +static DECLARE_WAIT_QUEUE_HEAD(rtc_wait); +static struct fasync_struct *rtc_async_queue; +static unsigned long rtc_irq_data; + +static void rtc_update(unsigned long events) +{ + spin_lock(&rtc_lock); + rtc_irq_data = (rtc_irq_data & ~0xff) + 0x100 + events; + spin_unlock(&rtc_lock); + + wake_up_interruptible(&rtc_wait); + kill_fasync(&rtc_async_queue, SIGIO, POLL_IN); +} + +static ssize_t +rtc_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long data; + ssize_t ret; + + if (count < sizeof(unsigned long)) + return -EINVAL; + + add_wait_queue(&rtc_wait, &wait); + do { + set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irq(&rtc_lock); + data = rtc_irq_data; + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + + if (data != 0) { + ret = 0; + break; + } + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + break; + } + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + schedule(); + } while (1); + set_current_state(TASK_RUNNING); + remove_wait_queue(&rtc_wait, &wait); + + if (ret == 0) { + ret = put_user(data, (unsigned long *)buf); + if (ret == 0) + ret = sizeof(unsigned long); + } + return ret; +} + +static unsigned int rtc_poll(struct file *file, poll_table *wait) +{ + unsigned long data; + + poll_wait(file, &rtc_wait, wait); + + spin_lock_irq(&rtc_lock); + data = rtc_irq_data; + spin_unlock_irq(&rtc_lock); + + return data != 0 ? POLLIN | POLLRDNORM : 0; +} + +static int +rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct rtc_time tm; + int ret; + + switch (cmd) { + case RTC_AIE_OFF: + ret = rtc_alarm_off(); + break; + + case RTC_AIE_ON: + ret = rtc_alarm_on(); + break; + + case RTC_UIE_OFF: + spin_lock_irq(&rtc_lock); + rtc_irq_data = 0; + ret = rtc_update_off(); + spin_unlock_irq(&rtc_lock); + break; + + case RTC_UIE_ON: + spin_lock_irq(&rtc_lock); + ret = rtc_update_on(); + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + break; + + case RTC_ALM_READ: + rtc_alarm_read(&tm); + ret = copy_to_user((void *)arg, &tm, sizeof(tm)); + if (ret) + ret = -EFAULT; + break; + + case RTC_ALM_SET: + ret = copy_from_user(&tm, (void *)arg, sizeof(tm)); + if (ret == 0) + ret = rtc_alarm_set(&tm); + else + ret = -EFAULT; + break; + + case RTC_RD_TIME: + rtc_time_read(&tm); + ret = copy_to_user((void *)arg, &tm, sizeof(tm)); + if (ret) + ret = -EFAULT; + break; + + case RTC_SET_TIME: + if (!capable(CAP_SYS_TIME)) { + ret = -EACCES; + break; + } + ret = copy_from_user(&tm, (void *)arg, sizeof(tm)); + if (ret == 0) + ret = rtc_time_set(&tm); + else + ret = -EFAULT; + break; + +#ifndef rtc_epoch + case RTC_EPOCH_SET: + /* + * There were no RTC clocks before 1900. + */ + if (arg < 1900) { + ret = -EINVAL; + break; + } + if (!capable(CAP_SYS_TIME)) { + ret = -EACCES; + break; + } + rtc_epoch = arg; + ret = 0; + break; +#endif + + case RTC_EPOCH_READ: + ret = put_user(rtc_epoch, (unsigned long *)arg); + break; + + default: + ret = -ENOTTY; + } + + return ret; +} + +static int rtc_open(struct inode *inode, struct file *file) +{ + spin_lock_irq(&rtc_lock); + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + + return 0; +} + +static int rtc_release(struct inode *inode, struct file *file) +{ + spin_lock_irq(&rtc_lock); + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + + 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 struct miscdevice rtc_miscdev = { + .minor = RTC_MINOR, + .name = "rtc", + .fops = &rtc_fops, +}; + +static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + struct rtc_time tm; + char *p = page; + int len; + + rtc_time_read(&tm); + + p += sprintf(p, + "rtc_time\t: %02d:%02d:%02d\n" + "rtc_date\t: %04d-%02d-%02d\n" + "rtc_epoch\t: %04lu\n", + tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + rtc_epoch); + + rtc_alarm_read(&tm); + p += sprintf(p, + "alarm\t\t: "); + + if (tm.tm_hour <= 24) + p += sprintf(p, "%02d:", tm.tm_hour); + else + p += sprintf(p, "**:"); + if (tm.tm_min <= 59) + p += sprintf(p, "%02d:", tm.tm_min); + else + p += sprintf(p, "**:"); + if (tm.tm_sec <= 59) + p += sprintf(p, "%02d\n", tm.tm_sec); + else + p += sprintf(p, "**\n"); + + len = p - page; + if (len <= off + count) + *eof = 1; + *start = page + off; + len -= off; + if (len > count) + len = count; + if (len < 0) + len = 0; + return len; +} + +static int __init integrator_rtc_init(void) +{ + int ret; + __raw_writel(0, RTC_CR); __raw_writel(0, RTC_EOI); xtime.tv_sec = __raw_readl(RTC_DR); - set_rtc = integrator_set_rtc; + ret = rtc_request_irqs(); + if (ret) + goto out; + ret = misc_register(&rtc_miscdev); + if (ret) + goto free_out; + + create_proc_read_entry("driver/rtc", 0, 0, rtc_read_proc, NULL); + set_rtc = integrator_set_rtc; return 0; + + free_out: + rtc_free_irqs(); + out: + return ret; +} + +static void __exit integrator_rtc_exit(void) +{ + set_rtc = NULL; + + rtc_free_irqs(); + + remove_proc_entry("driver/rtc", NULL); + misc_deregister(&rtc_miscdev); } -__initcall(integrator_rtc_init); +module_init(integrator_rtc_init); +module_exit(integrator_rtc_exit); diff -urN orig/arch/arm/mach-pxa/lubbock.c linux/arch/arm/mach-pxa/lubbock.c --- orig/arch/arm/mach-pxa/lubbock.c Tue May 27 10:04:13 2003 +++ linux/arch/arm/mach-pxa/lubbock.c Wed Jun 4 18:34:17 2003 @@ -88,9 +88,36 @@ set_irq_type(IRQ_GPIO(0), IRQT_FALLING); } +static struct resource sa1111_resources[] = { + [0] = { + .start = 0x10000000, + .end = 0x10001fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = LUBBOCK_SA1111_IRQ, + .end = LUBBOCK_SA1111_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device sa1111_device = { + .name = "sa1111", + .id = 0, + .dev = { + .name = "Intel Corporation SA1111", + }, + .num_resources = ARRAY_SIZE(sa1111_resources), + .resource = sa1111_resources, +}; + +static struct platform_device *devices[] __initdata = { + &sa1111_device, +}; + static int __init lubbock_init(void) { - return sa1111_init(0x10000000, LUBBOCK_SA1111_IRQ); + return platform_add_devices(devices, ARRAY_SIZE(devices)); } subsys_initcall(lubbock_init); diff -urN orig/arch/arm/mach-sa1100/Makefile linux/arch/arm/mach-sa1100/Makefile --- orig/arch/arm/mach-sa1100/Makefile Mon Mar 17 23:16:09 2003 +++ linux/arch/arm/mach-sa1100/Makefile Sun Feb 23 15:40:22 2003 @@ -3,7 +3,7 @@ # # Common support -obj-y := generic.o irq.o dma.o +obj-y := generic.o irq.o dma.o nmi-oopser.o obj-m := obj-n := obj- := @@ -88,7 +88,7 @@ obj-$(CONFIG_LEDS) += $(led-y) # SA1110 USB client support -#obj-$(CONFIG_SA1100_USB) += usb/ +obj-$(CONFIG_SA1100_USB) += usb/ # Miscelaneous functions obj-$(CONFIG_PM) += pm.o sleep.o diff -urN orig/arch/arm/mach-sa1100/adsbitsy.c linux/arch/arm/mach-sa1100/adsbitsy.c --- orig/arch/arm/mach-sa1100/adsbitsy.c Mon Nov 18 09:51:56 2002 +++ linux/arch/arm/mach-sa1100/adsbitsy.c Wed Jun 4 18:34:06 2003 @@ -27,6 +27,36 @@ #include "generic.h" +static struct resource sa1111_resources[] = { + [0] = { + .start = 0x18000000, + .end = 0x18001fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_GPIO0, + .end = IRQ_GPIO0, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 sa1111_dmamask = 0xffffffffUL; + +static struct platform_device sa1111_device = { + .name = "sa1111", + .id = 0, + .dev = { + .name = "Intel Corporation SA1111", + .dma_mask = &sa1111_dmamask, + }, + .num_resources = ARRAY_SIZE(sa1111_resources), + .resource = sa1111_resources, +}; + +static struct platform_device *devices[] __initdata = { + &sa1111_device, +}; + static int __init adsbitsy_init(void) { int ret; @@ -50,18 +80,17 @@ /* * Probe for SA1111. */ - ret = sa1111_init(0x18000000, IRQ_GPIO0); + ret = platform_add_devices(devices, ARRAY_SIZE(devices)); if (ret < 0) return ret; /* * Enable PWM control for LCD */ - sa1111_enable_device(SKPCR_PWMCLKEN); - SKPWM0 = 0x7F; // VEE - SKPEN0 = 1; - SKPWM1 = 0x01; // Backlight - SKPEN1 = 1; + sa1111_pwm_dutycycle(0, 127); // VEE + sa1111_pwm_enable(0); + sa1111_pwm_dutycycle(1, 1); // Backlight + sa1111_pwm_enable(1); return 0; } @@ -79,6 +108,8 @@ { 0xf4000000, 0x18000000, 0x00800000, MT_DEVICE } /* SA1111 */ }; +#error Old code. Someone needs to decide what to do about this. +#if 0 static int adsbitsy_uart_open(struct uart_port *port, struct uart_info *info) { if (port->mapbase == _Ser1UTCR0) { @@ -95,17 +126,13 @@ } return 0; } - -static struct sa1100_port_fns adsbitsy_port_fns __initdata = { - .open = adsbitsy_uart_open, -}; +#endif static void __init adsbitsy_map_io(void) { sa1100_map_io(); iotable_init(adsbitsy_io_desc, ARRAY_SIZE(adsbitsy_io_desc)); - sa1100_register_uart_fns(&adsbitsy_port_fns); sa1100_register_uart(0, 3); sa1100_register_uart(1, 1); sa1100_register_uart(2, 2); diff -urN orig/arch/arm/mach-sa1100/assabet.c linux/arch/arm/mach-sa1100/assabet.c --- orig/arch/arm/mach-sa1100/assabet.c Mon May 5 17:38:44 2003 +++ linux/arch/arm/mach-sa1100/assabet.c Sat Apr 26 13:31:56 2003 @@ -67,12 +67,14 @@ static void assabet_backlight_power(int on) { +#if 0 #ifndef ASSABET_PAL_VIDEO if (on) ASSABET_BCR_set(ASSABET_BCR_LIGHT_ON); else #endif ASSABET_BCR_clear(ASSABET_BCR_LIGHT_ON); +#endif } /* @@ -83,6 +85,7 @@ */ static void assabet_lcd_power(int on) { +#if 1 #ifndef ASSABET_PAL_VIDEO if (on) { ASSABET_BCR_set(ASSABET_BCR_LCD_ON); @@ -90,6 +93,7 @@ } else #endif ASSABET_BCR_clear(ASSABET_BCR_LCD_ON); +#endif } static int __init assabet_init(void) @@ -196,8 +200,13 @@ map_sa1100_gpio_regs(); get_assabet_scr(); - if (machine_has_neponset()) + if (machine_has_neponset()) { printk("Neponset expansion board detected\n"); + } else { + strcpy(*cmdline, "console=ttySA0,38400n8 cpufreq=221200 " + "rw root=/dev/mtdblock3 load_ramdisk=1 prompt_ramdisk=0 " + "noinitrd mem=32M"); + } } diff -urN orig/arch/arm/mach-sa1100/badge4.c linux/arch/arm/mach-sa1100/badge4.c --- orig/arch/arm/mach-sa1100/badge4.c Sun Apr 20 16:31:37 2003 +++ linux/arch/arm/mach-sa1100/badge4.c Wed Jun 4 18:34:06 2003 @@ -35,6 +35,36 @@ #include "generic.h" +static struct resource sa1111_resources[] = { + [0] = { + .start = BADGE4_SA1111_BASE, + .end = BADGE4_SA1111_BASE + 0x00001fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = BADGE4_IRQ_GPIO_SA1111, + .end = BADGE4_IRQ_GPIO_SA1111, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 sa1111_dmamask = 0xffffffffUL; + +static struct platform_device sa1111_device = { + .name = "sa1111", + .id = 0, + .dev = { + .name = "Intel Corporation SA1111", + .dma_mask = &sa1111_dmamask; + }, + .num_resources = ARRAY_SIZE(sa1111_resources), + .resource = sa1111_resources, +}; + +static struct platform_device *devices[] __initdata = { + &sa1111_device, +}; + static int __init badge4_sa1111_init(void) { /* @@ -46,7 +76,7 @@ /* * Probe for SA1111. */ - return sa1111_init(BADGE4_SA1111_BASE, BADGE4_IRQ_GPIO_SA1111); + return platform_add_devices(devices, ARRAY_SIZE(devices)); } diff -urN orig/arch/arm/mach-sa1100/dma.c linux/arch/arm/mach-sa1100/dma.c --- orig/arch/arm/mach-sa1100/dma.c Mon Nov 18 09:51:56 2002 +++ linux/arch/arm/mach-sa1100/dma.c Tue May 20 00:11:15 2003 @@ -42,7 +42,7 @@ static spinlock_t dma_list_lock; -static void 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) { dma_regs_t *dma_regs = dev_id; sa1100_dma_t *dma = dma_chan + (((u_int)dma_regs >> 5) & 7); @@ -60,6 +60,7 @@ if (status & DCSR_DONEB) dma->callback(dma->data); } + return IRQ_HANDLED; } diff -urN orig/arch/arm/mach-sa1100/generic.c linux/arch/arm/mach-sa1100/generic.c --- orig/arch/arm/mach-sa1100/generic.c Mon Mar 17 23:16:09 2003 +++ linux/arch/arm/mach-sa1100/generic.c Wed Jun 11 20:02:57 2003 @@ -16,11 +16,13 @@ #include #include #include +#include #include #include #include #include +#include #include "generic.h" @@ -128,13 +130,151 @@ PMCR = PMCR_SF; } +static struct resource sa11x0udc_resources[] = { + [0] = { + .start = 0x80000000, + .end = 0x8000ffff, + .flags = IORESOURCE_MEM, + }, +}; + +static u64 sa11x0udc_dma_mask = 0xffffffffUL; + +static struct platform_device sa11x0udc_device = { + .name = "sa11x0-udc", + .id = 0, + .dev = { + .name = "Intel Corporation SA11x0 [UDC]", + .dma_mask = &sa11x0udc_dma_mask, + }, + .num_resources = ARRAY_SIZE(sa11x0udc_resources), + .resource = sa11x0udc_resources, +}; + +static struct resource sa11x0uart1_resources[] = { + [0] = { + .start = 0x80010000, + .end = 0x8001ffff, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device sa11x0uart1_device = { + .name = "sa11x0-uart", + .id = 1, + .dev = { + .name = "Intel Corporation SA11x0 [UART]", + }, + .num_resources = ARRAY_SIZE(sa11x0uart1_resources), + .resource = sa11x0uart1_resources, +}; + +static struct resource sa11x0uart3_resources[] = { + [0] = { + .start = 0x80050000, + .end = 0x8005ffff, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device sa11x0uart3_device = { + .name = "sa11x0-uart", + .id = 3, + .dev = { + .name = "Intel Corporation SA11x0 [UART]", + }, + .num_resources = ARRAY_SIZE(sa11x0uart3_resources), + .resource = sa11x0uart3_resources, +}; + +static struct resource sa11x0mcp_resources[] = { + [0] = { + .start = 0x80060000, + .end = 0x8006ffff, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device sa11x0mcp_device = { + .name = "sa11x0-mcp", + .id = 0, + .dev = { + .name = "Intel Corporation SA11x0 [MCP]", + }, + .num_resources = ARRAY_SIZE(sa11x0mcp_resources), + .resource = sa11x0mcp_resources, +}; + +static struct resource sa11x0ssp_resources[] = { + [0] = { + .start = 0x80070000, + .end = 0x8007ffff, + .flags = IORESOURCE_MEM, + }, +}; + +static u64 sa11x0ssp_dma_mask = 0xffffffffUL; + +static struct platform_device sa11x0ssp_device = { + .name = "sa11x0-ssp", + .id = 0, + .dev = { + .name = "Intel Corporation SA11x0 [SSP]", + .dma_mask = &sa11x0ssp_dma_mask, + }, + .num_resources = ARRAY_SIZE(sa11x0ssp_resources), + .resource = sa11x0ssp_resources, +}; + +static struct resource sa11x0fb_resources[] = { + [0] = { + .start = 0xb0100000, + .end = 0xb010ffff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_LCD, + .end = IRQ_LCD, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device sa11x0fb_device = { + .name = "sa11x0-fb", + .id = 0, + .dev = { + .name = "Intel Corporation SA11x0 [LCD]", + }, + .num_resources = ARRAY_SIZE(sa11x0fb_resources), + .resource = sa11x0fb_resources, +}; + +static struct platform_device sa11x0pcmcia_device = { + .name = "sa11x0-pcmcia", + .id = 0, + .dev = { + .name = "Intel Corporation SA11x0 [PCMCIA]", + }, +}; + +static struct platform_device *sa11x0_devices[] __initdata = { + &sa11x0udc_device, + &sa11x0uart1_device, + &sa11x0uart3_device, + &sa11x0mcp_device, + &sa11x0ssp_device, + &sa11x0pcmcia_device, + &sa11x0fb_device, +}; + static int __init sa1100_init(void) { pm_power_off = sa1100_power_off; - return 0; + + return platform_add_devices(sa11x0_devices, ARRAY_SIZE(sa11x0_devices)); } -core_initcall(sa1100_init); +arch_initcall(sa1100_init); void (*sa1100fb_backlight_power)(int on); void (*sa1100fb_lcd_power)(int on); diff -urN orig/arch/arm/mach-sa1100/graphicsmaster.c linux/arch/arm/mach-sa1100/graphicsmaster.c --- orig/arch/arm/mach-sa1100/graphicsmaster.c Mon Nov 18 09:51:56 2002 +++ linux/arch/arm/mach-sa1100/graphicsmaster.c Wed Jun 4 18:34:06 2003 @@ -24,6 +24,36 @@ #include "generic.h" +static struct resource sa1111_resources[] = { + [0] = { + .start = 0x18000000, + .end = 0x18001fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = ADS_EXT_IRQ(0), + .end = ADS_EXT_IRQ(0), + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 sa1111_dmamask = 0xffffffffUL; + +static struct platform_device sa1111_device = { + .name = "sa1111", + .id = 0, + .dev = { + .name = "Intel Corporation SA1111", + .dma_mask = &sa1111_dmamask, + }, + .num_resources = ARRAY_SIZE(sa1111_resources), + .resource = sa1111_resources, +}; + +static struct platform_device *devices[] __initdata = { + &sa1111_device, +}; + static int __init graphicsmaster_init(void) { int ret; @@ -40,18 +70,17 @@ /* * Probe for SA1111. */ - ret = sa1111_init(0x18000000, ADS_EXT_IRQ(0)); + ret = platform_add_devices(devices, ARRAY_SIZE(devices)); if (ret < 0) return ret; /* * Enable PWM control for LCD */ - sa1111_enable_device(SKPCR_PWMCLKEN); - SKPWM0 = 0x7F; // VEE - SKPEN0 = 1; - SKPWM1 = 0x01; // Backlight - SKPEN1 = 1; + sa1111_pwm_dutycycle(0, 127); // VEE + sa1111_pwm_enable(0); + sa1111_pwm_dutycycle(1, 1); // Backlight + sa1111_pwm_enable(1); return 0; } diff -urN orig/arch/arm/mach-sa1100/itsy.c linux/arch/arm/mach-sa1100/itsy.c --- orig/arch/arm/mach-sa1100/itsy.c Sun Jun 9 16:01:36 2002 +++ linux/arch/arm/mach-sa1100/itsy.c Thu May 30 13:44:21 2002 @@ -1,6 +1,8 @@ /* * linux/arch/arm/mach-sa1100/itsy.c */ +#error Set the default command line to: +#error "mem=16M@0xc0000000 mem=16M@0xc8000000 mem=16M@0xd0000000 mem=16M@0xd8000000" #include #include diff -urN orig/arch/arm/mach-sa1100/jornada720.c linux/arch/arm/mach-sa1100/jornada720.c --- orig/arch/arm/mach-sa1100/jornada720.c Thu Jan 9 10:39:41 2003 +++ linux/arch/arm/mach-sa1100/jornada720.c Wed Jun 4 18:34:06 2003 @@ -24,6 +24,36 @@ #define JORTUCR_VAL 0x20000400 +static struct resource sa1111_resources[] = { + [0] = { + .start = 0x40000000, + .end = 0x40001fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_GPIO1, + .end = IRQ_GPIO1, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 sa1111_dmamask = 0xffffffffUL; + +static struct platform_device sa1111_device = { + .name = "sa1111", + .id = 0, + .dev = { + .name = "Intel Corporation SA1111", + .dma_mask = &sa1111_dmamask, + }, + .num_resources = ARRAY_SIZE(sa1111_resources), + .resource = sa1111_resources, +}; + +static struct platform_device *devices[] __initdata = { + &sa1111_device, +}; + static int __init jornada720_init(void) { int ret = -ENODEV; @@ -43,7 +73,7 @@ PPSR &= ~(PPC_LDD3 | PPC_LDD4); PPDR |= PPC_LDD3 | PPC_LDD4; - ret = sa1111_init(0x40000000, IRQ_GPIO1); + ret = platform_add_devices(devices, ARRAY_SIZE(devices)); } return ret; } diff -urN orig/arch/arm/mach-sa1100/nanoengine.c linux/arch/arm/mach-sa1100/nanoengine.c --- orig/arch/arm/mach-sa1100/nanoengine.c Sun Jun 9 16:01:36 2002 +++ linux/arch/arm/mach-sa1100/nanoengine.c Thu May 30 13:43:44 2002 @@ -1,6 +1,8 @@ /* * linux/arch/arm/mach-sa1100/nanoengine.c */ +#error Add the following command line: +#error "mem=32M root=/dev/ram ramdisk=8192 initrd=0xc0800000,4M" #include #include diff -urN orig/arch/arm/mach-sa1100/neponset.c linux/arch/arm/mach-sa1100/neponset.c --- orig/arch/arm/mach-sa1100/neponset.c Thu Jan 9 10:39:41 2003 +++ linux/arch/arm/mach-sa1100/neponset.c Wed Jun 4 18:34:06 2003 @@ -79,33 +79,6 @@ } } -static inline void __init neponset_init_irq(void) -{ - /* - * Install handler for GPIO25. - */ - set_irq_type(IRQ_GPIO25, IRQT_RISING); - set_irq_chained_handler(IRQ_GPIO25, neponset_irq_handler); - - /* - * We would set IRQ_GPIO25 to be a wake-up IRQ, but - * unfortunately something on the Neponset activates - * this IRQ on sleep (ethernet?) - */ -#if 0 - enable_irq_wake(IRQ_GPIO25); -#endif - - /* - * Setup other Neponset IRQs. SA1111 will be done by the - * generic SA1111 code. - */ - set_irq_handler(IRQ_NEPONSET_SMC9196, do_simple_IRQ); - set_irq_flags(IRQ_NEPONSET_SMC9196, IRQF_VALID | IRQF_PROBE); - set_irq_handler(IRQ_NEPONSET_USAR, do_simple_IRQ); - set_irq_flags(IRQ_NEPONSET_USAR, IRQF_VALID | IRQF_PROBE); -} - static void neponset_set_mctrl(struct uart_port *port, u_int mctrl) { u_int mdm_ctl0 = MDM_CTL_0; @@ -164,6 +137,42 @@ .get_mctrl = neponset_get_mctrl, }; +static int neponset_probe(struct device *dev) +{ + sa1100_register_uart_fns(&neponset_port_fns); + + /* + * Install handler for GPIO25. + */ + set_irq_type(IRQ_GPIO25, IRQT_RISING); + set_irq_chained_handler(IRQ_GPIO25, neponset_irq_handler); + + /* + * We would set IRQ_GPIO25 to be a wake-up IRQ, but + * unfortunately something on the Neponset activates + * this IRQ on sleep (ethernet?) + */ +#if 0 + enable_irq_wake(IRQ_GPIO25); +#endif + + /* + * Setup other Neponset IRQs. SA1111 will be done by the + * generic SA1111 code. + */ + set_irq_handler(IRQ_NEPONSET_SMC9196, do_simple_IRQ); + set_irq_flags(IRQ_NEPONSET_SMC9196, IRQF_VALID | IRQF_PROBE); + set_irq_handler(IRQ_NEPONSET_USAR, do_simple_IRQ); + set_irq_flags(IRQ_NEPONSET_USAR, IRQF_VALID | IRQF_PROBE); + + /* + * Disable GPIO 0/1 drivers so the buttons work on the module. + */ + NCR_0 = NCR_GP01_OFF; + + return 0; +} + /* * LDM power management. */ @@ -201,27 +210,63 @@ static struct device_driver neponset_device_driver = { .name = "neponset", - .bus = &system_bus_type, + .bus = &platform_bus_type, + .probe = neponset_probe, .suspend = neponset_suspend, .resume = neponset_resume, }; -static struct sys_device neponset_device = { - .name = "NEPONSET", +static struct resource neponset_resources[] = { + [0] = { + .start = 0x10000000, + .end = 0x17ffffff, + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device neponset_device = { + .name = "neponset", .id = 0, - .root = NULL, .dev = { .name = "Neponset", - .bus_id = "neponset", - .bus = &system_bus_type, - .driver = &neponset_device_driver, + }, + .num_resources = ARRAY_SIZE(neponset_resources), + .resource = neponset_resources, +}; + +static struct resource sa1111_resources[] = { + [0] = { + .start = 0x40000000, + .end = 0x40001fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_NEPONSET_SA1111, + .end = IRQ_NEPONSET_SA1111, + .flags = IORESOURCE_IRQ, }, }; +static u64 sa1111_dmamask = 0xffffffffUL; + +static struct platform_device sa1111_device = { + .name = "sa1111", + .id = 0, + .dev = { + .name = "Intel Corporation SA1111", + .dma_mask = &sa1111_dmamask, + }, + .num_resources = ARRAY_SIZE(sa1111_resources), + .resource = sa1111_resources, +}; + +static struct platform_device *devices[] __initdata = { + &neponset_device, + &sa1111_device, +}; + static int __init neponset_init(void) { - int ret; - driver_register(&neponset_device_driver); /* @@ -248,29 +293,7 @@ return -ENODEV; } - ret = sys_device_register(&neponset_device); - if (ret) - return ret; - - sa1100_register_uart_fns(&neponset_port_fns); - - neponset_init_irq(); - - /* - * Disable GPIO 0/1 drivers so the buttons work on the module. - */ - NCR_0 = NCR_GP01_OFF; - - /* - * Neponset has SA1111 connected to CS4. We know that after - * reset the chip will be configured for variable latency IO. - */ - /* FIXME: setup MSC2 */ - - /* - * Probe and initialise the SA1111. - */ - return sa1111_init(0x40000000, IRQ_NEPONSET_SA1111); + return platform_add_devices(devices, ARRAY_SIZE(devices)); } subsys_initcall(neponset_init); diff -urN orig/arch/arm/mach-sa1100/nmi-oopser.c linux/arch/arm/mach-sa1100/nmi-oopser.c --- orig/arch/arm/mach-sa1100/nmi-oopser.c Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/mach-sa1100/nmi-oopser.c Tue Apr 22 23:23:10 2003 @@ -0,0 +1,114 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static void *nmi_stack; + +asm(" \n\ +nmi_start: \n\ + mrs r8, spsr \n\ + ldr r9, .Lstack \n\ + ldr sp, [r9] \n\ + sub sp, sp, #18 * 4 \n\ + str r8, [sp, #16 * 4] \n\ + str lr, [sp, #15 * 4] \n\ + stmia sp, {r0 - r7} \n\ + add r0, sp, #8 * 4 \n\ + mrs r2, cpsr \n\ + bic r1, r2, #0x1f \n\ + orr r1, r1, #0x13 \n\ + msr cpsr_c, r1 \n\ + mov r0, r0 \n\ + stmia r0, {r8 - lr} \n\ + mov r0, r0 \n\ + msr cpsr_c, r2 \n\ + mov r0, r0 \n\ + mov r0, sp \n\ + mov lr, pc \n\ + ldr pc, .Lfn \n\ + ldmia sp, {r0 - r7} \n\ + ldr r8, [sp, #16 * 4] \n\ + ldr lr, [sp, #15 * 4] \n\ + add sp, sp, #18 * 4 \n\ + msr spsr, r8 \n\ + movs pc, lr \n\ + \n\ +.Lstack: .long nmi_stack \n\ +.Lfn: .long nmi_fn \n\ +nmi_end:"); + +extern unsigned char nmi_start, nmi_end; + +static void __attribute__((unused)) nmi_fn(struct pt_regs *regs) +{ + struct thread_info *thread; + struct task_struct *tsk; + unsigned long osmr0, osmr1, oscr, ossr, icmr, icip; + + oscr = OSCR; + osmr0 = OSMR0; + osmr1 = OSMR1; + ossr = OSSR; + icmr = ICMR; + icip = ICIP; + + OSSR = OSSR_M1; + ICMR &= ~IC_OST1; + + thread = (struct thread_info *)(regs->ARM_sp & ~8191); + tsk = thread->task; + + bust_spinlocks(1); + printk("OSMR0:%08lx OSMR1:%08lx OSCR:%08lx OSSR:%08lx ICMR:%08lx ICIP:%08lx\n", + osmr0, osmr1, oscr, ossr, icmr, icip); + printk("Internal error: NMI watchdog\n"); + print_modules(); + printk("CPU: %d\n", smp_processor_id()); + show_regs(regs); + printk("Process %s (pid: %d stack limit = 0x%p)\n", + tsk->comm, tsk->pid, thread + 1); + __dump_stack(tsk, regs->ARM_sp); + dump_backtrace(regs, tsk); + dump_instr(regs); + bust_spinlocks(0); + + OSSR = OSSR_M1; + OSMR1 = OSSR + 36864000; + ICMR |= IC_OST1; +} + +static int nmi_init(void) +{ + unsigned char *vec_base = (unsigned char *)vectors_base(); +return 0; + nmi_stack = (void *)__get_free_page(GFP_KERNEL); + if (!nmi_stack) + return -ENOMEM; + + nmi_stack += PAGE_SIZE; + + modify_domain(DOMAIN_USER, DOMAIN_MANAGER); + memcpy(vec_base + 0x1c, &nmi_start, &nmi_end - &nmi_start); + modify_domain(DOMAIN_USER, DOMAIN_CLIENT); + + /* + * Ensure timer 1 is set to FIQ, and enabled. + */ + OSMR1 = OSCR - 1; + OSSR = OSSR_M1; + OIER |= OIER_E1; + ICLR |= IC_OST1; + ICMR |= IC_OST1; + + return 0; +} + +__initcall(nmi_init); diff -urN orig/arch/arm/mach-sa1100/pfs168.c linux/arch/arm/mach-sa1100/pfs168.c --- orig/arch/arm/mach-sa1100/pfs168.c Thu Oct 3 12:45:52 2002 +++ linux/arch/arm/mach-sa1100/pfs168.c Wed Jun 4 18:34:06 2003 @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -18,6 +19,35 @@ #include "generic.h" +static struct resource sa1111_resources[] = { + [0] = { + .start = 0x40000000, + .end = 0x40001fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_GPIO25, + .end = IRQ_GPIO25, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 sa1111_dmamask = 0xffffffffUL; + +static struct platform_device sa1111_device = { + .name = "sa1111", + .id = 0, + .dev = { + .name = "Intel Corporation SA1111", + .dma_mask = &sa1111_dmamask, + }, + .num_resources = ARRAY_SIZE(sa1111_resources), + .resource = sa1111_resources, +}; + +static struct platform_device *devices[] __initdata = { + &sa1111_device, +}; static int __init pfs168_init(void) { @@ -32,10 +62,7 @@ */ sa1110_mb_disable(); - /* - * Probe for SA1111. - */ - return sa1111_init(0x40000000, IRQ_GPIO25); + return platform_add_devices(devices, ARRAY_SIZE(devices)); } arch_initcall(pfs168_init); diff -urN orig/arch/arm/mach-sa1100/simpad.c linux/arch/arm/mach-sa1100/simpad.c --- orig/arch/arm/mach-sa1100/simpad.c Thu Oct 3 12:45:52 2002 +++ linux/arch/arm/mach-sa1100/simpad.c Sun Aug 11 19:37:02 2002 @@ -1,6 +1,10 @@ /* * linux/arch/arm/mach-sa1100/simpad.c */ +#error Add the following command line: +#error "mem=32M root=/dev/ram ramdisk=8192 initrd=0xc0800000,4M" +#error or +#error "mem=64M root=/dev/ram ramdisk=8192 initrd=0xc0800000,4M" #include #include diff -urN orig/arch/arm/mach-sa1100/system3.c linux/arch/arm/mach-sa1100/system3.c --- orig/arch/arm/mach-sa1100/system3.c Mon Mar 17 23:16:09 2003 +++ linux/arch/arm/mach-sa1100/system3.c Wed Jun 4 18:34:06 2003 @@ -373,6 +373,36 @@ } } +static struct resource sa1111_resources[] = { + [0] = { + .start = PT_SA1111_BASE, + .end = PT_SA1111_BASE + 0x00001fff, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_SYSTEM3_SA1111, + .end = IRQ_SYSTEM3_SA1111, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 sa1111_dmamask = 0xffffffffUL; + +static struct platform_device sa1111_device = { + .name = "sa1111", + .id = 0, + .dev = { + .name = "Intel Corporation SA1111", + .dma_mask = &sa1111_dmamask, + }, + .num_resources = ARRAY_SIZE(sa1111_resources), + .resource = sa1111_resources, +}; + +static struct platform_device *devices[] __initdata = { + &sa1111_device, +}; + static int __init system3_init(void) { int ret = 0; @@ -405,7 +435,7 @@ /* * Probe for a SA1111. */ - ret = sa1111_init(PT_SA1111_BASE, IRQ_SYSTEM3_SA1111); + ret = platform_add_devices(devices, ARRAY_SIZE(devices)); if (ret < 0) { printk( KERN_WARNING"PT Digital Board: no SA1111 found!\n" ); goto DONE; diff -urN orig/arch/arm/mach-sa1100/usb/Makefile linux/arch/arm/mach-sa1100/usb/Makefile --- orig/arch/arm/mach-sa1100/usb/Makefile Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/mach-sa1100/usb/Makefile Tue Feb 18 00:34:43 2003 @@ -0,0 +1,12 @@ +# +# Makefile for the USB client +# + +usbdevcore-objs := buffer.o control.o strings.o usb_ctl.o + +sa1100-objs := sa1100usb.o usb_recv.o usb_send.o + +obj-$(CONFIG_SA1100_USB) += usbdevcore.o sa1100.o +obj-$(CONFIG_SA1100_USB_NETLINK) += usb-eth.o +obj-$(CONFIG_SA1100_USB_CHAR) += usb-char.o + diff -urN orig/arch/arm/mach-sa1100/usb/buffer.c linux/arch/arm/mach-sa1100/usb/buffer.c --- orig/arch/arm/mach-sa1100/usb/buffer.c Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/mach-sa1100/usb/buffer.c Tue Oct 8 16:34:51 2002 @@ -0,0 +1,63 @@ +/* + * usb/buffer.c + * + * Copyright (C) 2002 Russell King. + */ +#include +#include +#include + +#include "buffer.h" + +static LIST_HEAD(buffers); + +struct usb_buf *usbb_alloc(int size, int gfp) +{ + unsigned long flags; + struct usb_buf *buf; + + buf = kmalloc(sizeof(struct usb_buf) + size, gfp); + if (buf) { + atomic_set(&buf->users, 1); + local_irq_save(flags); + list_add(&buf->list, &buffers); + local_irq_restore(flags); + buf->len = 0; + buf->data = (unsigned char *) (buf + 1); + buf->head = (unsigned char *) (buf + 1); + } + + return buf; +} + +void __usbb_free(struct usb_buf *buf) +{ + unsigned long flags; + local_irq_save(flags); + list_del(&buf->list); + local_irq_restore(flags); + kfree(buf); +} + +EXPORT_SYMBOL(usbb_alloc); +EXPORT_SYMBOL(__usbb_free); + +static void __exit usbb_exit(void) +{ + if (!list_empty(&buffers)) { + struct list_head *l, *n; + printk("usbb: buffers not freed:\n"); + + list_for_each_safe(l, n, &buffers) { + struct usb_buf *b = list_entry(l, struct usb_buf, list); + + printk(" %p: alloced from %p count %d\n", + b, b->alloced_by, atomic_read(&b->users)); + + __usbb_free(b); + } + } +} + +module_exit(usbb_exit); + diff -urN orig/arch/arm/mach-sa1100/usb/buffer.h linux/arch/arm/mach-sa1100/usb/buffer.h --- orig/arch/arm/mach-sa1100/usb/buffer.h Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/mach-sa1100/usb/buffer.h Wed Aug 21 17:44:16 2002 @@ -0,0 +1,45 @@ +/* + * usb/buffer.h: USB client buffers + * + * Copyright (C) 2002 Russell King. + * + * Loosely based on linux/skbuff.h + */ +#ifndef USBDEV_BUFFER_H +#define USBDEV_BUFFER_H + +#include + +struct usb_buf { + atomic_t users; + struct list_head list; + void *alloced_by; + unsigned char *data; + unsigned char *head; + unsigned int len; +}; + +extern struct usb_buf *usbb_alloc(int size, int gfp); +extern void __usbb_free(struct usb_buf *); + +static inline struct usb_buf *usbb_get(struct usb_buf *buf) +{ + atomic_inc(&buf->users); + return buf; +} + +static inline void usbb_put(struct usb_buf *buf) +{ + if (atomic_dec_and_test(&buf->users)) + __usbb_free(buf); +} + +static inline void *usbb_push(struct usb_buf *buf, int len) +{ + unsigned char *b = buf->head; + buf->head += len; + buf->len += len; + return b; +} + +#endif diff -urN orig/arch/arm/mach-sa1100/usb/client.h linux/arch/arm/mach-sa1100/usb/client.h --- orig/arch/arm/mach-sa1100/usb/client.h Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/mach-sa1100/usb/client.h Wed Aug 21 19:32:18 2002 @@ -0,0 +1,40 @@ +#ifndef USBDEV_CLIENT_H +#define USBDEV_CLIENT_H + +#include "sa1100_usb.h" /* grr */ + +struct usbctl; + +struct usb_client { + struct usbctl *ctl; + const char *name; /* Client name */ + void *priv; /* Client-private data */ + void (*state_change)(void *priv, int state, int oldstate); + __u16 vendor; /* USB vendor ID */ + __u16 product; /* USB product ID */ + __u16 version; /* USB version ID */ + __u8 class; /* USB class */ + __u8 subclass; /* USB subclass */ + __u8 protocol; /* USB protocol */ + __u8 unused1; + __u16 unused2; + const char *manufacturer_str; + const char *product_str; + const char *serial_str; +}; + +int usbctl_start(struct usb_client *client); +void usbctl_stop(struct usb_client *client); +int usbctl_open(struct usb_client *client); +void usbctl_close(struct usb_client *client); + +int +usbctl_ep_queue_buffer(struct usbctl *ctl, unsigned int ep, + char *buf, unsigned int len); +void usbctl_ep_reset(struct usbctl *ctl, unsigned int ep); +void +usbctl_ep_set_callback(struct usbctl *ctl, unsigned int ep, + usb_callback_t callback, void *data); +int usbctl_ep_idle(struct usbctl *ctl, unsigned int ep); + +#endif diff -urN orig/arch/arm/mach-sa1100/usb/control.c linux/arch/arm/mach-sa1100/usb/control.c --- orig/arch/arm/mach-sa1100/usb/control.c Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/mach-sa1100/usb/control.c Mon May 19 23:44:32 2003 @@ -0,0 +1,933 @@ +/* + * usb/control.c + * + * This parses and handles all the control messages to/from endpoint 0. + */ +#include +#include +#include +#include +#include +#include + +#include "buffer.h" +#include "client.h" +#include "usbdev.h" + +#include "sa1100_usb.h" + +#define USB_ENDPOINT_HALT 0 +#define USB_DEVICE_REMOTE_WAKEUP 1 + +#undef DEBUG + +#ifdef DEBUG +#define DPRINTK(fmt, args...) printk(KERN_DEBUG fmt , ## args) +#else +#define DPRINTK(fmt, args...) +#endif + +/* + * print string descriptor + */ +static char * __attribute__((unused)) +psdesc(char *str, int len, struct usb_string_descriptor *desc) +{ + char *start = str; + int nchars = (desc->bLength - 2) / sizeof(__u16) + 2; + int i; + + if (nchars >= len) + nchars = len - 1; + + nchars -= 2; + + *str++ = '"'; + for(i = 0; i < nchars; i++) + *str++ = le16_to_cpu(desc->wData[i]); + *str++ = '"'; + *str = '\0'; + + return start; +} + +enum { + kError = -1, + kEvSuspend = 0, + kEvReset = 1, + kEvResume = 2, + kEvAddress = 3, + kEvConfig = 4, + kEvDeConfig = 5 +}; + +enum { + kStateZombie = 0, + kStateZombieSuspend = 1, + kStateDefault = 2, + kStateDefaultSuspend = 3, + kStateAddr = 4, + kStateAddrSuspend = 5, + kStateConfig = 6, + kStateConfigSuspend = 7 +}; + +#define kE kError +#define kSZ kStateZombie +#define kSZS kStateZombieSuspend +#define kSD kStateDefault +#define kSDS kStateDefaultSuspend +#define kSA kStateAddr +#define kSAS kStateAddrSuspend +#define kSC kStateConfig +#define kSCS kStateConfigSuspend + +/* + * Fig 9-1 P192 + * Zombie == Attached | Powered + */ +static int device_state_machine[8][6] = { +// suspend reset resume addr config deconfig +{ kSZS, kSD, kE, kE, kE, kE }, /* zombie */ +{ kE, kSD, kSZ, kE, kE, kE }, /* zom sus */ +{ kSDS, kError, kSD, kSA, kE, kE }, /* default */ +{ kE, kSD, kSD, kE, kE, kE }, /* def sus */ +{ kSAS, kSD, kE, kE, kSC, kE }, /* addr */ +{ kE, kSD, kSA, kE, kE, kE }, /* addr sus */ +{ kSCS, kSD, kE, kE, kE, kSA }, /* config */ +{ kE, kSD, kSC, kE, kE, kE } /* cfg sus */ +}; + +/* + * "device state" is the usb device framework state, as opposed to the + * "state machine state" which is whatever the driver needs and is much + * more fine grained + */ +static int sm_state_to_device_state[8] = { + USB_STATE_POWERED, /* zombie */ + USB_STATE_SUSPENDED, /* zombie suspended */ + USB_STATE_DEFAULT, /* default */ + USB_STATE_SUSPENDED, /* default suspended */ + USB_STATE_ADDRESS, /* address */ + USB_STATE_SUSPENDED, /* address suspended */ + USB_STATE_CONFIGURED, /* config */ + USB_STATE_SUSPENDED /* config suspended */ +}; + +static char * state_names[8] = { + "zombie", + "zombie suspended", + "default", + "default suspended", + "address", + "address suspended", + "configured", + "config suspended" +}; + +static char * event_names[6] = { + "suspend", + "reset", + "resume", + "address assigned", + "configure", + "de-configure" +}; + +static char * device_state_names[] = { + "not attached", + "attached", + "powered", + "default", + "address", + "configured", + "suspended" +}; + +static void usbctl_callbacks(struct usbctl *ctl, int state, int oldstate) +{ + struct usb_client *clnt = ctl->clnt; + + /* + * Inform any clients currently attached + * that the connectivity state changed. + */ + if (clnt && clnt->state_change) + clnt->state_change(clnt->priv, state, oldstate); +} + +/* + * called by the interrupt handler here and the two endpoint + * files when interesting .."events" happen + */ +static int usbctl_next_state_on_event(struct usbctl *ctl, int event) +{ + int next_state, next_dev_state, old_dev_state; + + printk(KERN_DEBUG "usbctl: %s --[%s]--> ", state_names[ctl->sm_state], + event_names[event]); + + next_state = device_state_machine[ctl->sm_state][event]; + if (next_state != kError) { + next_dev_state = sm_state_to_device_state[next_state]; + + printk("%s. Device in %s state.\n", + state_names[next_state], + device_state_names[next_dev_state]); + + old_dev_state = ctl->state; + ctl->sm_state = next_state; + ctl->state = next_dev_state; + + if (old_dev_state != next_dev_state) + usbctl_callbacks(ctl, next_dev_state, old_dev_state); + } else + printk("(error)\n"); + + return next_state; +} + +/* + * Driver detected USB HUB reset. + */ +int usbctl_reset(struct usbctl *ctl) +{ + int ret; + + ret = usbctl_next_state_on_event(ctl, kEvReset) == kError; + + if (!ret) { + ctl->address = 0; + } + return ret; +} + +EXPORT_SYMBOL(usbctl_reset); + +void usbctl_suspend(struct usbctl *ctl) +{ + usbctl_next_state_on_event(ctl, kEvSuspend); +} + +EXPORT_SYMBOL(usbctl_suspend); + +void usbctl_resume(struct usbctl *ctl) +{ + usbctl_next_state_on_event(ctl, kEvResume); +} + +EXPORT_SYMBOL(usbctl_resume); + +static struct usb_interface_descriptor * +usbctl_get_interface_descriptor(struct usbctl *ctl, unsigned int interface) +{ + /*FIXME*/ + struct cdb *cdb = sa1100_usb_get_descriptor_ptr(); + + return (struct usb_interface_descriptor *)&cdb->intf; +} + +static inline int +__usbctl_queue(struct usbctl *ctl, struct usb_ctrlrequest *req, + struct usb_buf *buf) +{ + unsigned int reqlen = le16_to_cpu(req->wLength); + + return ctl->driver->ep0_queue(ctl->driver->priv, buf, reqlen) ? + RET_ERROR : RET_QUEUED; +} + +static int +usbctl_queue(struct usbctl *ctl, struct usb_ctrlrequest *req, + void *data, unsigned int len) +{ + struct usb_buf *buf; + + buf = usbb_alloc(len, GFP_ATOMIC); + if (!buf) { + printk(KERN_ERR "usb: out of memory\n"); + return RET_ERROR; + } + + if (data) + memcpy(usbb_push(buf, len), data, len); + + return __usbctl_queue(ctl, req, buf); +} + +/* + * 9.4.5: Get Status (device) + */ +static int +usbctl_parse_dev_get_status(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + u16 status; + + status = /* self_powered_hook() ? 1 : 0 */1; + + status = cpu_to_le16(status); + + return usbctl_queue(ctl, req, &status, 2); +} + +/* + * Send USB device description to the host. + */ +static int +usbctl_desc_device(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + return __usbctl_queue(ctl, req, usbb_get(ctl->dev_desc_buf)); +} + +/* + * Send USB configuration information to the host. + */ +static int +usbctl_desc_config(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + /*FIXME*/ + struct cdb *cdb = sa1100_usb_get_descriptor_ptr(); + + return usbctl_queue(ctl, req, cdb, sizeof(struct cdb)); +} + +/* + * Send a string to the host from the string table. + */ +static int +usbctl_desc_string(struct usbctl *ctl, struct usb_ctrlrequest *req, + unsigned int idx) +{ + struct usb_buf *buf; + unsigned int lang = le16_to_cpu(req->wIndex); + char string[32] __attribute__((unused)); + int ret; + + DPRINTK("usbctl: desc_string (index %u, lang 0x%04x): ", idx, lang); + + buf = usbc_string_find(&ctl->strings, lang, idx); + if (buf) { + DPRINTK("%s\n", idx == 0 ? "language" : + psdesc(string, sizeof(string), usbc_string_desc(buf))); + + ret = __usbctl_queue(ctl, req, buf); + } else { + DPRINTK("not found -> stall\n"); + ret = RET_REQERROR; + } + return ret; +} + +/* + * Send an interface description (and endpoints) to the host. + */ +static int +usbctl_desc_interface(struct usbctl *ctl, struct usb_ctrlrequest *req, + unsigned int idx) +{ + struct usb_interface_descriptor *desc; + int ret; + + DPRINTK("usbctl: desc_interface (index %d)\n", idx); + + desc = usbctl_get_interface_descriptor(ctl, idx); + + if (desc) { + ret = usbctl_queue(ctl, req, desc, desc->bLength); + } else { + printk("usbctl: unknown interface %d\n", idx); + ret = RET_REQERROR; + } + + return ret; +} + +/* + * Send an endpoint (1 .. n) to the host. + */ +static int +usbctl_desc_endpoint(struct usbctl *ctl, struct usb_ctrlrequest *req, + unsigned int idx) +{ + int ret; + + DPRINTK("usbctl: desc_endpoint (index %d)\n", idx); + + if (idx >= 1 && idx <= ctl->nr_ep) { + struct usb_endpoint_descriptor *ep = ctl->ep_desc[idx - 1]; + + ret = usbctl_queue(ctl, req, ep, ep->bLength); + } else { + printk("usbctl: unknown endpoint %d\n", idx); + ret = RET_REQERROR; + } + + return ret; +} + +/* + * 9.4.3: Parse a request for a descriptor. + * Unspecified conditions: + * None + * Valid states: default, address, configured. + */ +static int +usbctl_parse_dev_descriptor(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + unsigned int idx = le16_to_cpu(req->wValue) & 255; + unsigned int type = le16_to_cpu(req->wValue) >> 8; + int ret; + + switch (type) { + case USB_DT_DEVICE: /* check if idx matters */ + ret = usbctl_desc_device(ctl, req); + break; + + case USB_DT_CONFIG: /* check if idx matters */ + ret = usbctl_desc_config(ctl, req); + break; + + case USB_DT_STRING: + ret = usbctl_desc_string(ctl, req, idx); + break; + + case USB_DT_INTERFACE: + ret = usbctl_desc_interface(ctl, req, idx); + break; + + case USB_DT_ENDPOINT: + ret = usbctl_desc_endpoint(ctl, req, idx); + break; + + case USB_DT_DEVICE_QUALIFIER: + case USB_DT_OTHER_SPEED_CONFIG: + case USB_DT_INTERFACE_POWER: + default: + printk(KERN_ERR "usbctl: unknown descriptor: " + "wValue = 0x%04x wIndex = 0x%04x\n", + le16_to_cpu(req->wValue), le16_to_cpu(req->wIndex)); + ret = RET_REQERROR; + break; + } + + return ret; +} + +/* + * 9.4.6: Set Address + * The USB1.1 spec says the response to SetAddress() with value 0 + * is undefined. It then goes on to define the response. We + * acknowledge addresses of zero, but take no further action. + */ +static int +usbctl_parse_dev_set_address(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + unsigned int address = le16_to_cpu(req->wValue) & 0x7f; + + if (ctl->state == USB_STATE_CONFIGURED) + return RET_REQERROR; + + if (address != 0) { + ctl->address = address; + + usbctl_next_state_on_event(ctl, kEvAddress); + + ctl->driver->set_address(ctl->driver->priv, address); + } + + return RET_ACK; +} + +/* + * 9.4.2: Get Configuration. + * Unspecified conditions: + * - non-zero wIndex, wValue or wLength (ignored) + * - default state (request error) + * Valid states: address, configured. + */ +static int +usbctl_parse_dev_get_config(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + u8 status = 0; + + if (ctl->state == USB_STATE_CONFIGURED) + status = 1; + + return usbctl_queue(ctl, req, &status, 1); +} + +/* + * 9.4.7: Set Configuration. + * Unspecified conditions: + * - default state (request error) + */ +static int +usbctl_parse_dev_set_config(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + unsigned int cfg = le16_to_cpu(req->wValue); + int ret = RET_REQERROR; + + if (ctl->state == USB_STATE_DEFAULT) + return ret; + + if (cfg == 0) { + /* enter address state, or remain in address state */ + usbctl_next_state_on_event(ctl, kEvDeConfig); + + ctl->driver->set_config(ctl->driver->priv, NULL); + + ret = RET_ACK; + } else if (cfg == 1) { + /* enter configured state, and set configuration */ + /*FIXME*/ + struct cdb *cdb = sa1100_usb_get_descriptor_ptr(); + + usbctl_next_state_on_event(ctl, kEvConfig); + + ctl->driver->set_config(ctl->driver->priv, cdb); + ret = RET_ACK; + } + + return ret; +} + +/* + * Interface handling + */ + +/* + * 9.4.5: Get Status (interface) + */ +static int +usbctl_parse_int_get_status(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + unsigned int interface = le16_to_cpu(req->wIndex) & 255; + u16 status; + + switch (ctl->state) { + case USB_STATE_DEFAULT: + return RET_REQERROR; + + case USB_STATE_ADDRESS: + if (interface != 0) + return RET_REQERROR; + break; + + case USB_STATE_CONFIGURED: + if (interface != 1) + return RET_REQERROR; + break; + } + + status = cpu_to_le16(0); + + return usbctl_queue(ctl, req, &status, 2); +} + +/* + * 9.4.4: Get Interface + * Unspecified conditions: + * - + * States: Default (unspecified), Address (Request Error), Configured (ok) + */ +static int +usbctl_parse_int_get_interface(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + unsigned int interface = le16_to_cpu(req->wIndex) & 255; + u8 null = 0; + + if (ctl->state != USB_STATE_CONFIGURED) + return RET_REQERROR; + + /* + * If the interface doesn't exist, respond with request error + */ + if (interface != 1) + return RET_REQERROR; + + printk("usbctl: get interface %d not supported\n", interface); + + return usbctl_queue(ctl, req, &null, 1); +} + +static int +usbctl_parse_int_set_interface(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + unsigned int interface = le16_to_cpu(req->wIndex) & 255; + + if (interface != 0) + printk("usbctl: set interface %d not supported (ignored)\n", + interface); + + return RET_ACK; +} + +/* + * Endpoint handling + */ + +/* + * 9.4.5: Get Status (endpoint) + */ +static int +usbctl_parse_ep_get_status(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + unsigned int ep = le16_to_cpu(req->wIndex) & 15; + u16 status; + + if ((ep != 0 && ctl->state != USB_STATE_CONFIGURED) || + ep <= ctl->nr_ep) + return RET_REQERROR; + + status = ctl->driver->ep_get_status(ctl->driver->priv, ep); + status = cpu_to_le16(status); + + return usbctl_queue(ctl, req, &status, 2); +} + +/* + * 9.4.1: Clear an endpoint feature. We only support ENDPOINT_HALT. + * Unspecified conditions: + * - non-zero wLength is not specified (ignored) + * Valid states: Address, Configured. + */ +static int +usbctl_parse_ep_clear_feature(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + unsigned int feature = le16_to_cpu(req->wValue); + unsigned int ep = le16_to_cpu(req->wIndex) & 15; + int ret; + + if ((ep != 0 && ctl->state != USB_STATE_CONFIGURED) || + ep <= ctl->nr_ep) + return RET_REQERROR; + + if (feature == USB_ENDPOINT_HALT) { + ctl->driver->ep_halt(ctl->driver->priv, ep, 0); + ret = RET_ACK; + } else { + printk(KERN_ERR "usbctl: unsupported clear feature: " + "wValue = 0x%04x wIndex = 0x%04x\n", + feature, ep); + + ret = RET_REQERROR; + } + return ret; +} + +/* + * 9.4.9: Set Feature (endpoint) + */ +static int +usbctl_parse_ep_set_feature(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + unsigned int feature = le16_to_cpu(req->wValue); + unsigned int ep = le16_to_cpu(req->wIndex) & 15; + int ret; + + if ((ep != 0 && ctl->state != USB_STATE_CONFIGURED) || + ep <= ctl->nr_ep) + return RET_REQERROR; + + if (feature == USB_ENDPOINT_HALT) { + ctl->driver->ep_halt(ctl->driver->priv, ep, 1); + ret = RET_ACK; + } else { + printk(KERN_ERR "usbctl: unsupported set feature " + "wValue = 0x%04x wIndex = 0x%04x\n", + feature, ep); + + ret = RET_REQERROR; + } + return ret; +} + +/* + * This reflects Table 9.3 (p186) in the USB1.1 spec. + * + * Some notes: + * - USB1.1 specifies remote wakeup feature, so we don't implement + * USB_RECIP_DEVICE USB_REQ_{SET,CLEAR}_FEATURE + * - USB1.1 doesn't actually specify any interface features, so we + * don't implement USB_RECIP_INTERFACE USB_REQ_{SET,CLEAR}_FEATURE + */ +static int (*request_fns[4][16])(struct usbctl *, struct usb_ctrlrequest *) = { + [USB_RECIP_DEVICE] = { + [USB_REQ_GET_STATUS] = usbctl_parse_dev_get_status, + [USB_REQ_CLEAR_FEATURE] = NULL, + [USB_REQ_SET_FEATURE] = NULL, + [USB_REQ_SET_ADDRESS] = usbctl_parse_dev_set_address, + [USB_REQ_GET_DESCRIPTOR] = usbctl_parse_dev_descriptor, + [USB_REQ_SET_DESCRIPTOR] = NULL, + [USB_REQ_GET_CONFIGURATION] = usbctl_parse_dev_get_config, + [USB_REQ_SET_CONFIGURATION] = usbctl_parse_dev_set_config, + }, + + [USB_RECIP_INTERFACE] = { + [USB_REQ_GET_STATUS] = usbctl_parse_int_get_status, + [USB_REQ_CLEAR_FEATURE] = NULL, + [USB_REQ_SET_FEATURE] = NULL, + [USB_REQ_GET_INTERFACE] = usbctl_parse_int_get_interface, + [USB_REQ_SET_INTERFACE] = usbctl_parse_int_set_interface, + }, + + [USB_RECIP_ENDPOINT] = { + [USB_REQ_GET_STATUS] = usbctl_parse_ep_get_status, + [USB_REQ_CLEAR_FEATURE] = usbctl_parse_ep_clear_feature, + [USB_REQ_SET_FEATURE] = usbctl_parse_ep_set_feature, + }, +}; + +static void __attribute__((unused)) +usbctl_dump_request(const char *prefix, const struct usb_ctrlrequest *req) +{ + printk("%sbRequestType=0x%02x bRequest=0x%02x " + "wValue=0x%04x wIndex=0x%04x wLength=0x%04x\n", + prefix, req->bRequestType, req->bRequest, + le16_to_cpu(req->wValue), le16_to_cpu(req->wIndex), + le16_to_cpu(req->wLength)); +} + +int usbctl_parse_request(struct usbctl *ctl, struct usb_ctrlrequest *req) +{ + unsigned int type; + int (*fn)(struct usbctl *, struct usb_ctrlrequest *) = NULL; + int ret = RET_REQERROR; + + //usbctl_dump_request("usbctl: ", req); + + type = req->bRequestType & USB_TYPE_MASK; + if (type == USB_TYPE_STANDARD) { + unsigned int recip; + + recip = req->bRequestType & USB_RECIP_MASK; + if (recip < ARRAY_SIZE(request_fns) && + req->bRequest < ARRAY_SIZE(request_fns[0])) + fn = request_fns[recip][req->bRequest]; + } + + if (fn) + ret = fn(ctl, req); + else + usbctl_dump_request(KERN_ERR "usbctl: unknown request: ", + req); + + /* + * Make sure we're doing the right thing. + */ + if (req->bRequestType & USB_DIR_IN) { + if (ret != RET_QUEUED && ret != RET_REQERROR) + printk("Error: device to host transfer expected\n"); + } else { + if (ret == RET_QUEUED) + printk("Error: no device to host transfer expected\n"); + } + + return ret; +} + +EXPORT_SYMBOL(usbctl_parse_request); + +/* Start running. Must have called usb_open (above) first */ +int usbctl_start(struct usb_client *client) +{ + struct usbctl *ctl = client->ctl; + + if (ctl == NULL || ctl->clnt != client) { + printk("usbctl: start: no client registered\n"); + return -EPERM; + } + + ctl->sm_state = kStateZombie; + ctl->state = USB_STATE_POWERED; + + /* + * Notify the client as to our state. + */ + usbctl_callbacks(ctl, USB_STATE_POWERED, USB_STATE_SUSPENDED); + + return ctl->driver->start(ctl->driver->priv); +} + +EXPORT_SYMBOL(usbctl_start); + +/* + * Stop USB core from running + */ +void usbctl_stop(struct usb_client *client) +{ + struct usbctl *ctl = client->ctl; + + if (ctl == NULL || ctl->clnt != client) { + printk("USBDEV: stop: no client/driver registered\n"); + return; + } + + ctl->driver->stop(ctl->driver->priv); +} + +EXPORT_SYMBOL(usbctl_stop); + +struct usbctl usbctl; + +EXPORT_SYMBOL(usbctl); + +/* Open SA usb core on behalf of a client, but don't start running */ + +int usbctl_open(struct usb_client *client) +{ + struct usbctl *ctl = &usbctl; + int ret; +printk("usbctl_open: ctl %p driver %p\n", ctl, ctl->driver); + if (!ctl->driver || !try_module_get(ctl->driver->owner)) + return -ENODEV; + + if (ctl->clnt != NULL) { + ret = -EBUSY; + goto err; + } + + ctl->clnt = client; + ctl->state = USB_STATE_SUSPENDED; + ctl->nr_ep = 2; + /* start in zombie suspended state */ + ctl->sm_state = kStateZombieSuspend; + ctl->state = USB_STATE_SUSPENDED; + client->ctl = ctl; + + ctl->dev_desc_buf = usbb_alloc(sizeof(struct usb_device_descriptor), + GFP_KERNEL); + if (!ctl->dev_desc_buf) { + ret = -ENOMEM; + goto err; + } + + ctl->dev_desc = usbb_push(ctl->dev_desc_buf, + sizeof(struct usb_device_descriptor)); + + /* create descriptors for enumeration */ + initialize_descriptors(ctl); + + return 0; + + err: + module_put(ctl->driver->owner); + return ret; +} + +EXPORT_SYMBOL(usbctl_open); + +/* Tell SA core client is through using it */ +void usbctl_close(struct usb_client *client) +{ + struct usbctl *ctl = client->ctl; + + if (ctl == NULL || ctl->clnt != client) { + printk("usbctl: close: no client registered\n"); + return; + } + + usbb_put(ctl->dev_desc_buf); + + client->ctl = NULL; + ctl->clnt = NULL; + ctl->dev_desc = NULL; + ctl->dev_desc_buf = NULL; + /* reset to zombie suspended state */ + ctl->sm_state = kStateZombieSuspend; + ctl->state = USB_STATE_SUSPENDED; + + usbc_string_free_all(&ctl->strings); + + if (ctl->driver->owner) + module_put(ctl->driver->owner); +} + +EXPORT_SYMBOL(usbctl_close); + +int usbctl_proc_info(struct usbctl *ctl, char *buf) +{ + char *p = buf; + + p += sprintf(p, "USB Gadget Core:\n"); + p += sprintf(p, "Driver\t: %s\n", + ctl->driver ? ctl->driver->name : "none"); + p += sprintf(p, "Client\t: %s\n", + ctl->clnt ? ctl->clnt->name : "none"); + p += sprintf(p, "State\t: %s (%s) %d\n", + device_state_names[sm_state_to_device_state[ctl->sm_state]], + state_names[ctl->sm_state], + ctl->sm_state); + p += sprintf(p, "Address\t: %d\n", ctl->address); + + return p - buf; +} + +EXPORT_SYMBOL(usbctl_proc_info); + +int +usbctl_ep_queue_buffer(struct usbctl *ctl, unsigned int ep, + char *buf, unsigned int len) +{ + return ctl->driver->ep_queue(ctl->driver->priv, ep, buf, len); +} + +EXPORT_SYMBOL(usbctl_ep_queue_buffer); + +void usbctl_ep_reset(struct usbctl *ctl, unsigned int ep) +{ + return ctl->driver->ep_reset(ctl->driver->priv, ep); +} + +EXPORT_SYMBOL(usbctl_ep_reset); + +void +usbctl_ep_set_callback(struct usbctl *ctl, unsigned int ep, + usb_callback_t callback, void *data) +{ + ctl->driver->ep_callback(ctl->driver->priv, ep, callback, data); +} + +EXPORT_SYMBOL(usbctl_ep_set_callback); + +int usbctl_ep_idle(struct usbctl *ctl, unsigned int ep) +{ + return ctl->driver->ep_idle(ctl->driver->priv, ep); +} + +EXPORT_SYMBOL(usbctl_ep_idle); + +/* + * usbctl_init() + * Module load time. Allocate dma and interrupt resources. Setup /proc fs + * entry. Leave UDC disabled. + */ +int usbctl_init(struct usbctl *ctl, struct usbc_driver *drv) +{ + usbc_string_init(&ctl->strings); +printk("usbctl_init: %p %p\n", ctl, drv); + /* + * start in zombie suspended state + */ + ctl->sm_state = kStateZombieSuspend; + ctl->state = USB_STATE_SUSPENDED; + ctl->driver = drv; + + return 0; +} + +/* + * usbctl_exit() + */ +void usbctl_exit(struct usbctl *ctl) +{ + usbc_string_free_all(&ctl->strings); + + ctl->driver = NULL; +} + +EXPORT_SYMBOL(usbctl_init); +EXPORT_SYMBOL(usbctl_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("USB gadget core"); diff -urN orig/arch/arm/mach-sa1100/usb/sa1100_usb.h linux/arch/arm/mach-sa1100/usb/sa1100_usb.h --- orig/arch/arm/mach-sa1100/usb/sa1100_usb.h Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/mach-sa1100/usb/sa1100_usb.h Tue Mar 18 14:01:25 2003 @@ -0,0 +1,110 @@ +/* + * sa1100_usb.h + * + * Public interface to the sa1100 USB core. For use by client modules + * like usb-eth and usb-char. + * + */ +#ifndef _SA1100_USB_H +#define _SA1100_USB_H + +typedef void (*usb_callback_t)(void *data, int flag, int size); + +/* in usb_send.c */ +int sa1100_usb_xmitter_avail( void ); +int sa1100_usb_send(char *buf, int len); +void sa1100_usb_send_set_callback(usb_callback_t callback, void *data); +void sa1100_usb_send_reset(void); + +/* in usb_recev.c */ +int sa1100_usb_recv(char *buf, int len); +void sa1100_usb_recv_set_callback(usb_callback_t callback, void *data); +void sa1100_usb_recv_reset(void); + +////////////////////////////////////////////////////////////////////////////// +// Descriptor Management +////////////////////////////////////////////////////////////////////////////// + +#define DescriptorHeader \ + __u8 bLength; \ + __u8 bDescriptorType + + +// --- Configuration Descriptor ------------ + +typedef struct { + DescriptorHeader; + __u16 wTotalLength; /* total # of bytes returned in the cfg buf 4 this cfg */ + __u8 bNumInterfaces; /* number of interfaces in this cfg */ + __u8 bConfigurationValue; /* used to uniquely ID this cfg */ + __u8 iConfiguration; /* index of string describing configuration */ + __u8 bmAttributes; /* bitmap of attributes for ths cfg */ + __u8 MaxPower; /* power draw in 2ma units */ +} __attribute__ ((packed)) config_desc_t; + +// bmAttributes: +enum { + USB_CONFIG_REMOTEWAKE = 0x20, + USB_CONFIG_SELFPOWERED = 0x40, + USB_CONFIG_BUSPOWERED = 0x80 +}; + +// MaxPower: +#define USB_POWER(x) ((x)>>1) /* convert mA to descriptor units of A for MaxPower */ + +// --- Interface Descriptor --------------- + +typedef struct { + DescriptorHeader; + __u8 bInterfaceNumber; /* Index uniquely identfying this interface */ + __u8 bAlternateSetting; /* ids an alternate setting for this interface */ + __u8 bNumEndpoints; /* number of endpoints in this interface */ + __u8 bInterfaceClass; /* USB class info applying to this interface */ + __u8 bInterfaceSubClass; /* USB subclass info applying to this interface */ + __u8 bInterfaceProtocol; /* USB protocol info applying to this interface */ + __u8 iInterface; /* index of string describing interface */ +} __attribute__ ((packed)) intf_desc_t; + +// --- Endpoint Descriptor --------------- + +typedef struct { + DescriptorHeader; + __u8 bEndpointAddress; /* 0..3 ep num, bit 7: 0 = 0ut 1= in */ + __u8 bmAttributes; /* 0..1 = 0: ctrl, 1: isoc, 2: bulk 3: intr */ + __u16 wMaxPacketSize; /* data payload size for this ep in this cfg */ + __u8 bInterval; /* polling interval for this ep in this cfg */ +} __attribute__ ((packed)) ep_desc_t; + +// bEndpointAddress: +enum { + USB_OUT = 0, + USB_IN = 1 +}; + +#define USB_EP_ADDRESS(a,d) (((a)&0xf) | ((d) << 7)) + +/*======================================================= + * Default descriptor layout for SA-1100 and SA-1110 UDC + */ + +/* "config descriptor buffer" - that is, one config, + ..one interface and 2 endpoints */ +struct cdb { + config_desc_t cfg; + intf_desc_t intf; + ep_desc_t ep1; + ep_desc_t ep2; +} __attribute__ ((packed)); + + +/*======================================================= + * Descriptor API + */ + +/* Get the address of the statically allocated desc_t structure + in the usb core driver. Clients can modify this between + the time they call sa1100_usb_open() and sa1100_usb_start() +*/ +struct cdb *sa1100_usb_get_descriptor_ptr(void); + +#endif /* _SA1100_USB_H */ diff -urN orig/arch/arm/mach-sa1100/usb/sa1100usb.c linux/arch/arm/mach-sa1100/usb/sa1100usb.c --- orig/arch/arm/mach-sa1100/usb/sa1100usb.c Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/mach-sa1100/usb/sa1100usb.c Mon May 19 23:08:09 2003 @@ -0,0 +1,1159 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "buffer.h" +#include "usbdev.h" +#include "sa1100_usb.h" +#include "sa1100usb.h" + +#ifdef DEBUG +#define DPRINTK(fmt, args...) printk( fmt , ## args) +#else +#define DPRINTK(fmt, args...) +#endif + +static inline void pcs(const char *prefix) +{ +#ifdef DEBUG + __u32 foo = Ser0UDCCS0; + + DPRINTK("%s UDCAR: %d\n", prefix, Ser0UDCAR); + + printk("UDC: %s: %08x [ %s%s%s%s%s%s]\n", prefix, + foo, + foo & UDCCS0_SE ? "SE " : "", + foo & UDCCS0_DE ? "DE " : "", + foo & UDCCS0_FST ? "FST " : "", + foo & UDCCS0_SST ? "SST " : "", + foo & UDCCS0_IPR ? "IPR " : "", + foo & UDCCS0_OPR ? "OPR " : ""); +#endif +} + +/* + * soft_connect_hook() + * + * Some devices have platform-specific circuitry to make USB + * not seem to be plugged in, even when it is. This allows + * software to control when a device 'appears' on the USB bus + * (after Linux has booted and this driver has loaded, for + * example). If you have such a circuit, control it here. + */ +static inline void soft_connect_hook(int enable) +{ +#ifdef CONFIG_SA1100_EXTENEX1 + if (machine_is_extenex1()) { + if (enable) { + PPDR |= PPC_USB_SOFT_CON; + PPSR |= PPC_USB_SOFT_CON; + } else { + PPSR &= ~PPC_USB_SOFT_CON; + PPDR &= ~PPC_USB_SOFT_CON; + } + } +#endif +} + +/* + * disable the UDC at the source + */ +static inline void udc_disable(struct sausb_dev *usb) +{ + soft_connect_hook(0); + + usb->udccr = UDCCR_UDD | UDCCR_SUSIM; + + UDC_write(Ser0UDCCR, usb->udccr); +} + +/* + * Clear any pending write from the EP0 write buffer. + */ +static void ep0_clear_write(struct sausb_dev *usb) +{ + struct usb_buf *buf; + + buf = usb->wrbuf; + usb->wrint = NULL; + usb->wrbuf = NULL; + usb->wrptr = NULL; + usb->wrlen = 0; + + if (buf) + usbb_put(buf); +} + +static int udc_start(void *priv) +{ + struct sausb_dev *usb = priv; + + usb->ep[0].maxpktsize = 0; + usb->ep[1].maxpktsize = 0; + + /* + * start UDC internal machinery running, but mask interrupts. + */ + usb->udccr = UDCCR_SUSIM | UDCCR_TIM | UDCCR_RIM | UDCCR_EIM | + UDCCR_RESIM; + UDC_write(Ser0UDCCR, usb->udccr); + + udelay(100); + + /* + * clear all interrupt sources + */ + Ser0UDCSR = UDCSR_RSTIR | UDCSR_RESIR | UDCSR_EIR | + UDCSR_RIR | UDCSR_TIR | UDCSR_SUSIR; + + /* + * flush DMA and fire through some -EAGAINs + */ + udc_ep1_init(usb); + udc_ep2_init(usb); + + /* + * enable any platform specific hardware + */ + soft_connect_hook(1); + + /* + * Enable resume, suspend and endpoint 0 interrupts. Leave + * endpoint 1 and 2 interrupts masked. + * + * If you are unplugged you will immediately get a suspend + * interrupt. If you are plugged and have a soft connect-circuit, + * you will get a reset. If you are plugged without a soft-connect, + * I think you also get suspend. + */ + usb->udccr &= ~(UDCCR_SUSIM | UDCCR_EIM | UDCCR_RESIM); + UDC_write(Ser0UDCCR, usb->udccr); + + return 0; +} + +static int udc_stop(void *priv) +{ + struct sausb_dev *usb = priv; + + ep0_clear_write(usb); + + /* mask everything */ + Ser0UDCCR = 0xFC; + + udc_ep1_reset(usb); + udc_ep2_reset(usb); + + udc_disable(usb); + + return 0; +} + + + + + +/* + * some voodo I am adding, since the vanilla macros just aren't doing it + * 1Mar01ww + */ + +#define ABORT_BITS (UDCCS0_SST | UDCCS0_SE) +#define OK_TO_WRITE (!(Ser0UDCCS0 & ABORT_BITS)) +#define BOTH_BITS (UDCCS0_IPR | UDCCS0_DE) + +static void set_de(void) +{ + int i = 1; + + while (1) { + if (OK_TO_WRITE) { + Ser0UDCCS0 |= UDCCS0_DE; + } else { + DPRINTK("UDC: quitting set DE because SST or SE set\n"); + break; + } + if (Ser0UDCCS0 & UDCCS0_DE) + break; + udelay(i); + if (++i == 50) { + printk("UDC: Dangnabbbit! Cannot set DE! (DE=%8.8X CCS0=%8.8X)\n", + UDCCS0_DE, Ser0UDCCS0); + break; + } + } +} + +static void set_ipr(void) +{ + int i = 1; + + while (1) { + if (OK_TO_WRITE) { + Ser0UDCCS0 |= UDCCS0_IPR; + } else { + DPRINTK("UDC: Quitting set IPR because SST or SE set\n"); + break; + } + if (Ser0UDCCS0 & UDCCS0_IPR) + break; + udelay(i); + if (++i == 50) { + printk("UDC: Dangnabbbit! Cannot set IPR! (IPR=%8.8X CCS0=%8.8X)\n", + UDCCS0_IPR, Ser0UDCCS0); + break; + } + } +} + +static void set_ipr_and_de(void) +{ + int i = 1; + + while (1) { + if (OK_TO_WRITE) { + Ser0UDCCS0 |= BOTH_BITS; + } else { + DPRINTK("UDC: Quitting set IPR/DE because SST or SE set\n"); + break; + } + if ((Ser0UDCCS0 & BOTH_BITS) == BOTH_BITS) + break; + udelay(i); + if (++i == 50) { + printk("UDC: Dangnabbbit! Cannot set DE/IPR! (DE=%8.8X IPR=%8.8X CCS0=%8.8X)\n", + UDCCS0_DE, UDCCS0_IPR, Ser0UDCCS0); + break; + } + } +} + +static inline void set_cs_bits(__u32 bits) +{ + if (bits & (UDCCS0_SO | UDCCS0_SSE | UDCCS0_FST | UDCCS0_SST)) + Ser0UDCCS0 = bits; + else if ((bits & BOTH_BITS) == BOTH_BITS) + set_ipr_and_de(); + else if (bits & UDCCS0_IPR) + set_ipr(); + else if (bits & UDCCS0_DE) + set_de(); +} + +/* + * udc_ep0_write_fifo() + * + * Stick bytes in the 8 bytes endpoint zero FIFO. This version uses a + * variety of tricks to make sure the bytes are written correctly: + * 1. The count register is checked to see if the byte went in, + * and the write is attempted again if not. + * 2. An overall counter is used to break out so we don't hang in + * those (rare) cases where the UDC reverses direction of the + * FIFO underneath us without notification (in response to host + * aborting a setup transaction early). + */ +static void udc_ep0_write_fifo(struct sausb_dev *usb) +{ + unsigned int bytes_this_time = min(usb->wrlen, 8U); + int bytes_written = 0; + + DPRINTK("WF=%d: ", bytes_this_time); + + while (bytes_this_time--) { + unsigned int cwc; + int i; + + DPRINTK("%2.2X ", *usb->wrptr); + + cwc = Ser0UDCWC & 15; + + i = 10; + do { + Ser0UDCD0 = *usb->wrptr; + udelay(20); /* voodo 28Feb01ww */ + } while ((Ser0UDCWC & 15) == cwc && --i); + + if (i == 0) { + printk("UDC: udc_ep0_write_fifo: write failure\n"); + usb->ep0_wr_fifo_errs++; + } + + usb->wrptr++; + bytes_written++; + } + usb->wrlen -= bytes_written; + + /* following propagation voodo so maybe caller writing IPR in + ..a moment might actually get it to stick 28Feb01ww */ + udelay(300); + + usb->ep0_wr_bytes += bytes_written; + DPRINTK("L=%d WCR=%8.8X\n", usb->wrlen, Ser0UDCWC); +} + +/* + * read_fifo() + * + * Read 1-8 bytes out of FIFO and put in request. Called to do the + * initial read of setup requests from the host. Return number of + * bytes read. + * + * Like write fifo above, this driver uses multiple reads checked + * against the count register with an overall timeout. + */ +static int +udc_ep0_read_fifo(struct sausb_dev *usb, struct usb_ctrlrequest *request, int sz) +{ + unsigned char *pOut = (unsigned char *) request; + unsigned int fifo_count, bytes_read = 0; + + fifo_count = Ser0UDCWC & 15; + + DPRINTK("RF=%d ", fifo_count); + BUG_ON(fifo_count > sz); + + while (fifo_count--) { + unsigned int cwc; + int i; + + cwc = Ser0UDCWC & 15; + + i = 10; + do { + *pOut = (unsigned char) Ser0UDCD0; + udelay(20); + } while ((Ser0UDCWC & 15) == cwc && --i); + + if (i == 0) { + printk(KERN_ERR "UDC: udc_ep0_read_fifo: read failure\n"); + usb->ep0_rd_fifo_errs++; + break; + } + pOut++; + bytes_read++; + } + + DPRINTK("fc=%d\n", bytes_read); + usb->ep0_rd_bytes += bytes_read; + usb->ep0_rd_packets ++; + return bytes_read; +} + +static void ep0_sh_write_data(struct sausb_dev *usb) +{ + /* + * If bytes left is zero, we are coming in on the + * interrupt after the last packet went out. And + * we know we don't have to empty packet this + * transfer so just set DE and we are done + */ + set_cs_bits(UDCCS0_DE); +} + +static void ep0_sh_write_with_empty_packet(struct sausb_dev *usb) +{ + /* + * If bytes left is zero, we are coming in on the + * interrupt after the last packet went out. + * We must do short packet suff, so set DE and IPR + */ + set_cs_bits(UDCCS0_IPR | UDCCS0_DE); + DPRINTK("UDC: sh_write_empty: Sent empty packet\n"); +} + +static int udc_clear_opr(void) +{ + int i = 10000; + int is_clear; + + /*FIXME*/ + do { + Ser0UDCCS0 = UDCCS0_SO; + is_clear = !(Ser0UDCCS0 & UDCCS0_OPR); + if (i-- <= 0) + break; + } while (!is_clear); + + return is_clear; +} + +static int udc_ep0_queue(void *priv, struct usb_buf *buf, + unsigned int req_len) +{ + struct sausb_dev *usb = priv; + __u32 cs_reg_bits = UDCCS0_IPR; + + DPRINTK("a=%d r=%d\n", buf->len, req_len); + + /* + * thou shalt not enter data phase until + * Out Packet Ready is clear + */ + if (!udc_clear_opr()) { + printk("UDC: SO did not clear OPR\n"); + set_cs_bits(UDCCS0_DE | UDCCS0_SO); + usbb_put(buf); + return 1; + } + + usb->ep0_wr_packets++; + + usb->wrbuf = buf; + usb->wrptr = buf->data; + usb->wrlen = min(buf->len, req_len); + + udc_ep0_write_fifo(usb); + + if (usb->wrlen == 0) { + /* + * out in one, so data end + */ + cs_reg_bits |= UDCCS0_DE; + ep0_clear_write(usb); + } else if (buf->len < req_len) { + /* + * we are going to short-change host + * so need nul to not stall + */ + usb->wrint = ep0_sh_write_with_empty_packet; + } else { + /* + * we have as much or more than requested + */ + usb->wrint = ep0_sh_write_data; + } + + /* + * note: IPR was set uncondtionally at start of routine + */ + set_cs_bits(cs_reg_bits); + return 0; +} + +/* + * When SO and DE sent, UDC will enter status phase and ack, propagating + * new address to udc core. Next control transfer will be on the new + * address. + * + * You can't see the change in a read back of CAR until then (about 250us + * later, on my box). The original Intel driver sets S0 and DE and code + * to check that address has propagated here. I tried this, but it would + * only sometimes work! The rest of the time it would never propagate and + * we'd spin forever. So now I just set it and pray... + */ +static void udc_set_address(void *priv, unsigned int addr) +{ + Ser0UDCAR = addr; +} + +static void udc_set_config(void *priv, struct cdb *cdb) +{ + struct sausb_dev *usb = priv; + + if (cdb) { + udc_ep1_config(usb, le16_to_cpu(cdb->ep1.wMaxPacketSize)); + udc_ep2_config(usb, le16_to_cpu(cdb->ep2.wMaxPacketSize)); + } else { + udc_ep1_reset(usb); + udc_ep2_reset(usb); + } +} + +static unsigned int udc_ep_get_status(void *priv, unsigned int ep) +{ + unsigned int status; + + switch (ep) { + case 0: + status = (Ser0UDCCS0 & UDCCS0_FST) ? 1 : 0; + break; + + case 1: + status = (Ser0UDCCS1 & UDCCS1_FST) ? 1 : 0; + break; + + case 2: + status = (Ser0UDCCS2 & UDCCS2_FST) ? 1 : 0; + break; + + default: + printk(KERN_ERR "UDC: get_status: bad end point %d\n", ep); + status = 0; + break; + } + + return status; +} + +static void udc_ep_halt(void *priv, unsigned int ep, int halt) +{ + struct sausb_dev *usb = priv; + + printk("UDC: ep%d %s halt\n", ep, halt ? "set" : "clear"); + + switch (ep) { + case 1: + udc_ep1_halt(usb, halt); + break; + + case 2: + udc_ep2_halt(usb, halt); + break; + } +} + +static int udc_ep_queue(void *priv, unsigned int ep, char *buf, unsigned int len) +{ + struct sausb_dev *usb = priv; + int ret = -EINVAL; + + switch (ep) { + case 1: + ret = udc_ep1_queue_buffer(usb, buf, len); + break; + case 2: + ret = udc_ep2_send(usb, buf, len); + break; + } + + return ret; +} + +static void udc_ep_reset(void *priv, unsigned int ep) +{ + struct sausb_dev *usb = priv; + + switch (ep) { + case 1: + udc_ep1_recv_reset(usb); + break; + case 2: + udc_ep2_send_reset(usb); + break; + } +} + +static void udc_ep_callback(void *priv, unsigned int ep, usb_callback_t cb, void *data) +{ + struct sausb_dev *usb = priv; + unsigned long flags; + + if (ep == 1 || ep == 2) { + ep -= 1; + + spin_lock_irqsave(&usb->lock, flags); + usb->ep[ep].cb_func = cb; + usb->ep[ep].cb_data = data; + spin_unlock_irqrestore(&usb->lock, flags); + } +} + +static int udc_ep_idle(void *priv, unsigned int ep) +{ + struct sausb_dev *usb = priv; + int ret = -EINVAL; + + switch (ep) { + case 1: + break; + case 2: + ret = udc_ep2_idle(usb); + break; + } + + return ret; +} + +static struct usbc_driver usb_sa1100_drv = { + .owner = THIS_MODULE, + .name = "SA1100", + .start = udc_start, + .stop = udc_stop, + .ep0_queue = udc_ep0_queue, + .set_address = udc_set_address, + .set_config = udc_set_config, + .ep_get_status = udc_ep_get_status, + .ep_halt = udc_ep_halt, + .ep_queue = udc_ep_queue, + .ep_reset = udc_ep_reset, + .ep_callback = udc_ep_callback, + .ep_idle = udc_ep_idle, +}; + + +/* + * udc_ep0_read_packet() + * + * This setup handler is the "idle" state of endpoint zero. It looks for + * OPR (OUT packet ready) to see if a setup request has been been received + * from the host. Requests without a return data phase are immediately + * handled. Otherwise, the handler may be set to one of the sh_write_xxxx + * data pumpers if more than 8 bytes need to get back to the host. + */ +static void udc_ep0_read_packet(struct sausb_dev *usb, u32 cs_reg_in) +{ + struct usb_ctrlrequest req; + int n, ret = RET_NOACTION; + + /* + * A control request has been received by EP0. + * Read the request. + */ + n = udc_ep0_read_fifo(usb, &req, sizeof(req)); + + if (n == sizeof(req)) { + ret = usbctl_parse_request(usb->ctl, &req); + } else { + /* + * The request wasn't fully received. Force a + * stall. + */ + set_cs_bits(UDCCS0_FST | UDCCS0_SO); + printk("UDC: fifo read error: wanted %d bytes got %d\n", + sizeof(req), n); + } + + switch (ret) { + case RET_ERROR: + case RET_NOACTION: + break; + + case RET_ACK: + set_cs_bits(UDCCS0_DE | UDCCS0_SO); + break; + + case RET_REQERROR: + /* + * Send stall PID to host. + */ + set_cs_bits(UDCCS0_DE | UDCCS0_SO | UDCCS0_FST); + break; + } +} + +/* + * HACK DEBUG 3Mar01ww + * Well, maybe not, it really seems to help! 08Mar01ww + */ +static void core_kicker(struct sausb_dev *usb) +{ + __u32 car = Ser0UDCAR; + __u32 imp = Ser0UDCIMP; + __u32 omp = Ser0UDCOMP; + + UDC_set(Ser0UDCCR, UDCCR_UDD); + udelay(300); + UDC_clear(Ser0UDCCR, UDCCR_UDD); + + Ser0UDCAR = car; + Ser0UDCIMP = imp; + Ser0UDCOMP = omp; +} + +static void enable_resume_mask_suspend(struct sausb_dev *usb) +{ + int i; + + usb->udccr |= UDCCR_SUSIM; + + i = 1; + do { + Ser0UDCCR = usb->udccr; + udelay(i); + if (Ser0UDCCR == usb->udccr) + break; + if (Ser0UDCSR & UDCSR_RSTIR) + break; + } while (i++ < 50); + + if (i == 50) + printk("UDC: enable_resume: could not set SUSIM 0x%08x\n", + Ser0UDCCR); + + usb->udccr &= ~UDCCR_RESIM; + + i = 1; + do { + Ser0UDCCR = usb->udccr; + udelay(i); + if (Ser0UDCCR == usb->udccr) + break; + if (Ser0UDCSR & UDCSR_RSTIR) + break; + } while (i++ < 50); + + if (i == 50) + printk("UDC: enable_resume: could not clear RESIM 0x%08x\n", + Ser0UDCCR); +} + +static void enable_suspend_mask_resume(struct sausb_dev *usb) +{ + int i; + + usb->udccr |= UDCCR_RESIM; + + i = 1; + do { + Ser0UDCCR = usb->udccr; + udelay(i); + if (Ser0UDCCR == usb->udccr) + break; + if (Ser0UDCSR & UDCSR_RSTIR) + break; + } while (i++ < 50); + + if (i == 50) + printk("UDC: enable_resume: could not set RESIM 0x%08x\n", + Ser0UDCCR); + + usb->udccr &= ~UDCCR_SUSIM; + + i = 1; + do { + Ser0UDCCR = usb->udccr; + udelay(i); + if (Ser0UDCCR == usb->udccr) + break; + if (Ser0UDCSR & UDCSR_RSTIR) + break; + } while (i++ < 50); + + if (i == 50) + printk("UDC: enable_resume: could not clear SUSIM 0x%08x\n", + Ser0UDCCR); +} + +/* + * Reset received from HUB (or controller just went nuts and reset by + * itself!) so UDC core has been reset, track this state here + */ +static void udc_reset(struct sausb_dev *usb) +{ + if (usbctl_reset(usb->ctl)) { + ep0_clear_write(usb); + + /* + * Clean up endpoints. + */ + udc_ep1_reset(usb); + udc_ep2_reset(usb); + } + + /* + * mask reset ints, they flood during sequence, enable + * suspend and resume + */ + usb->udccr = (usb->udccr & ~(UDCCR_SUSIM | UDCCR_RESIM)) | UDCCR_REM; + Ser0UDCCR = usb->udccr; +} + +/* + * handle interrupt for endpoint zero + */ +static void udc_ep0_int_hndlr(struct sausb_dev *usb) +{ + u32 cs_reg_in; + + pcs("-->"); + + cs_reg_in = Ser0UDCCS0; + + /* + * If "setup end" has been set, the usb controller has terminated + * a setup transaction before we set DE. This happens during + * enumeration with some hosts. For example, the host will ask for + * our device descriptor and specify a return of 64 bytes. When we + * hand back the first 8, the host will know our max packet size + * and turn around and issue a new setup immediately. This causes + * the UDC to auto-ack the new setup and set SE. We must then + * "unload" (process) the new setup, which is what will happen + * after this preamble is finished executing. + */ + if (cs_reg_in & UDCCS0_SE) { + DPRINTK("UDC: early termination of setup\n"); + + /* + * Clear setup end + */ + set_cs_bits(UDCCS0_SSE); + + /* + * Clear any pending write. + */ + ep0_clear_write(usb); + } + + /* + * UDC sent a stall due to a protocol violation. + */ + if (cs_reg_in & UDCCS0_SST) { + usb->ep0_stall_sent++; + + DPRINTK("UDC: write_preamble: UDC sent stall\n"); + + /* + * Clear sent stall + */ + set_cs_bits(UDCCS0_SST); + + /* + * Clear any pending write. + */ + ep0_clear_write(usb); + } + + switch (cs_reg_in & (UDCCS0_OPR | UDCCS0_IPR)) { + case UDCCS0_OPR | UDCCS0_IPR: + DPRINTK("UDC: write_preamble: see OPR. Stopping write to " + "handle new SETUP\n"); + + /* + * very rarely, you can get OPR and + * leftover IPR. Try to clear + */ + UDC_clear(Ser0UDCCS0, UDCCS0_IPR); + + /* + * Clear any pending write. + */ + ep0_clear_write(usb); + + /*FALLTHROUGH*/ + case UDCCS0_OPR: + /* + * A new setup request is pending. Handle + * it. Note that we don't try to read a + * packet if SE was set and OPR is clear. + */ + udc_ep0_read_packet(usb, cs_reg_in); + break; + + case 0: + if (usb->wrint) { + if (usb->wrlen != 0) { + /* + * More data to go + */ + udc_ep0_write_fifo(usb); + set_ipr(); + } + + if (usb->wrlen == 0) { + /* + * All data sent. + */ + usb->wrint(usb); + + ep0_clear_write(usb); + } + } + break; + + case UDCCS0_IPR: + DPRINTK("UDC: IPR set, not writing\n"); + usb->ep0_early_irqs++; + break; + } + + pcs("<--"); +} + +static irqreturn_t udc_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct sausb_dev *usb = dev_id; + u32 status = Ser0UDCSR; + + /* + * ReSeT Interrupt Request - UDC has been reset + */ + if (status & UDCSR_RSTIR) { + udc_reset(usb); + + /* + * clear all pending sources + */ + UDC_flip(Ser0UDCSR, status); + return IRQ_HANDLED; + } + + /* + * else we have done something other than reset, + * so be sure reset enabled + */ + usb->udccr &= ~UDCCR_REM; + UDC_write(Ser0UDCCR, usb->udccr); + + /* + * RESume Interrupt Request + */ + if (status & UDCSR_RESIR) { + usbctl_resume(usb->ctl); + core_kicker(usb); + enable_suspend_mask_resume(usb); + } + + /* + * SUSpend Interrupt Request + */ + if (status & UDCSR_SUSIR) { + usbctl_suspend(usb->ctl); + enable_resume_mask_suspend(usb); + } + + /* + * clear all pending sources + */ + UDC_flip(Ser0UDCSR, status); + + if (status & UDCSR_EIR) + udc_ep0_int_hndlr(usb); + + if (status & UDCSR_RIR) + udc_ep1_int_hndlr(usb); + + if (status & UDCSR_TIR) + udc_ep2_int_hndlr(usb); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_PROC_FS + +#define SAY( fmt, args... ) p += sprintf(p, fmt, ## args ) +#define SAYV( num ) p += sprintf(p, num_fmt, "Value", num ) +#define SAYC( label, yn ) p += sprintf(p, yn_fmt, label, yn ) +#define SAYS( label, v ) p += sprintf(p, cnt_fmt, label, v ) + +static int +udc_read_proc(char *page, char **start, off_t off, int cnt, int *eof, + void *data) +{ + struct sausb_dev *usb = data; + char *p = page; + u32 v; + int len, i; + + p += usbctl_proc_info(usb->ctl, p); + p += sprintf(p, "\nUDC:\n"); + v = Ser0UDCAR; + p += sprintf(p, "Address\t: %d (0x%02x)\n", v, v); + v = Ser0UDCIMP; + p += sprintf(p, "IN max\t: %d (0x%02x)\n", v + 1, v); + v = Ser0UDCOMP; + p += sprintf(p, "OUT max\t: %d (0x%02x)\n", v + 1, v); + v = Ser0UDCCR; + p += sprintf(p, "UDCCR\t: 0x%02x " + "[ %cSUSIM %cTIM %cRIM %cEIM %cRESIM %cUDA %cUDD ] " + "(0x%02x)\n", + v, + v & UDCCR_SUSIM ? '+' : '-', v & UDCCR_TIM ? '+' : '-', + v & UDCCR_RIM ? '+' : '-', v & UDCCR_EIM ? '+' : '-', + v & UDCCR_RESIM ? '+' : '-', v & UDCCR_UDA ? '+' : '-', + v & UDCCR_UDD ? '+' : '-', usb->udccr); + v = Ser0UDCCS0; + p += sprintf(p, "UDCCS0\t: 0x%02x " + "[ %cSO %cSE %cDE %cFST %cSST %cIPR %cOPR ]\n", + v, + v & UDCCS0_SO ? '+' : '-', v & UDCCS0_SE ? '+' : '-', + v & UDCCS0_DE ? '+' : '-', v & UDCCS0_FST ? '+' : '-', + v & UDCCS0_SST ? '+' : '-', v & UDCCS0_IPR ? '+' : '-', + v & UDCCS0_OPR ? '+' : '-'); + v = Ser0UDCCS1; + p += sprintf(p, "UDCCS1\t: 0x%02x " + "[ %cRNE %cFST %cSST %cRPE %cRPC %cRFS ]\n", + v, + v & UDCCS1_RNE ? '+' : '-', v & UDCCS1_FST ? '+' : '-', + v & UDCCS1_SST ? '+' : '-', v & UDCCS1_RPE ? '+' : '-', + v & UDCCS1_RPC ? '+' : '-', v & UDCCS1_RFS ? '+' : '-'); + v = Ser0UDCCS2; + p += sprintf(p, "UDCCS2\t: 0x%02x " + "[ %cFST %cSST %cTUR %cTPE %cTPC %cTFS ]\n", + v, + v & UDCCS2_FST ? '+' : '-', v & UDCCS2_SST ? '+' : '-', + v & UDCCS2_TUR ? '+' : '-', v & UDCCS2_TPE ? '+' : '-', + v & UDCCS2_TPC ? '+' : '-', v & UDCCS2_TFS ? '+' : '-'); + + p += sprintf(p, "\n"); + p += sprintf(p, " Bytes Packets FIFO errs Max Sz\n"); + p += sprintf(p, "EP0 Rd: %10ld %10ld %10ld -\n", + usb->ep0_rd_bytes, + usb->ep0_rd_packets, + usb->ep0_rd_fifo_errs); + p += sprintf(p, "EP0 Wr: %10ld %10ld %10ld -\n", + usb->ep0_wr_bytes, + usb->ep0_wr_packets, + usb->ep0_wr_fifo_errs); + + for (i = 0; i < 2; i++) + p += sprintf(p, "EP%d : %10ld %10ld %10ld %6d\n", + i + 1, + usb->ep[i].bytes, + usb->ep[i].packets, + usb->ep[i].fifo_errs, + usb->ep[i].maxpktsize); + + p += sprintf(p, "Stalls sent\t: %ld\n", usb->ep0_stall_sent); + p += sprintf(p, "Early ints\t: %ld\n", usb->ep0_early_irqs); + +#if 0 + v = Ser0UDCSR; + SAY("\nUDC Interrupt Request Register\n"); + SAYV(v); + SAYC("Reset pending", (v & UDCSR_RSTIR) ? yes : no); + SAYC("Suspend pending", (v & UDCSR_SUSIR) ? yes : no); + SAYC("Resume pending", (v & UDCSR_RESIR) ? yes : no); + SAYC("ep0 pending", (v & UDCSR_EIR) ? yes : no); + SAYC("receiver pending", (v & UDCSR_RIR) ? yes : no); + SAYC("tramsitter pending", (v & UDCSR_TIR) ? yes : no); + +#ifdef CONFIG_SA1100_EXTENEX1 + SAYC("\nSoft connect", + (PPSR & PPC_USB_SOFT_CON) ? "Visible" : "Hidden"); +#endif +#endif + + len = (p - page) - off; + if (len < 0) + len = 0; + *eof = (len <= cnt) ? 1 : 0; + *start = page + off; + + return len; +} + +#endif + +extern struct usbctl usbctl; + +static int __devinit udc_probe(struct device *dev) +{ + struct sausb_dev *usb; + int retval; + + if (!request_mem_region(0x80000000, 0x10000, "sa11x0-udc")) + return -EBUSY; + + usb = kmalloc(sizeof(struct sausb_dev), GFP_KERNEL); + if (!usb) + return -ENOMEM; + + memset(usb, 0, sizeof(struct sausb_dev)); + dev_set_drvdata(dev, usb); + + usb_sa1100_drv.priv = usb; + + usb->dev = dev; + usb->ctl = &usbctl; + + spin_lock_init(&usb->lock); + + udc_disable(usb); + + usbctl_init(usb->ctl, &usb_sa1100_drv); + +#ifdef CONFIG_PROC_FS + create_proc_read_entry("sausb", 0, NULL, udc_read_proc, usb); +#endif + + /* setup rx dma */ + retval = sa1100_request_dma(DMA_Ser0UDCRd, "USB receive", + NULL, NULL, &usb->ep[0].dmach); + if (retval) { + printk("UDC: unable to register for rx dma rc=%d\n", + retval); + goto err; + } + + /* setup tx dma */ + retval = sa1100_request_dma(DMA_Ser0UDCWr, "USB transmit", + NULL, NULL, &usb->ep[1].dmach); + if (retval) { + printk("UDC: unable to register for tx dma rc=%d\n", + retval); + goto err; + } + + /* now allocate the IRQ. */ + retval = request_irq(IRQ_Ser0UDC, udc_interrupt, SA_INTERRUPT, + "SA USB core", usb); + if (retval) { + printk("UDC: couldn't request USB irq rc=%d\n", retval); + goto err; + } + + return retval; + + err: + if (usb->ep[2].dmach) { + sa1100_free_dma(usb->ep[2].dmach); + usb->ep[2].dmach = NULL; + } + if (usb->ep[1].dmach) { + sa1100_free_dma(usb->ep[1].dmach); + usb->ep[1].dmach = NULL; + } +#ifdef CONFIG_PROC_FS + remove_proc_entry("sausb", NULL); +#endif + release_mem_region(0x80000000, 0x10000); + return retval; +} + +/* + * Release DMA and interrupt resources + */ +static int __devexit udc_remove(struct device *dev) +{ + struct sausb_dev *usb = dev_get_drvdata(dev); + + dev_set_drvdata(dev, NULL); + +#ifdef CONFIG_PROC_FS + remove_proc_entry("sausb", NULL); +#endif + + udc_disable(usb); + + free_irq(IRQ_Ser0UDC, usb); + sa1100_free_dma(usb->ep[1].dmach); + sa1100_free_dma(usb->ep[0].dmach); + + usbctl_exit(usb->ctl); + + release_mem_region(0x80000000, 0x10000); + + return 0; +} + +static struct device_driver sa11x0usb_driver = { + .name = "sa11x0-udc", + .bus = &platform_bus_type, + .probe = udc_probe, + .remove = __devexit_p(udc_remove), +}; + +static int __init udc_init(void) +{ + return driver_register(&sa11x0usb_driver); +} + +static void __exit udc_exit(void) +{ + driver_unregister(&sa11x0usb_driver); +} + +module_init(udc_init); +module_exit(udc_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SA1100 USB Gadget driver"); diff -urN orig/arch/arm/mach-sa1100/usb/sa1100usb.h linux/arch/arm/mach-sa1100/usb/sa1100usb.h --- orig/arch/arm/mach-sa1100/usb/sa1100usb.h Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/mach-sa1100/usb/sa1100usb.h Mon May 19 18:02:02 2003 @@ -0,0 +1,136 @@ +/* + * Copyright (C) Compaq Computer Corporation, 1998, 1999 + * Copyright (C) Extenex Corporation 2001 + * + * usb_ctl.h + * + * PRIVATE interface used to share info among components of the SA-1100 USB + * core: usb_ctl, usb_ep0, usb_recv and usb_send. Clients of the USB core + * should use sa1100_usb.h. + * + */ +#ifndef SA1100USB_H +#define SA1100USB_H + +struct usbctl; + +struct sausb_dev { + struct device *dev; + struct usbctl *ctl; + spinlock_t lock; + + u32 udccr; + + /* + * EP0 write thread. + */ + void (*wrint)(struct sausb_dev *); + struct usb_buf *wrbuf; + unsigned char *wrptr; + unsigned int wrlen; + + /* + * EP0 statistics. + */ + unsigned long ep0_wr_fifo_errs; + unsigned long ep0_wr_bytes; + unsigned long ep0_wr_packets; + unsigned long ep0_rd_fifo_errs; + unsigned long ep0_rd_bytes; + unsigned long ep0_rd_packets; + unsigned long ep0_stall_sent; + unsigned long ep0_early_irqs; + + /* + * EP1 .. n + */ + struct { + dma_regs_t *dmach; + + dma_addr_t bufdma; + unsigned int buflen; + void *pktcpu; + dma_addr_t pktdma; + unsigned int pktlen; + unsigned int pktrem; + + void *cb_data; + void (*cb_func)(void *data, int flag, int size); + + u32 udccs; + unsigned int maxpktsize; + unsigned int configured; + unsigned int host_halt; + unsigned long fifo_errs; + unsigned long bytes; + unsigned long packets; + } ep[2]; +}; + +/* receiver */ +int ep1_recv(void); +void udc_ep1_init(struct sausb_dev *); +void udc_ep1_halt(struct sausb_dev *, int); +void udc_ep1_reset(struct sausb_dev *); +void udc_ep1_config(struct sausb_dev *, unsigned int); +void udc_ep1_int_hndlr(struct sausb_dev *); + +/* xmitter */ +void udc_ep2_init(struct sausb_dev *); +void udc_ep2_halt(struct sausb_dev *, int); +void udc_ep2_reset(struct sausb_dev *); +void udc_ep2_config(struct sausb_dev *, unsigned int); +void udc_ep2_int_hndlr(struct sausb_dev *); + +#define UDC_write(reg, val) do { \ + int i = 10000; \ + do { \ + (reg) = (val); \ + if (i-- <= 0) { \ + printk( "%s [%d]: write %#x to %p (%#x) failed\n", \ + __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ + break; \ + } \ + } while((reg) != (val)); \ +} while (0) + +#define UDC_set(reg, val) do { \ + int i = 10000; \ + do { \ + (reg) |= (val); \ + if (i-- <= 0) { \ + printk( "%s [%d]: set %#x of %p (%#x) failed\n", \ + __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ + break; \ + } \ + } while(!((reg) & (val))); \ +} while (0) + +#define UDC_clear(reg, val) do { \ + int i = 10000; \ + do { \ + (reg) &= ~(val); \ + if (i-- <= 0) { \ + printk( "%s [%d]: clear %#x of %p (%#x) failed\n", \ + __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ + break; \ + } \ + } while((reg) & (val)); \ +} while (0) + +#define UDC_flip(reg, val) do { \ + int i = 10000; \ + (reg) = (val); \ + do { \ + (reg) = (val); \ + if (i-- <= 0) { \ + printk( "%s [%d]: flip %#x of %p (%#x) failed\n", \ + __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ + break; \ + } \ + } while(((reg) & (val))); \ +} while (0) + +#define CHECK_ADDRESS { if ( Ser0UDCAR == 1 ) { printk("%s:%d I lost my address!!!\n",__FUNCTION__, __LINE__);}} + +#endif diff -urN orig/arch/arm/mach-sa1100/usb/strings.c linux/arch/arm/mach-sa1100/usb/strings.c --- orig/arch/arm/mach-sa1100/usb/strings.c Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/mach-sa1100/usb/strings.c Wed Aug 21 16:28:57 2002 @@ -0,0 +1,117 @@ +/* + * usb/strings.c + * + * Copyright (C) 2002 Russell King. + */ +#include +#include +#include +#include +#include +#include + +#include "buffer.h" +#include "strings.h" + +struct usb_buf *usbc_string_alloc(int len) +{ + struct usb_buf *buf; + int tot_len; + + tot_len = sizeof(struct usb_descriptor_header) + sizeof(u16) * len; + + buf = usbb_alloc(tot_len, GFP_KERNEL); + + if (buf) { + struct usb_string_descriptor *desc = usbb_push(buf, tot_len); + + desc->bLength = tot_len; + desc->bDescriptorType = USB_DT_STRING; + } + return buf; +} + +void usbc_string_free(struct usb_buf *buf) +{ + if (buf) + usbb_put(buf); +} + +void usbc_string_from_cstr(struct usb_buf *buf, const char *str) +{ + struct usb_string_descriptor *desc = usbc_string_desc(buf); + int i, len; + + len = strlen(str); + BUG_ON((sizeof(__u16) * len) > desc->bLength - sizeof(struct usb_descriptor_header)); + + for (i = 0; i < len; i++) + desc->wData[i] = cpu_to_le16(str[i]); +} + +int usbc_string_add(struct usbc_strs *table, struct usb_buf *buf) +{ + int nr, i; + + nr = -ENOSPC; + spin_lock_irq(&table->lock); + for (i = 0; i < NR_STRINGS; i++) + if (table->buf[i] == NULL) { + table->buf[i] = buf; + nr = i; + break; + } + spin_unlock_irq(&table->lock); + + return nr; +} + +void usbc_string_del(struct usbc_strs *table, int nr) +{ + if (nr < NR_STRINGS) { + spin_lock_irq(&table->lock); + table->buf[nr] = NULL; + spin_unlock_irq(&table->lock); + } +} + +struct usb_buf * +usbc_string_find(struct usbc_strs *table, unsigned int lang, unsigned int idx) +{ + struct usb_buf *buf = NULL; + + if (idx < NR_STRINGS) { + spin_lock_irq(&table->lock); + buf = usbb_get(table->buf[idx]); + spin_unlock_irq(&table->lock); + } + + return buf; +} + +void usbc_string_free_all(struct usbc_strs *table) +{ + int i; + + spin_lock_irq(&table->lock); + for (i = 0; i < NR_STRINGS; i++) { + usbc_string_free(table->buf[i]); + table->buf[i] = NULL; + } + spin_unlock_irq(&table->lock); +} + +void usbc_string_init(struct usbc_strs *table) +{ + memset(table, 0, sizeof(struct usbc_strs)); + spin_lock_init(&table->lock); +} + +EXPORT_SYMBOL(usbc_string_from_cstr); +EXPORT_SYMBOL(usbc_string_alloc); +EXPORT_SYMBOL(usbc_string_free); +EXPORT_SYMBOL(usbc_string_add); +EXPORT_SYMBOL(usbc_string_del); +EXPORT_SYMBOL(usbc_string_find); +EXPORT_SYMBOL(usbc_string_free_all); +EXPORT_SYMBOL(usbc_string_init); diff -urN orig/arch/arm/mach-sa1100/usb/strings.h linux/arch/arm/mach-sa1100/usb/strings.h --- orig/arch/arm/mach-sa1100/usb/strings.h Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/mach-sa1100/usb/strings.h Wed Aug 21 16:28:57 2002 @@ -0,0 +1,43 @@ +/* + * usb/strings.h + * + * Copyright (C) 2002 Russell King. + * + * USB device string handling, built upon usb buffers. + */ +#ifndef USBDEV_STRINGS_H +#define USBDEV_STRINGS_H + +#include + +struct usb_buf; + +#define NR_STRINGS 8 + +struct usb_string_descriptor; + +struct usbc_strs { + spinlock_t lock; + struct usb_buf *buf[NR_STRINGS]; +}; + +#define usbc_string_desc(buf) ((struct usb_string_descriptor *)(buf)->data) + +void usbc_string_from_cstr(struct usb_buf *buf, const char *str); +struct usb_buf *usbc_string_alloc(int len); +void usbc_string_free(struct usb_buf *buf); + +int usbc_string_add(struct usbc_strs *table, struct usb_buf *buf); +void usbc_string_del(struct usbc_strs *table, int nr); + +/* + * Note: usbc_string_find() increments the buffer use count. + * You must call usbb_put() after use. + */ +struct usb_buf * +usbc_string_find(struct usbc_strs *table, unsigned int lang, unsigned int idx); + +void usbc_string_free_all(struct usbc_strs *table); +void usbc_string_init(struct usbc_strs *table); + +#endif diff -urN orig/arch/arm/mach-sa1100/usb/usb-char.c linux/arch/arm/mach-sa1100/usb/usb-char.c --- orig/arch/arm/mach-sa1100/usb/usb-char.c Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/mach-sa1100/usb/usb-char.c Mon May 19 18:57:34 2003 @@ -0,0 +1,708 @@ +/* + * (C) Copyright 2000-2001 Extenex Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * usb-char.c + * + * Miscellaneous character device interface for SA1100 USB function + * driver. + * + * Background: + * The SA1100 function driver ported from the Compaq Itsy project + * has an interface, usb-eth.c, to feed network packets over the + * usb wire and into the Linux TCP/IP stack. + * + * This file replaces that one with a simple character device + * interface that allows unstructured "byte pipe" style reads and + * writes over the USB bulk endpoints by userspace programs. + * + * A new define, CONFIG_SA1100_USB_NETLINK, has been created that, + * when set, (the default) causes the ethernet interface to be used. + * When not set, this more pedestrian character interface is linked + * in instead. + * + * Please see linux/Documentation/arm/SA1100/SA1100_USB for details. + * + * ward.willats@extenex.com + * + * To do: + * - Can't dma into ring buffer directly with dma_map/unmap usb_recv + * uses and get bytes out at the same time DMA is going on. Investigate: + * a) changing usb_recv to use alloc_consistent() at client request; or + * b) non-ring-buffer based data structures. In the meantime, I am using + * a bounce buffer. Simple, but wasteful. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "usb-char.h" +#include "client.h" + + + +////////////////////////////////////////////////////////////////////////////// +// Driver Options +////////////////////////////////////////////////////////////////////////////// + +#define VERSION "0.4" + + +#define VERBOSITY 1 + +#if VERBOSITY +# define PRINTK(x, a...) printk (x, ## a) +#else +# define PRINTK(x, a...) /**/ +#endif + +////////////////////////////////////////////////////////////////////////////// +// Globals - Macros - Enums - Structures +////////////////////////////////////////////////////////////////////////////// +#ifndef MIN +#define MIN( a, b ) ((a)<(b)?(a):(b)) +#endif + +typedef int bool; enum { false = 0, true = 1 }; + +static const char pszMe[] = "usbchr: "; + +static wait_queue_head_t wq_read; +static wait_queue_head_t wq_write; +static wait_queue_head_t wq_poll; + +/* Serialze multiple writers onto the transmit hardware +.. since we sleep the writer during transmit to stay in +.. sync. (Multiple writers don't make much sense, but..) */ +static DECLARE_MUTEX( xmit_sem ); + +// size of usb DATA0/1 packets. 64 is standard maximum +// for bulk transport, though most hosts seem to be able +// to handle larger. +#define TX_PACKET_SIZE 64 +#define RX_PACKET_SIZE 64 +#define RBUF_SIZE (4*PAGE_SIZE) + +static struct wcirc_buf { + char *buf; + int in; + int out; +} rx_ring = { NULL, 0, 0 }; + +static struct { + unsigned long cnt_rx_complete; + unsigned long cnt_rx_errors; + unsigned long bytes_rx; + unsigned long cnt_tx_timeouts; + unsigned long cnt_tx_errors; + unsigned long bytes_tx; +} charstats; + + +static char * tx_buf = NULL; +static char * packet_buffer = NULL; +static int sending = 0; +static int usb_ref_count = 0; +static int last_tx_result = 0; +static int last_rx_result = 0; +static int last_tx_size = 0; +static struct timer_list tx_timer; + +////////////////////////////////////////////////////////////////////////////// +// Prototypes +////////////////////////////////////////////////////////////////////////////// +static char * what_the_f( int e ); +static void free_txrx_buffers( void ); +static void twiddle_descriptors(struct usb_client *client); +static int usbc_open( struct inode *pInode, struct file *pFile ); +static void rx_done_callback_packet_buffer(void *data, int flag, int size ); + +static void tx_timeout( unsigned long ); +static void tx_done_callback(void *data, int flag, int size ); + +static ssize_t usbc_read( struct file *, char *, size_t, loff_t * ); +static ssize_t usbc_write( struct file *, const char *, size_t, loff_t * ); +static unsigned int usbc_poll( struct file *pFile, poll_table * pWait ); +static int usbc_ioctl( struct inode *pInode, struct file *pFile, + unsigned int nCmd, unsigned long argument ); +static int usbc_close( struct inode *pInode, struct file *pFile ); + +#ifdef CONFIG_SA1100_EXTENEX1 +static void extenex_configured_notify_proc( void ); +#endif +////////////////////////////////////////////////////////////////////////////// +// Private Helpers +////////////////////////////////////////////////////////////////////////////// + +static char * what_the_f( int e ) +{ + char * p; + switch( e ) { + case 0: + p = "noErr"; + break; + case -ENODEV: + p = "ENODEV - usb not in config state"; + break; + case -EBUSY: + p = "EBUSY - another request on the hardware"; + break; + case -EAGAIN: + p = "EAGAIN"; + break; + case -EINTR: + p = "EINTR - interrupted\n"; + break; + case -EPIPE: + p = "EPIPE - zero length xfer\n"; + break; + default: + p = "????"; + break; + } + return p; +} + +static void free_txrx_buffers( void ) +{ + if ( rx_ring.buf != NULL ) { + kfree( rx_ring.buf ); + rx_ring.buf = NULL; + } + if ( packet_buffer != NULL ) { + kfree( packet_buffer ); + packet_buffer = NULL; + } + if ( tx_buf != NULL ) { + kfree( tx_buf ); + tx_buf = NULL; + } +} + +/* twiddle_descriptors() + * It is between open() and start(). Setup descriptors. + */ +static void twiddle_descriptors(struct usb_client *client) +{ + struct cdb *cdb = sa1100_usb_get_descriptor_ptr(); + + cdb->ep1.wMaxPacketSize = cpu_to_le16(RX_PACKET_SIZE); + cdb->ep2.wMaxPacketSize = cpu_to_le16(TX_PACKET_SIZE); + +#ifdef CONFIG_SA1100_EXTENEX1 + if (machine_is_extenex1()) { + int nr; + + cdb->cfg.bmAttributes = USB_CONFIG_SELFPOWERED; + cdb->cfg.MaxPower = 0; + + nr = sa1100_usb_add_string(client->ctl, "HHT Bulk Transfer"); + + if (nr > 0) + cdb->intf.iInterface = nr; + } +#endif +} + +////////////////////////////////////////////////////////////////////////////// +// ASYNCHRONOUS +////////////////////////////////////////////////////////////////////////////// +static void kick_start_rx( void ) +{ + if ( usb_ref_count ) { + int total_space = CIRC_SPACE( rx_ring.in, rx_ring.out, RBUF_SIZE ); + if ( total_space >= RX_PACKET_SIZE ) { + sa1100_usb_recv_set_callback(rx_done_callback_packet_buffer, NULL); + sa1100_usb_recv( packet_buffer, RX_PACKET_SIZE); + } + } +} +/* + * rx_done_callback_packet_buffer() + * We have completed a DMA xfer into the temp packet buffer. + * Move to ring. + * + * flag values: + * on init, -EAGAIN + * on reset, -EINTR + * on RPE, -EIO + * on short packet -EPIPE + */ +static void +rx_done_callback_packet_buffer(void *data, int flag, int size ) +{ + charstats.cnt_rx_complete++; + + if ( flag == 0 || flag == -EPIPE ) { + size_t n; + + charstats.bytes_rx += size; + + n = CIRC_SPACE_TO_END( rx_ring.in, rx_ring.out, RBUF_SIZE ); + n = MIN( n, size ); + size -= n; + + memcpy( &rx_ring.buf[ rx_ring.in ], packet_buffer, n ); + rx_ring.in = (rx_ring.in + n) & (RBUF_SIZE-1); + memcpy( &rx_ring.buf[ rx_ring.in ], packet_buffer + n, size ); + rx_ring.in = (rx_ring.in + size) & (RBUF_SIZE-1); + + wake_up_interruptible( &wq_read ); + wake_up_interruptible( &wq_poll ); + + last_rx_result = 0; + + kick_start_rx(); + + } else if ( flag != -EAGAIN ) { + charstats.cnt_rx_errors++; + last_rx_result = flag; + wake_up_interruptible( &wq_read ); + wake_up_interruptible( &wq_poll ); + } + else /* init, start a read */ + kick_start_rx(); +} + + +static void tx_timeout( unsigned long unused ) +{ + printk( "%stx timeout\n", pszMe ); + sa1100_usb_send_reset(); + charstats.cnt_tx_timeouts++; +} + + +// on init, -EAGAIN +// on reset, -EINTR +// on TPE, -EIO +static void tx_done_callback(void *data, int flags, int size ) +{ + if ( flags == 0 ) + charstats.bytes_tx += size; + else + charstats.cnt_tx_errors++; + last_tx_size = size; + last_tx_result = flags; + sending = 0; + wake_up_interruptible( &wq_write ); + wake_up_interruptible( &wq_poll ); +} + + +static struct usb_client usbc_client = { + .name = "usb-char", + + /* + * USB client identification for host use in CPU endian. + */ + .vendor = 0, + .product = 0, + .version = 0, + .class = 0xff, + .subclass = 0, + .protocol = 0, +}; + +#ifdef CONFIG_SA1100_EXTENEX1 +#include "../../../drivers/char/ex_gpio.h" +static void extenex_state_change(void *data, int state, int oldstate) +{ + if (exgpio_play_string( "440,1:698,1") == -EAGAIN) + printk( "%sWanted to BEEP but ex_gpio not open\n", pszMe ); +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// Workers +////////////////////////////////////////////////////////////////////////////// + +static int usbc_open(struct inode *pInode, struct file *pFile) +{ + int retval = 0; + + PRINTK( KERN_DEBUG "%sopen()\n", pszMe ); + +#ifdef CONFIG_SA1100_EXTENEX1 + if (machine_is_extenex1()) { + usbc_client.vendor = 0x0c9f; + usbc_client.product = 0x0100; + usbc_client.version = 0x0001; + usbc_client.manufacturer_str = "Extenex"; + usbc_client.product_str = "Handheld Theater"; + usbc_client.serial_str = "00000000"; + usbc_client.state_change = extenex_state_change; + } +#endif + + /* start usb core */ + retval = usbctl_open(&usbc_client); + if (retval) + return retval; + + /* allocate memory */ + if ( usb_ref_count == 0 ) { + tx_buf = (char*) kmalloc( TX_PACKET_SIZE, GFP_KERNEL | GFP_DMA ); + if ( tx_buf == NULL ) { + printk( "%sARGHH! COULD NOT ALLOCATE TX BUFFER\n", pszMe ); + goto malloc_fail; + } + rx_ring.buf = + (char*) kmalloc( RBUF_SIZE, GFP_KERNEL ); + + if ( rx_ring.buf == NULL ) { + printk( "%sARGHH! COULD NOT ALLOCATE RX BUFFER\n", pszMe ); + goto malloc_fail; + } + + packet_buffer = + (char*) kmalloc( RX_PACKET_SIZE, GFP_KERNEL | GFP_DMA ); + + if ( packet_buffer == NULL ) { + printk( "%sARGHH! COULD NOT ALLOCATE RX PACKET BUFFER\n", pszMe ); + goto malloc_fail; + } + rx_ring.in = rx_ring.out = 0; + memset( &charstats, 0, sizeof( charstats ) ); + sending = 0; + last_tx_result = 0; + last_tx_size = 0; + } + + /* modify default descriptors */ + twiddle_descriptors(&usbc_client); + + retval = usbctl_start(&usbc_client); + if ( retval ) { + printk( "%sAGHH! Could not USB core\n", pszMe ); + free_txrx_buffers(); + return retval; + } + usb_ref_count++; /* must do _before_ kick_start() */ + MOD_INC_USE_COUNT; + kick_start_rx(); + return 0; + + malloc_fail: + free_txrx_buffers(); + return -ENOMEM; +} + +/* + * Read endpoint. Note that you can issue a read to an + * unconfigured endpoint. Eventually, the host may come along + * and configure underneath this module and data will appear. + */ +static ssize_t usbc_read( struct file *pFile, char *pUserBuffer, + size_t stCount, loff_t *pPos ) +{ + ssize_t retval; + unsigned long flags; + DECLARE_WAITQUEUE( wait, current ); + + PRINTK( KERN_DEBUG "%sread()\n", pszMe ); + + local_irq_save(flags); + if ( last_rx_result == 0 ) { + local_irq_restore( flags ); + } else { /* an error happended and receiver is paused */ + local_irq_restore( flags ); + last_rx_result = 0; + kick_start_rx(); + } + + add_wait_queue( &wq_read, &wait ); + while( 1 ) { + ssize_t bytes_avail; + ssize_t bytes_to_end; + + set_current_state( TASK_INTERRUPTIBLE ); + + /* snap ring buf state */ + local_irq_save( flags ); + bytes_avail = CIRC_CNT( rx_ring.in, rx_ring.out, RBUF_SIZE ); + bytes_to_end = CIRC_CNT_TO_END( rx_ring.in, rx_ring.out, RBUF_SIZE ); + local_irq_restore( flags ); + + if ( bytes_avail != 0 ) { + ssize_t bytes_to_move = MIN( stCount, bytes_avail ); + retval = 0; // will be bytes transfered + if ( bytes_to_move != 0 ) { + size_t n = MIN( bytes_to_end, bytes_to_move ); + if ( copy_to_user( pUserBuffer, + &rx_ring.buf[ rx_ring.out ], + n ) ) { + retval = -EFAULT; + break; + } + bytes_to_move -= n; + retval += n; + // might go 1 char off end, so wrap + rx_ring.out = ( rx_ring.out + n ) & (RBUF_SIZE-1); + if ( copy_to_user( pUserBuffer + n, + &rx_ring.buf[ rx_ring.out ], + bytes_to_move ) + ) { + retval = -EFAULT; + break; + } + rx_ring.out += bytes_to_move; // cannot wrap + retval += bytes_to_move; + kick_start_rx(); + } + break; + } + else if ( last_rx_result ) { + retval = last_rx_result; + break; + } + else if ( pFile->f_flags & O_NONBLOCK ) { // no data, can't sleep + retval = -EAGAIN; + break; + } + else if ( signal_pending( current ) ) { // no data, can sleep, but signal + retval = -ERESTARTSYS; + break; + } + schedule(); // no data, can sleep + } + set_current_state( TASK_RUNNING ); + remove_wait_queue( &wq_read, &wait ); + + if ( retval < 0 ) + printk( "%sread error %d - %s\n", pszMe, retval, what_the_f( retval ) ); + return retval; +} + +/* + * Write endpoint. This routine attempts to break the passed in buffer + * into usb DATA0/1 packet size chunks and send them to the host. + * (The lower-level driver tries to do this too, but easier for us + * to manage things here.) + * + * We are at the mercy of the host here, in that it must send an IN + * token to us to pull this data back, so hopefully some higher level + * protocol is expecting traffic to flow in that direction so the host + * is actually polling us. To guard against hangs, a 5 second timeout + * is used. + * + * This routine takes some care to only report bytes sent that have + * actually made it across the wire. Thus we try to stay in lockstep + * with the completion routine and only have one packet on the xmit + * hardware at a time. Multiple simultaneous writers will get + * "undefined" results. + * + */ +static ssize_t usbc_write( struct file *pFile, const char * pUserBuffer, + size_t stCount, loff_t *pPos ) +{ + ssize_t retval = 0; + ssize_t stSent = 0; + + DECLARE_WAITQUEUE( wait, current ); + + PRINTK( KERN_DEBUG "%swrite() %d bytes\n", pszMe, stCount ); + + down( &xmit_sem ); // only one thread onto the hardware at a time + + while( stCount != 0 && retval == 0 ) { + int nThisTime = MIN( TX_PACKET_SIZE, stCount ); + copy_from_user( tx_buf, pUserBuffer, nThisTime ); + sending = nThisTime; + sa1100_usb_send_set_callback(tx_done_callback, NULL); + retval = sa1100_usb_send( tx_buf, nThisTime); + if ( retval < 0 ) { + char * p = what_the_f( retval ); + printk( "%sCould not queue xmission. rc=%d - %s\n", + pszMe, retval, p ); + sending = 0; + break; + } + /* now have something on the diving board */ + add_wait_queue( &wq_write, &wait ); + tx_timer.expires = jiffies + ( HZ * 5 ); + add_timer( &tx_timer ); + while( 1 ) { + set_current_state( TASK_INTERRUPTIBLE ); + if ( sending == 0 ) { /* it jumped into the pool */ + del_timer( &tx_timer ); + retval = last_tx_result; + if ( retval == 0 ) { + stSent += last_tx_size; + pUserBuffer += last_tx_size; + stCount -= last_tx_size; + } + else + printk( "%sxmission error rc=%d - %s\n", + pszMe, retval, what_the_f(retval) ); + break; + } + else if ( signal_pending( current ) ) { + del_timer( &tx_timer ); + printk( "%ssignal\n", pszMe ); + retval = -ERESTARTSYS; + break; + } + schedule(); + } + set_current_state( TASK_RUNNING ); + remove_wait_queue( &wq_write, &wait ); + } + + up( &xmit_sem ); + + if ( 0 == retval ) + retval = stSent; + return retval; +} + +static unsigned int usbc_poll( struct file *pFile, poll_table * pWait ) +{ + unsigned int retval = 0; + + PRINTK( KERN_DEBUG "%poll()\n", pszMe ); + + poll_wait( pFile, &wq_poll, pWait ); + + if ( CIRC_CNT( rx_ring.in, rx_ring.out, RBUF_SIZE ) ) + retval |= POLLIN | POLLRDNORM; + if ( sa1100_usb_xmitter_avail() ) + retval |= POLLOUT | POLLWRNORM; + return retval; +} + +static int usbc_ioctl( struct inode *pInode, struct file *pFile, + unsigned int nCmd, unsigned long argument ) +{ + int retval = 0; + + switch( nCmd ) { + + case USBC_IOC_FLUSH_RECEIVER: + sa1100_usb_recv_reset(); + rx_ring.in = rx_ring.out = 0; + break; + + case USBC_IOC_FLUSH_TRANSMITTER: + sa1100_usb_send_reset(); + break; + + case USBC_IOC_FLUSH_ALL: + sa1100_usb_recv_reset(); + rx_ring.in = rx_ring.out = 0; + sa1100_usb_send_reset(); + break; + + default: + retval = -ENOIOCTLCMD; + break; + + } + return retval; +} + + +static int usbc_close( struct inode *pInode, struct file * pFile ) +{ + PRINTK( KERN_DEBUG "%sclose()\n", pszMe ); + if ( --usb_ref_count == 0 ) { + down( &xmit_sem ); + usbctl_stop(&usbc_client); + free_txrx_buffers(); + del_timer( &tx_timer ); + usbctl_close(&usbc_client); + up(&xmit_sem); + } + MOD_DEC_USE_COUNT; + return 0; +} + +////////////////////////////////////////////////////////////////////////////// +// Initialization +////////////////////////////////////////////////////////////////////////////// + +static struct file_operations usbc_fops = { + owner: THIS_MODULE, + open: usbc_open, + read: usbc_read, + write: usbc_write, + poll: usbc_poll, + ioctl: usbc_ioctl, + release: usbc_close, +}; + +static struct miscdevice usbc_misc_device = { + USBC_MINOR, + "usb_char", + &usbc_fops +}; + +/* + * usbc_init() + */ + +static int __init usbc_init( void ) +{ + int rc; + +#if !defined( CONFIG_ARCH_SA1100 ) + return -ENODEV; +#endif + + if ( (rc = misc_register( &usbc_misc_device )) != 0 ) { + printk( KERN_WARNING "%sCould not register device 10, " + "%d. (%d)\n", pszMe, USBC_MINOR, rc ); + return -EBUSY; + } + + // initialize wait queues + init_waitqueue_head( &wq_read ); + init_waitqueue_head( &wq_write ); + init_waitqueue_head( &wq_poll ); + + // initialize tx timeout timer + init_timer( &tx_timer ); + tx_timer.function = tx_timeout; + + printk( KERN_INFO "USB Function Character Driver Interface" + " - %s, (C) 2001, Extenex Corp.\n", VERSION + ); + + return rc; +} + +static void __exit usbc_exit( void ) +{ +} + +module_init(usbc_init); +module_exit(usbc_exit); + +// end: usb-char.c + +MODULE_LICENSE("GPL"); diff -urN orig/arch/arm/mach-sa1100/usb/usb-char.h linux/arch/arm/mach-sa1100/usb/usb-char.h --- orig/arch/arm/mach-sa1100/usb/usb-char.h Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/mach-sa1100/usb/usb-char.h Wed Aug 21 16:28:57 2002 @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2001 Extenex Corporation + * + * usb-char.h + * + * Character device emulation client for SA-1100 client usb core. + * + * + * + */ +#ifndef _USB_CHAR_H +#define _USB_CHAR_H + +#define USBC_MAJOR 10 /* miscellaneous character device */ +#define USBC_MINOR 240 /* in the "reserved for local use" range */ + +#define USBC_MAGIC 0x8E + +/* zap everything in receive ring buffer */ +#define USBC_IOC_FLUSH_RECEIVER _IO( USBC_MAGIC, 0x01 ) + +/* reset transmitter */ +#define USBC_IOC_FLUSH_TRANSMITTER _IO( USBC_MAGIC, 0x02 ) + +/* do both of above */ +#define USBC_IOC_FLUSH_ALL _IO( USBC_MAGIC, 0x03 ) + + + + + + +#endif /* _USB_CHAR_H */ + diff -urN orig/arch/arm/mach-sa1100/usb/usb-eth.c linux/arch/arm/mach-sa1100/usb/usb-eth.c --- orig/arch/arm/mach-sa1100/usb/usb-eth.c Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/mach-sa1100/usb/usb-eth.c Tue Mar 18 14:07:43 2003 @@ -0,0 +1,535 @@ +/* + * Network driver for the SA1100 USB client function + * Copyright (c) 2001 by Nicolas Pitre + * + * This code was loosely inspired by the original initial ethernet test driver + * Copyright (c) Compaq Computer Corporation, 1999 + * + * 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. + * + * Issues: + * - DMA needs 8 byte aligned buffer, but causes inefficiencies + * in the IP code. + * - stall endpoint operations appeared to be very unstable. + */ + +/* + * Define RX_NO_COPY if you want data to arrive directly into the + * receive network buffers, instead of arriving into bounce buffer + * and then get copied to network buffer. + * + * Since the SA1100 DMA engine is unable to cope with unaligned + * buffer addresses, we need to use bounce buffers or suffer the + * alignment trap performance hit. + */ +#undef RX_NO_COPY + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "client.h" + + +#define ETHERNET_VENDOR_ID 0x049f +#define ETHERNET_PRODUCT_ID 0x505A +#define MAX_PACKET 32768 + +/* + * This is our usb "packet size", and must match the host "packet size". + */ +static int usb_rsize = 64; +static int usb_wsize = 64; + +struct usbe_info { + struct net_device dev; + struct usb_client client; + struct sk_buff *cur_tx_skb; + struct sk_buff *next_tx_skb; + struct sk_buff *cur_rx_skb; + struct sk_buff *next_rx_skb; +#ifndef RX_NO_COPY + char *dmabuf; // dma expects it's buffers to be aligned on 8 bytes boundary +#endif + struct net_device_stats stats; +}; + + +static int usbeth_change_mtu(struct net_device *dev, int new_mtu) +{ + if (new_mtu <= sizeof(struct ethhdr) || new_mtu > MAX_PACKET) + return -EINVAL; + + // no second zero-length packet read wanted after mtu-sized packets + if (((new_mtu + sizeof(struct ethhdr)) % usb_rsize) == 0) + return -EDOM; + + dev->mtu = new_mtu; + return 0; +} + +static struct sk_buff *usb_new_recv_skb(struct usbe_info *usbe) +{ + struct sk_buff *skb; + + skb = alloc_skb(2 + sizeof(struct ethhdr) + usbe->dev.mtu, + GFP_ATOMIC); + + if (skb) + skb_reserve(skb, 2); + + return skb; +} + +static void usbeth_recv_callback(void *data, int flag, int len) +{ + struct usbe_info *usbe = data; + struct sk_buff *skb; + unsigned int size; + char *buf; + + skb = usbe->cur_rx_skb; + + /* flag validation */ + if (flag != 0) + goto error; + + /* + * Make sure we have enough room left in the buffer. + */ + if (len > skb_tailroom(skb)) { + usbe->stats.rx_over_errors++; + usbe->stats.rx_errors++; + goto oversize; + } + + /* + * If the packet is smaller than usb_rsize bytes, the packet + * is complete, and we need to use the next receive buffer. + */ + if (len != usb_rsize) + usbe->cur_rx_skb = usbe->next_rx_skb; + + /* + * Put the data onto the socket buffer and resume USB receive. + */ +#ifndef RX_NO_COPY + memcpy(skb_put(skb, len), usbe->dmabuf, len); + buf = usbe->dmabuf; + size = usb_rsize; +#else + skb_put(skb, len); + buf = usbe->cur_rx_skb->tail; + size = skb_tailroom(usbe->cur_rx_skb); +#endif + usbctl_ep_queue_buffer(usbe->client.ctl, 1, buf, size); + + if (len == usb_rsize) + return; + + /* + * A frame must contain at least an ethernet header. + */ + if (skb->len < sizeof(struct ethhdr)) { + usbe->stats.rx_length_errors++; + usbe->stats.rx_errors++; + goto recycle; + } + + /* + * MAC must match our address or the broadcast address. + * Really, we should let any packet through, otherwise + * things that rely on multicast won't work. + */ + if (memcmp(skb->data, usbe->dev.dev_addr, ETH_ALEN) && + memcmp(skb->data, usbe->dev.broadcast, ETH_ALEN)) { + usbe->stats.rx_frame_errors++; + usbe->stats.rx_errors++; + goto recycle; + } + + /* + * We're going to consume this SKB. Get a new skb to + * replace it with. IF this fails, we'd better recycle + * the one we have. + */ + usbe->next_rx_skb = usb_new_recv_skb(usbe); + if (!usbe->next_rx_skb) { + if (net_ratelimit()) + printk(KERN_ERR "%s: can't allocate new rx skb\n", + usbe->dev.name); + usbe->stats.rx_dropped++; + goto recycle; + } + +// FIXME: eth_copy_and_csum "small" packets to new SKB (small < ~200 bytes) ? + + usbe->stats.rx_packets++; + usbe->stats.rx_bytes += skb->len; + usbe->dev.last_rx = jiffies; + + skb->dev = &usbe->dev; + skb->protocol = eth_type_trans(skb, &usbe->dev); + skb->ip_summed = CHECKSUM_NONE; + + if (netif_rx(skb) == NET_RX_DROP) + usbe->stats.rx_dropped++; + return; + + error: + /* + * Oops, IO error, or stalled. + */ + switch (flag) { + case -EIO: /* aborted transfer */ + usbe->stats.rx_errors++; + break; + + case -EPIPE: /* fifo screwed/no data */ + usbe->stats.rx_fifo_errors++; + usbe->stats.rx_errors++; + break; + + case -EINTR: /* reset */ + break; + + case -EAGAIN: /* initialisation */ + break; + } + + oversize: + skb_trim(skb, 0); + +#ifndef RX_NO_COPY + buf = usbe->dmabuf; + size = usb_rsize; +#else + buf = skb->tail; + size = skb_tailroom(skb); +#endif + usbctl_ep_queue_buffer(usbe->client.ctl, 1, buf, size); + return; + + recycle: + skb_trim(skb, 0); + usbe->next_rx_skb = skb; + return; +} + +/* + * Send a skb. + * + * Note that the receiver expects the last packet to be a non-multiple + * of its rsize. If the packet length is a muliple of wsize (and + * therefore the remote rsize) tweak the length. + */ +static void usbeth_send(struct sk_buff *skb, struct usbe_info *usbe) +{ + unsigned int len = skb->len; + int ret; + + if ((len % usb_wsize) == 0) + len++; + + ret = usbctl_ep_queue_buffer(usbe->client.ctl, 2, skb->data, len); + if (ret) { + printk(KERN_ERR "%s: tx dropping packet: %d\n", + usbe->dev.name, ret); + + /* + * If the USB core can't accept the packet, we drop it. + */ + dev_kfree_skb_irq(skb); + + usbe->cur_tx_skb = NULL; + usbe->stats.tx_carrier_errors++; + } else { + usbe->dev.trans_start = jiffies; + } +} + +static void usbeth_send_callback(void *data, int flag, int size) +{ + struct usbe_info *usbe = data; + struct sk_buff *skb = usbe->cur_tx_skb; + + switch (flag) { + case 0: + usbe->stats.tx_packets++; + usbe->stats.tx_bytes += skb->len; + break; + case -EIO: + usbe->stats.tx_errors++; + break; + default: + usbe->stats.tx_dropped++; + break; + } + + dev_kfree_skb_irq(skb); + + skb = usbe->cur_tx_skb = usbe->next_tx_skb; + usbe->next_tx_skb = NULL; + + if (skb) + usbeth_send(skb, usbe); + + netif_wake_queue(&usbe->dev); +} + +static int usbeth_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct usbe_info *usbe = dev->priv; + unsigned long flags; + + if (usbe->next_tx_skb) { + printk(KERN_ERR "%s: called with next_tx_skb != NULL\n", + usbe->dev.name); + return 1; + } + + local_irq_save(flags); + if (usbe->cur_tx_skb) { + usbe->next_tx_skb = skb; + netif_stop_queue(dev); + } else { + usbe->cur_tx_skb = skb; + + usbeth_send(skb, usbe); + } + local_irq_restore(flags); + return 0; +} + +/* + * Transmit timed out. Reset the endpoint, and re-queue the pending + * packet. If we have a free transmit slot, wake the transmit queue. + */ +static void usbeth_xmit_timeout(struct net_device *dev) +{ + struct usbe_info *usbe = dev->priv; + unsigned long flags; + + usbctl_ep_reset(usbe->client.ctl, 2); + + local_irq_save(flags); + if (usbe->cur_tx_skb) + usbeth_send(usbe->cur_tx_skb, usbe); + + if (usbe->next_tx_skb == NULL) + netif_wake_queue(dev); + + usbe->stats.tx_errors++; + local_irq_restore(flags); +} + +static int usbeth_open(struct net_device *dev) +{ + struct usbe_info *usbe = dev->priv; + unsigned char *buf; + unsigned int size; + + usbctl_ep_set_callback(usbe->client.ctl, 2, usbeth_send_callback, usbe); + usbctl_ep_set_callback(usbe->client.ctl, 1, usbeth_recv_callback, usbe); + + usbe->cur_tx_skb = usbe->next_tx_skb = NULL; + usbe->cur_rx_skb = usb_new_recv_skb(usbe); + usbe->next_rx_skb = usb_new_recv_skb(usbe); + if (!usbe->cur_rx_skb || !usbe->next_rx_skb) { + printk(KERN_ERR "%s: can't allocate new skb\n", + usbe->dev.name); + if (usbe->cur_rx_skb) + kfree_skb(usbe->cur_rx_skb); + if (usbe->next_rx_skb) + kfree_skb(usbe->next_rx_skb); + return -ENOMEM;; + } +#ifndef RX_NO_COPY + buf = usbe->dmabuf; + size = usb_rsize; +#else + buf = usbe->cur_rx_skb->tail; + size = skb_tailroom(usbe->cur_rx_skb); +#endif + usbctl_ep_queue_buffer(usbe->client.ctl, 1, buf, size); + + if (netif_carrier_ok(dev)) + netif_start_queue(dev); + + return 0; +} + +static int usbeth_close(struct net_device *dev) +{ + struct usbe_info *usbe = dev->priv; + + netif_stop_queue(dev); + + usbctl_ep_set_callback(usbe->client.ctl, 2, NULL, NULL); + usbctl_ep_set_callback(usbe->client.ctl, 1, NULL, NULL); + usbctl_ep_reset(usbe->client.ctl, 2); + usbctl_ep_reset(usbe->client.ctl, 1); + + if (usbe->cur_tx_skb) + kfree_skb(usbe->cur_tx_skb); + if (usbe->next_tx_skb) + kfree_skb(usbe->next_tx_skb); + if (usbe->cur_rx_skb) + kfree_skb(usbe->cur_rx_skb); + if (usbe->next_rx_skb) + kfree_skb(usbe->next_rx_skb); + + return 0; +} + +static struct net_device_stats *usbeth_stats(struct net_device *dev) +{ + struct usbe_info *usbe = dev->priv; + + return &usbe->stats; +} + +static int __init usbeth_probe(struct net_device *dev) +{ + u8 node_id[ETH_ALEN]; + + SET_MODULE_OWNER(dev); + + /* + * Assign the hardware address of the board: + * generate it randomly, as there can be many such + * devices on the bus. + */ + get_random_bytes(node_id, sizeof node_id); + node_id[0] &= 0xfe; // clear multicast bit + memcpy(dev->dev_addr, node_id, sizeof node_id); + + ether_setup(dev); + dev->flags &= ~IFF_MULTICAST; + dev->flags &= ~IFF_BROADCAST; + //dev->flags |= IFF_NOARP; + + return 0; +} + +/* + * This is called when something in the upper usb client layers + * changes that affects the endpoint connectivity state (eg, + * connection or disconnection from the host.) We probably want + * to do some more handling here, like kicking off a pending + * transmission if we're running? + */ +static void usbeth_state_change(void *data, int state, int oldstate) +{ + struct usbe_info *usbe = data; + + if (state == USB_STATE_CONFIGURED) { + netif_carrier_on(&usbe->dev); + if (netif_running(&usbe->dev)) + netif_wake_queue(&usbe->dev); + } else { + if (netif_running(&usbe->dev)) + netif_stop_queue(&usbe->dev); + netif_carrier_off(&usbe->dev); + } +} + +static struct usbe_info usbe_info = { + .dev = { + .name = "usbf", + .init = usbeth_probe, + .get_stats = usbeth_stats, + .watchdog_timeo = 1 * HZ, + .open = usbeth_open, + .stop = usbeth_close, + .hard_start_xmit = usbeth_xmit, + .change_mtu = usbeth_change_mtu, + .tx_timeout = usbeth_xmit_timeout, + .priv = &usbe_info, + }, + .client = { + .name = "usbeth", + .priv = &usbe_info, + .state_change = usbeth_state_change, + + /* + * USB client identification for host use in CPU endian. + */ + .vendor = ETHERNET_VENDOR_ID, + .product = ETHERNET_PRODUCT_ID, + .version = 0, + .class = 0xff, /* vendor specific */ + .subclass = 0, + .protocol = 0, + + .product_str = "SA1100 USB NIC", + }, +}; + +static int __init usbeth_init(void) +{ + int rc; + +#ifndef RX_NO_COPY + usbe_info.dmabuf = kmalloc(usb_rsize, GFP_KERNEL | GFP_DMA); + if (!usbe_info.dmabuf) + return -ENOMEM; +#endif + + if (register_netdev(&usbe_info.dev) != 0) { +#ifndef RX_NO_COPY + kfree(usbe_info.dmabuf); +#endif + return -EIO; + } + + rc = usbctl_open(&usbe_info.client); + if (rc == 0) { + struct cdb *cdb = sa1100_usb_get_descriptor_ptr(); + + cdb->ep1.wMaxPacketSize = cpu_to_le16(usb_rsize); + cdb->ep2.wMaxPacketSize = cpu_to_le16(usb_wsize); + + rc = usbctl_start(&usbe_info.client); + if (rc) + usbctl_close(&usbe_info.client); + } + + if (rc) { + unregister_netdev(&usbe_info.dev); +#ifndef RX_NO_COPY + kfree(usbe_info.dmabuf); +#endif + } + + return rc; +} + +static void __exit usbeth_cleanup(void) +{ + usbctl_stop(&usbe_info.client); + usbctl_close(&usbe_info.client); + + unregister_netdev(&usbe_info.dev); +#ifndef RX_NO_COPY + kfree(usbe_info.dmabuf); +#endif +} + +module_init(usbeth_init); +module_exit(usbeth_cleanup); + +MODULE_DESCRIPTION("USB client ethernet driver"); +MODULE_PARM(usb_rsize, "1i"); +MODULE_PARM_DESC(usb_rsize, "number of bytes in packets from host to sa11x0"); +MODULE_PARM(usb_wsize, "1i"); +MODULE_PARM_DESC(usb_wsize, "number of bytes in packets from sa11x0 to host"); +MODULE_LICENSE("GPL"); diff -urN orig/arch/arm/mach-sa1100/usb/usb_ctl.c linux/arch/arm/mach-sa1100/usb/usb_ctl.c --- orig/arch/arm/mach-sa1100/usb/usb_ctl.c Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/mach-sa1100/usb/usb_ctl.c Wed Aug 21 16:28:57 2002 @@ -0,0 +1,171 @@ + /* + * Copyright (C) Compaq Computer Corporation, 1998, 1999 + * Copyright (C) Extenex Corporation, 2001 + * + * usb_ctl.c + * + * SA1100 USB controller core driver. + * + * This file provides interrupt routing and overall coordination + * of the three endpoints in usb_ep0, usb_receive (1), and usb_send (2). + * + * Please see linux/Documentation/arm/SA1100/SA1100_USB for details. + * + */ +#include +#include +#include + +#include "buffer.h" +#include "client.h" +#include "usbdev.h" +#include "sa1100_usb.h" + +////////////////////////////////////////////////////////////////////////////// +// Globals +////////////////////////////////////////////////////////////////////////////// + +/* device descriptors */ +static struct cdb cdb; + +////////////////////////////////////////////////////////////////////////////// +// Private Helpers +////////////////////////////////////////////////////////////////////////////// + +int sa1100_usb_add_string(struct usbctl *ctl, const char *str) +{ + int nr = 0; + + if (str) { + struct usb_buf *buf; + int len; + + len = strlen(str); + + nr = -ENOMEM; + buf = usbc_string_alloc(len); + if (buf) { + usbc_string_from_cstr(buf, str); + nr = usbc_string_add(&ctl->strings, buf); + + if (nr < 0) + usbc_string_free(buf); + } + } + + return nr; +} + +EXPORT_SYMBOL(sa1100_usb_add_string); + +static int sa1100_usb_add_language(struct usbctl *ctl, unsigned int lang) +{ + struct usb_buf *buf; + int nr = -ENOMEM; + + buf = usbc_string_alloc(1); + if (buf) { + usbc_string_desc(buf)->wData[0] = cpu_to_le16(lang); /* American English */ + nr = usbc_string_add(&ctl->strings, buf); + + if (nr < 0) + usbc_string_free(buf); + } + + return nr; +} + +/* setup default descriptors */ + +void initialize_descriptors(struct usbctl *ctl) +{ + struct usb_client *clnt = ctl->clnt; + int r; + + ctl->ep_desc[0] = (struct usb_endpoint_descriptor *)&cdb.ep1; + ctl->ep_desc[1] = (struct usb_endpoint_descriptor *)&cdb.ep2; + + cdb.cfg.bLength = sizeof(config_desc_t); + cdb.cfg.bDescriptorType = USB_DT_CONFIG; + cdb.cfg.wTotalLength = cpu_to_le16(sizeof(struct cdb)); + cdb.cfg.bNumInterfaces = 1; + cdb.cfg.bConfigurationValue = 1; + cdb.cfg.iConfiguration = 0; + cdb.cfg.bmAttributes = USB_CONFIG_BUSPOWERED; + cdb.cfg.MaxPower = USB_POWER( 500 ); + + cdb.intf.bLength = sizeof(intf_desc_t); + cdb.intf.bDescriptorType = USB_DT_INTERFACE; + cdb.intf.bInterfaceNumber = 0; /* unique intf index*/ + cdb.intf.bAlternateSetting = 0; + cdb.intf.bNumEndpoints = 2; + cdb.intf.bInterfaceClass = 0xff; /* vendor specific */ + cdb.intf.bInterfaceSubClass = 0; + cdb.intf.bInterfaceProtocol = 0; + cdb.intf.iInterface = 0; + + cdb.ep1.bLength = sizeof(ep_desc_t); + cdb.ep1.bDescriptorType = USB_DT_ENDPOINT; + cdb.ep1.bEndpointAddress = USB_DIR_OUT | 1; + cdb.ep1.bmAttributes = USB_ENDPOINT_XFER_BULK; + cdb.ep1.wMaxPacketSize = cpu_to_le16(64); + cdb.ep1.bInterval = 0; + + cdb.ep2.bLength = sizeof(ep_desc_t); + cdb.ep2.bDescriptorType = USB_DT_ENDPOINT; + cdb.ep2.bEndpointAddress = USB_DIR_IN | 2; + cdb.ep2.bmAttributes = USB_ENDPOINT_XFER_BULK; + cdb.ep2.wMaxPacketSize = cpu_to_le16(64); + cdb.ep2.bInterval = 0; + + ctl->dev_desc->bLength = sizeof(struct usb_device_descriptor); + ctl->dev_desc->bDescriptorType = USB_DT_DEVICE; + ctl->dev_desc->bcdUSB = cpu_to_le16(0x100); /* 1.0 */ + ctl->dev_desc->bDeviceClass = clnt->class; + ctl->dev_desc->bDeviceSubClass = clnt->subclass; + ctl->dev_desc->bDeviceProtocol = clnt->protocol; + ctl->dev_desc->bMaxPacketSize0 = 8; /* ep0 max fifo size */ + ctl->dev_desc->idVendor = cpu_to_le16(clnt->vendor); + ctl->dev_desc->idProduct = cpu_to_le16(clnt->product); + ctl->dev_desc->bcdDevice = cpu_to_le16(clnt->version); + ctl->dev_desc->bNumConfigurations = 1; + + /* set language */ + /* See: http://www.usb.org/developers/data/USB_LANGIDs.pdf */ + r = sa1100_usb_add_language(ctl, 0x409); + if (r < 0) + printk(KERN_ERR "usbc: couldn't add language\n"); + + r = sa1100_usb_add_string(ctl, clnt->manufacturer_str); + if (r < 0) + printk(KERN_ERR "usbc: couldn't add manufacturer string\n"); + + ctl->dev_desc->iManufacturer = r > 0 ? r : 0; + + r = sa1100_usb_add_string(ctl, clnt->product_str); + if (r < 0) + printk(KERN_ERR "usbc: couldn't add product string\n"); + + ctl->dev_desc->iProduct = r > 0 ? r : 0; + + r = sa1100_usb_add_string(ctl, clnt->serial_str); + if (r < 0) + printk(KERN_ERR "usbc: couldn't add serial string\n"); + + ctl->dev_desc->iSerialNumber = r > 0 ? r : 0; +} + + +/*==================================================== + * Descriptor Manipulation. + * Use these between open() and start() above to setup + * the descriptors for your device. + */ + +/* get pointer to static default descriptor */ +struct cdb *sa1100_usb_get_descriptor_ptr(void) +{ + return &cdb; +} + +EXPORT_SYMBOL(sa1100_usb_get_descriptor_ptr); diff -urN orig/arch/arm/mach-sa1100/usb/usb_ctl.h linux/arch/arm/mach-sa1100/usb/usb_ctl.h --- orig/arch/arm/mach-sa1100/usb/usb_ctl.h Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/mach-sa1100/usb/usb_ctl.h Wed Aug 21 16:28:57 2002 @@ -0,0 +1,114 @@ +/* + * Copyright (C) Compaq Computer Corporation, 1998, 1999 + * Copyright (C) Extenex Corporation 2001 + * + * usb_ctl.h + * + * PRIVATE interface used to share info among components of the SA-1100 USB + * core: usb_ctl, usb_ep0, usb_recv and usb_send. Clients of the USB core + * should use sa1100_usb.h. + * + */ + +#ifndef _USB_CTL_H +#define _USB_CTL_H + +#include /* dmach_t */ + +struct usb_client; + +struct usb_stats_t { + unsigned long ep0_fifo_write_failures; + unsigned long ep0_bytes_written; + unsigned long ep0_fifo_read_failures; + unsigned long ep0_bytes_read; +}; + +struct usb_info_t +{ + struct usb_client *client; + dma_regs_t *dmach_tx, *dmach_rx; + int state; + unsigned char address; + struct usb_stats_t stats; +}; + +/* in usb_ctl.c */ +extern struct usb_info_t usbd_info; + +/* + * Function Prototypes + */ +enum { kError=-1, kEvSuspend=0, kEvReset=1, + kEvResume=2, kEvAddress=3, kEvConfig=4, kEvDeConfig=5 }; +int usbctl_next_state_on_event( int event ); + +/* endpoint zero */ +void ep0_reset(void); +void ep0_int_hndlr(void); + +/* receiver */ +int ep1_recv(void); +int ep1_init(dma_regs_t *dma); +void ep1_int_hndlr(int status); +void ep1_reset(void); +void ep1_stall(void); + +/* xmitter */ +void ep2_reset(void); +int ep2_init(dma_regs_t *dma); +void ep2_int_hndlr(int status); +void ep2_stall(void); + +#define UDC_write(reg, val) { \ + int i = 10000; \ + do { \ + (reg) = (val); \ + if (i-- <= 0) { \ + printk( "%s [%d]: write %#x to %p (%#x) failed\n", \ + __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ + break; \ + } \ + } while((reg) != (val)); \ +} + +#define UDC_set(reg, val) { \ + int i = 10000; \ + do { \ + (reg) |= (val); \ + if (i-- <= 0) { \ + printk( "%s [%d]: set %#x of %p (%#x) failed\n", \ + __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ + break; \ + } \ + } while(!((reg) & (val))); \ +} + +#define UDC_clear(reg, val) { \ + int i = 10000; \ + do { \ + (reg) &= ~(val); \ + if (i-- <= 0) { \ + printk( "%s [%d]: clear %#x of %p (%#x) failed\n", \ + __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ + break; \ + } \ + } while((reg) & (val)); \ +} + +#define UDC_flip(reg, val) { \ + int i = 10000; \ + (reg) = (val); \ + do { \ + (reg) = (val); \ + if (i-- <= 0) { \ + printk( "%s [%d]: flip %#x of %p (%#x) failed\n", \ + __FUNCTION__, __LINE__, (val), &(reg), (reg)); \ + break; \ + } \ + } while(((reg) & (val))); \ +} + + +#define CHECK_ADDRESS { if ( Ser0UDCAR == 1 ) { printk("%s:%d I lost my address!!!\n",__FUNCTION__, __LINE__);}} +#endif /* _USB_CTL_H */ diff -urN orig/arch/arm/mach-sa1100/usb/usb_recv.c linux/arch/arm/mach-sa1100/usb/usb_recv.c --- orig/arch/arm/mach-sa1100/usb/usb_recv.c Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/mach-sa1100/usb/usb_recv.c Tue May 20 00:10:56 2003 @@ -0,0 +1,318 @@ +/* + * Generic receive layer for the SA1100 USB client function + * Copyright (c) 2001 by Nicolas Pitre + * + * This code was loosely inspired by the original version which was + * Copyright (c) Compaq Computer Corporation, 1998-1999 + * + * 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 still work in progress... + * + * Please see linux/Documentation/arm/SA1100/SA1100_USB for details. + */ + +#include +#include +#include +#include + +#include +#include + +#include "sa1100_usb.h" +#include "sa1100usb.h" + +static int naking; + +#if 1 +static void dump_buf(struct sausb_dev *usb, const char *prefix) +{ + printk("%s: buf [dma=%08x len=%3d] pkt [cpu=%08x dma=%08x len=%3d rem=%3d]\n", + prefix, + usb->ep[0].bufdma, + usb->ep[0].buflen, + (unsigned int)usb->ep[0].pktcpu, + usb->ep[0].pktdma, + usb->ep[0].pktlen, + usb->ep[0].pktrem); +} +#endif + +static void udc_ep1_done(struct sausb_dev *usb, int flag, int size) +{ +// printk("UDC: rxd: %3d %3d\n", flag, size); + dump_buf(usb, "UDC: rxd"); + + if (!usb->ep[0].buflen) + return; + + dma_unmap_single(usb->dev, usb->ep[0].bufdma, usb->ep[0].buflen, + DMA_FROM_DEVICE); + + usb->ep[0].bufdma = 0; + usb->ep[0].buflen = 0; + usb->ep[0].pktcpu = NULL; + usb->ep[0].pktdma = 0; + usb->ep[0].pktlen = 0; + usb->ep[0].pktrem = 0; + + if (usb->ep[0].cb_func) + usb->ep[0].cb_func(usb->ep[0].cb_data, flag, size); +} + +/* + * Initialisation. Clear out the status, and set FST. + */ +void udc_ep1_init(struct sausb_dev *usb) +{ + sa1100_reset_dma(usb->ep[0].dmach); + + UDC_clear(Ser0UDCCS1, UDCCS1_FST | UDCCS1_RPE | UDCCS1_RPC); + + BUG_ON(usb->ep[0].buflen); + BUG_ON(usb->ep[0].pktlen); +} + +void udc_ep1_halt(struct sausb_dev *usb, int halt) +{ + if (halt) { + /* force stall at UDC */ + UDC_set(Ser0UDCCS1, UDCCS1_FST); + } else { + sa1100_reset_dma(usb->ep[0].dmach); + + UDC_clear(Ser0UDCCS1, UDCCS1_FST); + + udc_ep1_done(usb, -EINTR, 0); + } +} + +/* + * This gets called when we receive a SET_CONFIGURATION packet to EP0. + * We were configured. We can now accept packets from the host. + */ +void udc_ep1_config(struct sausb_dev *usb, unsigned int maxpktsize) +{ + usb->ep[0].maxpktsize = maxpktsize; + usb->ep[0].configured = 1; + + Ser0UDCOMP = maxpktsize - 1; + + sa1100_reset_dma(usb->ep[0].dmach); + udc_ep1_done(usb, -EINTR, 0); + + /* + * Enable EP1 interrupts. + */ + usb->udccr &= ~UDCCR_RIM; + UDC_write(Ser0UDCCR, usb->udccr); +} + +/* + * We saw a reset from the attached hub. This means we are no + * longer configured, and as far as the rest of the world is + * concerned, we don't exist. + */ +void udc_ep1_reset(struct sausb_dev *usb) +{ + /* + * Disable EP1 interrupts. + */ + usb->udccr |= UDCCR_RIM; + UDC_write(Ser0UDCCR, usb->udccr); + + usb->ep[0].configured = 0; + usb->ep[0].maxpktsize = 0; + + sa1100_reset_dma(usb->ep[0].dmach); + udc_ep1_done(usb, -EINTR, 0); +} + +void udc_ep1_int_hndlr(struct sausb_dev *usb) +{ + dma_addr_t dma_addr; + unsigned int len; + u32 status = Ser0UDCCS1; + + dump_buf(usb, "UDC: int"); + + if (naking) { + printk("UDC: usbrx: in ISR but naking [0x%02x]\n", status); + return; + } + + if (!(status & UDCCS1_RPC)) + /* you can get here if we are holding NAK */ + return; + + if (!usb->ep[0].buflen) { + printk("UDC: usb_recv: RPC for non-existent buffer [0x%02x]\n", status); + naking = 1; + return; + } + + sa1100_stop_dma(usb->ep[0].dmach); + + dma_addr = sa1100_get_dma_pos(usb->ep[0].dmach); + + /* + * We've finished with the DMA for this packet. + */ + sa1100_clear_dma(usb->ep[0].dmach); + + if (status & UDCCS1_SST) { + printk("UDC: usb_recv: stall sent\n"); + UDC_flip(Ser0UDCCS1, UDCCS1_SST); + + /* + * UDC aborted current transfer, so we do. + * + * It would be better to re-queue this buffer IMHO. It + * hasn't gone anywhere yet. --rmk + */ + UDC_flip(Ser0UDCCS1, UDCCS1_RPC); + udc_ep1_done(usb, -EIO, 0); + return; + } + + if (status & UDCCS1_RPE) { + printk("UDC: usb_recv: RPError %x\n", status); + UDC_flip(Ser0UDCCS1, UDCCS1_RPC); + udc_ep1_done(usb, -EIO, 0); + return; + } + + len = dma_addr - usb->ep[0].pktdma; + if (len < 0) { + printk("UDC: usb_recv: dma_addr (%x) < pktdma (%x)\n", + dma_addr, usb->ep[0].pktdma); + len = 0; + } + + if (len > usb->ep[0].pktlen) + len = usb->ep[0].pktlen; + + /* + * If our transfer was smaller, and we have bytes left in + * the FIFO, we need to read them out manually. + */ + if (len < usb->ep[0].pktlen && (Ser0UDCCS1 & UDCCS1_RNE)) { + char *buf; + + dma_sync_single(usb->dev, usb->ep[0].pktdma + len, + usb->ep[0].pktlen - len, DMA_FROM_DEVICE); + + buf = (char *)usb->ep[0].pktcpu + len; + + do { + *buf++ = Ser0UDCDR; + len++; + } while (len < usb->ep[0].pktlen && (Ser0UDCCS1 & UDCCS1_RNE)); + + /* + * Note: knowing the internals of this macro is BAD, but we + * need this to cause the data to be written back to memory. + */ + dma_sync_single(usb->dev, usb->ep[0].pktdma + len, + usb->ep[0].pktlen - len, DMA_TO_DEVICE); + } + + /* + * If the FIFO still contains data, something's definitely wrong. + */ + if (Ser0UDCCS1 & UDCCS1_RNE) { + printk("UDC: usb_recv: fifo screwed, shouldn't contain data\n"); + usb->ep[0].fifo_errs++; + naking = 1; + udc_ep1_done(usb, -EPIPE, 0); + return; + } + + /* + * Do statistics. + */ + if (len) { + usb->ep[0].bytes += len; + usb->ep[0].packets ++; + } + + /* + * Update remaining byte count for this buffer. + */ + usb->ep[0].pktrem -= len; + + /* + * If we received a full-sized packet, and there's more + * data remaining, th, queue up another receive. + */ + if (len == usb->ep[0].pktlen && usb->ep[0].pktrem != 0) { + usb->ep[0].pktcpu += len; + usb->ep[0].pktdma += len; + usb->ep[0].pktlen = min(usb->ep[0].pktrem, usb->ep[0].maxpktsize); + sa1100_start_dma(usb->ep[0].dmach, usb->ep[0].pktdma, usb->ep[0].pktlen); + /* + * Clear RPC to receive next packet. + */ + UDC_flip(Ser0UDCCS1, UDCCS1_RPC); + dump_buf(usb, "UDC: req"); + return; + } + + naking = 1; + udc_ep1_done(usb, 0, usb->ep[0].buflen - usb->ep[0].pktrem); +} + +int udc_ep1_queue_buffer(struct sausb_dev *usb, char *buf, unsigned int len) +{ + unsigned long flags; + dma_addr_t dma; + int ret; + + if (!buf || len == 0) + return -EINVAL; + + dma = dma_map_single(usb->dev, buf, len, DMA_FROM_DEVICE); + + spin_lock_irqsave(&usb->lock, flags); + do { + if (usb->ep[0].buflen) { + ret = -EBUSY; + break; + } + + sa1100_clear_dma(usb->ep[0].dmach); + + usb->ep[0].bufdma = dma; + usb->ep[0].buflen = len; + usb->ep[0].pktcpu = buf; + usb->ep[0].pktdma = dma; + usb->ep[0].pktlen = min(len, usb->ep[0].maxpktsize); + usb->ep[0].pktrem = len; + + sa1100_start_dma(usb->ep[0].dmach, usb->ep[0].bufdma, usb->ep[0].buflen); + dump_buf(usb, "UDC: que"); + + if (naking) { + /* turn off NAK of OUT packets, if set */ + UDC_flip(Ser0UDCCS1, UDCCS1_RPC); + naking = 0; + } + + ret = 0; + } while (0); + spin_unlock_irqrestore(&usb->lock, flags); + + if (ret) + dma_unmap_single(usb->dev, dma, len, DMA_FROM_DEVICE); + + return 0; +} + +void udc_ep1_recv_reset(struct sausb_dev *usb) +{ + sa1100_reset_dma(usb->ep[0].dmach); + udc_ep1_done(usb, -EINTR, 0); +} diff -urN orig/arch/arm/mach-sa1100/usb/usb_send.c linux/arch/arm/mach-sa1100/usb/usb_send.c --- orig/arch/arm/mach-sa1100/usb/usb_send.c Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/mach-sa1100/usb/usb_send.c Tue May 20 00:10:37 2003 @@ -0,0 +1,302 @@ +/* + * Generic xmit layer for the SA1100 USB client function + * Copyright (c) 2001 by Nicolas Pitre + * + * This code was loosely inspired by the original version which was + * Copyright (c) Compaq Computer Corporation, 1998-1999 + * + * 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 still work in progress... + * + * Please see linux/Documentation/arm/SA1100/SA1100_USB for details. + * 15/03/2001 - ep2_start now sets UDCAR to overcome something that is hardware + * bug, I think. green@iXcelerator.com + */ + +#include +#include +#include +#include // for the massive_attack hack 28Feb01ww +#include + +#include + +#include "usbdev.h" +#include "sa1100_usb.h" +#include "sa1100usb.h" + +static unsigned int ep2_curdmalen; +static unsigned int ep2_remain; + +static struct sausb_dev *ep2_dev; + +static void udc_set_cs2(u32 val, u32 mask, u32 check) +{ + int i = 0; + + do { + Ser0UDCCS2 = val; + udelay(1); + if ((Ser0UDCCS2 & mask) == check) + return; + } while (i++ < 10000); + + printk("UDC: UDCCS2 write timed out: val=0x%08x\n", val); +} + +/* set feature stall executing, async */ +static void ep2_start(struct sausb_dev *usb) +{ + ep2_curdmalen = min(ep2_remain, usb->ep[1].maxpktsize); + if (ep2_curdmalen == 0) + return; + + /* + * must do this _before_ queue buffer.. + * stop NAKing IN tokens + */ + udc_set_cs2(usb->ep[1].udccs | UDCCS2_TPC, UDCCS2_TPC, 0); + + UDC_write(Ser0UDCIMP, ep2_curdmalen - 1); + + /* Remove if never seen...8Mar01ww */ + { + int massive_attack = 20; + while (Ser0UDCIMP != ep2_curdmalen - 1 && massive_attack--) { + printk("usbsnd: Oh no you don't! Let me spin..."); + udelay(500); + printk("and try again...\n"); + UDC_write(Ser0UDCIMP, ep2_curdmalen - 1); + } + if (massive_attack != 20) { + if (Ser0UDCIMP != ep2_curdmalen - 1) + printk("usbsnd: Massive attack FAILED. %d\n", + 20 - massive_attack); + else + printk("usbsnd: Massive attack WORKED. %d\n", + 20 - massive_attack); + } + } + /* End remove if never seen... 8Mar01ww */ + + /* + * fight stupid silicon bug + */ + Ser0UDCAR = usb->ctl->address; + + sa1100_start_dma(usb->ep[1].dmach, usb->ep[1].pktdma, ep2_curdmalen); +} + +static void udc_ep2_done(struct sausb_dev *usb, int flag) +{ + int size = usb->ep[1].buflen - ep2_remain; + + if (!usb->ep[1].buflen) + return; + + dma_unmap_single(usb->dev, usb->ep[1].bufdma, usb->ep[1].buflen, + DMA_TO_DEVICE); + + usb->ep[1].bufdma = 0; + usb->ep[1].buflen = 0; + usb->ep[1].pktdma = 0; + + if (usb->ep[1].cb_func) + usb->ep[1].cb_func(usb->ep[1].cb_data, flag, size); +} + +/* + * Initialisation. Clear out the status. + */ +void udc_ep2_init(struct sausb_dev *usb) +{ + ep2_dev = usb; + + usb->ep[1].udccs = UDCCS2_FST; + + BUG_ON(usb->ep[1].buflen); + BUG_ON(usb->ep[1].pktlen); + + sa1100_reset_dma(usb->ep[1].dmach); +} + +/* + * Note: rev A0-B2 chips don't like FST + */ +void udc_ep2_halt(struct sausb_dev *usb, int halt) +{ + usb->ep[1].host_halt = halt; + + if (halt) { + usb->ep[1].udccs |= UDCCS2_FST; + udc_set_cs2(UDCCS2_FST, UDCCS2_FST, UDCCS2_FST); + } else { + sa1100_clear_dma(usb->ep[1].dmach); + + udc_set_cs2(UDCCS2_FST, UDCCS2_FST, UDCCS2_FST); + udc_set_cs2(0, UDCCS2_FST, 0); + udc_set_cs2(UDCCS2_SST, UDCCS2_SST, 0); + + usb->ep[1].udccs &= ~UDCCS2_FST; + + udc_ep2_done(usb, -EINTR); + } +} + +/* + * This gets called when we receive a SET_CONFIGURATION packet to EP0. + * We were configured. We can now send packets to the host. + */ +void udc_ep2_config(struct sausb_dev *usb, unsigned int maxpktsize) +{ + /* + * We shouldn't be transmitting anything... + */ + BUG_ON(usb->ep[1].buflen); + BUG_ON(usb->ep[1].pktlen); + + /* + * Set our configuration. + */ + usb->ep[1].maxpktsize = maxpktsize; + usb->ep[1].configured = 1; + + /* + * Clear any pending TPC status. + */ + udc_set_cs2(UDCCS2_TPC, UDCCS2_TPC, 0); + + /* + * Enable EP2 interrupts. + */ + usb->udccr &= ~UDCCR_TIM; + UDC_write(Ser0UDCCR, usb->udccr); + + usb->ep[1].udccs = 0; +} + +/* + * We saw a reset from the attached hub, or were deconfigured. + * This means we are no longer configured. + */ +void udc_ep2_reset(struct sausb_dev *usb) +{ + /* + * Disable EP2 interrupts. + */ + usb->udccr |= UDCCR_TIM; + UDC_write(Ser0UDCCR, usb->udccr); + + usb->ep[1].configured = 0; + usb->ep[1].maxpktsize = 0; + + sa1100_reset_dma(usb->ep[1].dmach); + udc_ep2_done(usb, -EINTR); +} + +void udc_ep2_int_hndlr(struct sausb_dev *usb) +{ + u32 status = Ser0UDCCS2; + + // check for stupid silicon bug. + if (Ser0UDCAR != usb->ctl->address) + Ser0UDCAR = usb->ctl->address; + + udc_set_cs2(usb->ep[1].udccs | UDCCS2_SST, UDCCS2_SST, 0); + + if (!(status & UDCCS2_TPC)) { + printk("usb_send: Not TPC: UDCCS2 = %x\n", status); + return; + } + + sa1100_stop_dma(usb->ep[1].dmach); + + if (status & (UDCCS2_TPE | UDCCS2_TUR)) { + printk("usb_send: transmit error %x\n", status); + usb->ep[1].fifo_errs ++; + udc_ep2_done(usb, -EIO); + } else { + unsigned int imp; +#if 1 // 22Feb01ww/Oleg + imp = ep2_curdmalen; +#else + // this is workaround for case when setting + // of Ser0UDCIMP was failed + imp = Ser0UDCIMP + 1; +#endif + usb->ep[1].pktdma += imp; + ep2_remain -= imp; + + usb->ep[1].bytes += imp; + usb->ep[1].packets++; + + sa1100_clear_dma(usb->ep[1].dmach); + + if (ep2_remain != 0) { + ep2_start(usb); + } else { + udc_ep2_done(usb, 0); + } + } +} + +int udc_ep2_send(struct sausb_dev *usb, char *buf, int len) +{ + unsigned long flags; + dma_addr_t dma; + int ret; + + if (!buf || len == 0) + return -EINVAL; + + dma = dma_map_single(usb->dev, buf, len, DMA_TO_DEVICE); + + spin_lock_irqsave(&usb->lock, flags); + do { + if (!usb->ep[1].configured) { + ret = -ENODEV; + break; + } + + if (usb->ep[1].buflen) { + ret = -EBUSY; + break; + } + + usb->ep[1].bufdma = dma; + usb->ep[1].buflen = len; + usb->ep[1].pktdma = dma; + ep2_remain = len; + + sa1100_clear_dma(usb->ep[1].dmach); + + ep2_start(usb); + ret = 0; + } while (0); + spin_unlock_irqrestore(&usb->lock, flags); + + if (ret) + dma_unmap_single(usb->dev, dma, len, DMA_TO_DEVICE); + + return ret; +} + +void udc_ep2_send_reset(struct sausb_dev *usb) +{ + sa1100_reset_dma(usb->ep[1].dmach); + udc_ep2_done(usb, -EINTR); +} + +int udc_ep2_idle(struct sausb_dev *usb) +{ + if (!usb->ep[1].configured) + return -ENODEV; + + if (usb->ep[1].buflen) + return -EBUSY; + + return 0; +} diff -urN orig/arch/arm/mach-sa1100/usb/usbdev.h linux/arch/arm/mach-sa1100/usb/usbdev.h --- orig/arch/arm/mach-sa1100/usb/usbdev.h Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/mach-sa1100/usb/usbdev.h Wed Aug 21 17:46:53 2002 @@ -0,0 +1,91 @@ +#ifndef USBDEV_H +#define USBDEV_H + +#include "strings.h" + +struct usb_buf; +struct module; +struct cdb; +struct usb_client; + +struct usbc_driver { + struct module *owner; + const char *name; + void *priv; + int (*start)(void *); + int (*stop)(void *); + + int (*ep0_queue)(void *, struct usb_buf *buf, unsigned int req_len); + void (*set_address)(void *, unsigned int addr); + void (*set_config)(void *, struct cdb *config); + + /* + * Get specified endpoint status, as defined in 9.4.5. + */ + unsigned int (*ep_get_status)(void *, unsigned int ep); + void (*ep_halt)(void *, unsigned int ep, int halt); + + /* + * Client + */ + int (*ep_queue)(void *, unsigned int, char *, unsigned int); + void (*ep_reset)(void *, unsigned int); + void (*ep_callback)(void *, unsigned int, void (*)(void *, int, int), void *); + int (*ep_idle)(void *, unsigned int); +}; + +struct usbc_endpoint { + struct usb_endpoint_descriptor *desc; +}; + +struct usbc_interface { + struct usb_interface_descriptor *desc; + unsigned int nr_ep; + struct usbc_endpoint *ep[0]; +}; + +struct usbc_config { + struct usb_config_descriptor *desc; + unsigned int nr_interface; + struct usbc_interface *interface[0]; +}; + +struct usbctl { + struct usb_client *clnt; + const struct usbc_driver *driver; + + /* Internal state */ + unsigned int address; /* host assigned address */ + unsigned int state; /* our device state */ + unsigned int sm_state; /* state machine state */ + + struct usbc_config *config; /* active configuration */ + struct usbc_strs strings; + + /* Descriptors */ + struct usb_device_descriptor *dev_desc; /* device descriptor */ + struct usb_buf *dev_desc_buf; /* device descriptor buffer */ + + + int nr_ep; + struct usb_endpoint_descriptor *ep_desc[2]; +}; + +/* + * Function Prototypes + */ + +#define RET_ERROR (-1) +#define RET_NOACTION (0) +#define RET_QUEUED (1) +#define RET_ACK (2) +#define RET_REQERROR (3) + +int usbctl_parse_request(struct usbctl *ctl, struct usb_ctrlrequest *req); + +int usbctl_reset(struct usbctl *ctl); +void usbctl_suspend(struct usbctl *ctl); +void usbctl_resume(struct usbctl *ctl); + +#endif + diff -urN orig/arch/arm/mach-sa1100/xp860.c linux/arch/arm/mach-sa1100/xp860.c --- orig/arch/arm/mach-sa1100/xp860.c Fri Feb 21 19:48:36 2003 +++ linux/arch/arm/mach-sa1100/xp860.c Wed Jun 4 18:34:06 2003 @@ -1,6 +1,7 @@ /* * linux/arch/arm/mach-sa1100/xp860.c */ +#error Add "mem=32M" to the default command line #include #include @@ -30,6 +31,31 @@ while(1); } +static struct resource sa1111_resources[] = { + [0] = { + .start = 0x40000000, + .end = 0x40001fff, + .flags = IORESOURCE_MEM, + }, +}; + +static u64 sa1111_dmamask = 0xffffffffUL; + +static struct platform_device sa1111_device = { + .name = "sa1111", + .id = 0, + .dev = { + .name = "Intel Corporation SA1111", + .dma_mask = &sa1111_dmamask, + }, + .num_resources = ARRAY_SIZE(sa1111_resources), + .resource = sa1111_resources, +}; + +static struct platform_device *devices[] __initdata = { + &sa1111_device, +}; + /* * Note: I replaced the sa1111_init() without the full SA1111 initialisation * because this machine doesn't appear to use the DMA features. If this is @@ -39,19 +65,7 @@ { pm_power_off = xp860_power_off; - /* - * Probe for SA1111. - */ - ret = sa1111_probe(0x40000000); - if (ret < 0) - return ret; - - /* - * We found it. Wake the chip up. - */ - sa1111_wake(); - - return 0; + return platform_add_devices(devices, ARRAY_SIZE(devices)); } arch_initcall(xp860_init); diff -urN orig/arch/arm/mm/Makefile linux/arch/arm/mm/Makefile --- orig/arch/arm/mm/Makefile Mon May 5 17:38:45 2003 +++ linux/arch/arm/mm/Makefile Mon May 5 17:39:27 2003 @@ -32,7 +32,7 @@ p-$(CONFIG_CPU_SA1100) += proc-sa1100.o tlb-v4wb.o cache-v4wb.o copypage-v4mc.o abort-ev4.o minicache.o # ARMv5 -p-$(CONFIG_CPU_ARM926T) += proc-arm926.o tlb-v4wbi.o copypage-v4wb.o abort-ev5tej.o -p-$(CONFIG_CPU_XSCALE) += proc-xscale.o tlb-v4wbi.o copypage-xscale.o abort-xscale.o minicache.o +p-$(CONFIG_CPU_ARM926T) += proc-arm926.o tlb-v4wbi.o copypage-v4wb.o abort-ev5tj.o +p-$(CONFIG_CPU_XSCALE) += proc-xscale.o tlb-v4wbi.o copypage-xscale.o abort-ev5t.o minicache.o obj-y += $(sort $(p-y)) diff -urN orig/arch/arm/mm/abort-ev5t.S linux/arch/arm/mm/abort-ev5t.S --- orig/arch/arm/mm/abort-ev5t.S Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/mm/abort-ev5t.S Sun Nov 10 12:11:23 2002 @@ -0,0 +1,31 @@ +#include +#include +/* + * Function: ev5t_early_abort + * + * Params : r2 = address of aborted instruction + * : r3 = saved SPSR + * + * Returns : r0 = address of abort + * : r1 = FSR, bit 11 = write + * : r2-r8 = corrupted + * : r9 = preserved + * : sp = pointer to registers + * + * Purpose : obtain information about current aborted instruction. + * Note: we read user space. This means we might cause a data + * abort here if the I-TLB and D-TLB aren't seeing the same + * picture. Unfortunately, this does happen. We live with it. + */ + .align 5 +ENTRY(ev5t_early_abort) + mrc p15, 0, r1, c5, c0, 0 @ get FSR + mrc p15, 0, r0, c6, c0, 0 @ get FAR + tst r3, #PSR_T_BIT + ldrneh r3, [r2] @ read aborted thumb instruction + ldreq r3, [r2] @ read aborted ARM instruction + bic r1, r1, #1 << 11 @ clear bits 11 of FSR + movne r3, r3, lsl #(21 - 12) @ move thumb bit 11 to ARM bit 20 + tst r3, #1 << 20 @ check write + orreq r1, r1, #1 << 11 + mov pc, lr diff -urN orig/arch/arm/mm/abort-ev5tej.S linux/arch/arm/mm/abort-ev5tej.S --- orig/arch/arm/mm/abort-ev5tej.S Wed Jul 17 15:10:33 2002 +++ linux/arch/arm/mm/abort-ev5tej.S Thu Jan 1 01:00:00 1970 @@ -1,36 +0,0 @@ -#include -#include -/* - * Function: v5tej_early_abort - * - * Params : r2 = address of aborted instruction - * : r3 = saved SPSR - * - * Returns : r0 = address of abort - * : r1 = FSR, bit 11 = write - * : r2-r8 = corrupted - * : r9 = preserved - * : sp = pointer to registers - * - * Purpose : obtain information about current aborted instruction. - * Note: we read user space. This means we might cause a data - * abort here if the I-TLB and D-TLB aren't seeing the same - * picture. Unfortunately, this does happen. We live with it. - */ - .align 5 -ENTRY(v5tej_early_abort) - mrc p15, 0, r1, c5, c0, 0 @ get FSR - mrc p15, 0, r0, c6, c0, 0 @ get FAR - bic r1, r1, #1 << 11 | 1 << 10 @ clear bits 11 and 10 of FSR - tst r3, #PSR_J_BIT - orrne r1, r1, #1 << 11 @ always assume write - bne 1f - tst r3, #PSR_T_BIT - ldrneh r3, [r2] @ read aborted thumb instruction - ldreq r3, [r2] @ read aborted ARM instruction - movne r3, r3, lsl #(21 - 12) @ move thumb bit 11 to ARM bit 20 - tst r3, #1 << 20 @ L = 1 -> write - orreq r1, r1, #1 << 11 @ yes. -1: mov pc, lr - - diff -urN orig/arch/arm/mm/abort-ev5tj.S linux/arch/arm/mm/abort-ev5tj.S --- orig/arch/arm/mm/abort-ev5tj.S Thu Jan 1 01:00:00 1970 +++ linux/arch/arm/mm/abort-ev5tj.S Sun Nov 10 12:09:59 2002 @@ -0,0 +1,36 @@ +#include +#include +/* + * Function: v5tj_early_abort + * + * Params : r2 = address of aborted instruction + * : r3 = saved SPSR + * + * Returns : r0 = address of abort + * : r1 = FSR, bit 11 = write + * : r2-r8 = corrupted + * : r9 = preserved + * : sp = pointer to registers + * + * Purpose : obtain information about current aborted instruction. + * Note: we read user space. This means we might cause a data + * abort here if the I-TLB and D-TLB aren't seeing the same + * picture. Unfortunately, this does happen. We live with it. + */ + .align 5 +ENTRY(v5tj_early_abort) + mrc p15, 0, r1, c5, c0, 0 @ get FSR + mrc p15, 0, r0, c6, c0, 0 @ get FAR + bic r1, r1, #1 << 11 | 1 << 10 @ clear bits 11 and 10 of FSR + tst r3, #PSR_J_BIT @ Java? + orrne r1, r1, #1 << 11 @ always assume write + movne pc, lr + tst r3, #PSR_T_BIT @ Thumb? + ldrneh r3, [r2] @ read aborted thumb instruction + ldreq r3, [r2] @ read aborted ARM instruction + movne r3, r3, lsl #(21 - 12) @ move thumb bit 11 to ARM bit 20 + tst r3, #1 << 20 @ L = 0 -> write + orreq r1, r1, #1 << 11 @ yes. + mov pc, lr + + diff -urN orig/arch/arm/mm/abort-xscale.S linux/arch/arm/mm/abort-xscale.S --- orig/arch/arm/mm/abort-xscale.S Wed Jul 17 15:10:33 2002 +++ linux/arch/arm/mm/abort-xscale.S Thu Jan 1 01:00:00 1970 @@ -1,34 +0,0 @@ -#include -#include -/* - * Function: xscale_abort - * - * Params : r2 = address of aborted instruction - * : r3 = saved SPSR - * - * Returns : r0 = address of abort - * : r1 = FSR, bit 11 = write - * : r2-r8 = corrupted - * : r9 = preserved - * : sp = pointer to registers - * - * Purpose : obtain information about current aborted instruction. - * Note: we read user space. This means we might cause a data - * abort here if the I-TLB and D-TLB aren't seeing the same - * picture. Unfortunately, this does happen. We live with it. - * - * Note: Xscale is contains non-standard architecture extensions. - * It requires its own early abort handler - */ - .align 5 -ENTRY(xscale_abort) - mrc p15, 0, r1, c5, c0, 0 @ get FSR - mrc p15, 0, r0, c6, c0, 0 @ get FAR - tst r3, #PSR_T_BIT - ldrneh r3, [r2] @ read aborted thumb instruction - ldreq r3, [r2] @ read aborted ARM instruction - bic r1, r1, #1 << 11 @ clear bits 11 of FSR - movne r3, r3, lsl #(21 - 12) @ move thumb bit 11 to ARM bit 20 - tst r3, #1 << 20 @ check write - orreq r1, r1, #1 << 11 - mov pc, lr diff -urN orig/arch/arm/mm/consistent.c linux/arch/arm/mm/consistent.c --- orig/arch/arm/mm/consistent.c Tue May 27 10:04:13 2003 +++ linux/arch/arm/mm/consistent.c Wed Jun 4 11:29:14 2003 @@ -10,7 +10,7 @@ * DMA uncached mapping support. */ #include -#include +#include #include #include #include @@ -145,6 +145,7 @@ struct vm_region *c; unsigned long order, flags; void *ret = NULL; + int res; if (!consistent_pte) { printk(KERN_ERR "consistent_alloc: not initialised\n"); @@ -177,14 +178,19 @@ if (!c) goto no_remap; - spin_lock_irqsave(&consistent_lock, flags); - vm_region_dump(&consistent_head, "before alloc"); - /* * Attempt to allocate a virtual address in the * consistent mapping region. */ - if (!vm_region_alloc(&consistent_head, c, size)) { + spin_lock_irqsave(&consistent_lock, flags); + vm_region_dump(&consistent_head, "before alloc"); + + res = vm_region_alloc(&consistent_head, c, size); + + vm_region_dump(&consistent_head, "after alloc"); + spin_unlock_irqrestore(&consistent_lock, flags); + + if (!res) { pte_t *pte = consistent_pte + CONSISTENT_OFFSET(c->vm_start); struct page *end = page + (1 << order); pgprot_t prot = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | @@ -218,9 +224,6 @@ ret = (void *)c->vm_start; } - vm_region_dump(&consistent_head, "after alloc"); - spin_unlock_irqrestore(&consistent_lock, flags); - no_remap: if (ret == NULL) { kfree(c); @@ -231,6 +234,22 @@ } /* + * Since we have the DMA mask available to us here, we could try to do + * a normal allocation, and only fall back to a "DMA" allocation if the + * resulting bus address does not satisfy the dma_mask requirements. + */ +void * +dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, int gfp) +{ + if (dev == NULL || *dev->dma_mask != 0xffffffff) + gfp |= GFP_DMA; + + return consistent_alloc(gfp, size, handle, 0); +} + +EXPORT_SYMBOL(dma_alloc_coherent); + +/* * free a page as defined by the above mapping. */ void consistent_free(void *vaddr, size_t size, dma_addr_t handle) diff -urN orig/arch/arm/mm/fault-armv.c linux/arch/arm/mm/fault-armv.c --- orig/arch/arm/mm/fault-armv.c Mon May 5 17:38:45 2003 +++ linux/arch/arm/mm/fault-armv.c Sat May 31 14:49:01 2003 @@ -213,7 +213,7 @@ if (off >= (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT) continue; - flush_cache_page(mpnt, off); + flush_cache_page(mpnt, mpnt->vm_start + (off << PAGE_SHIFT)); } } diff -urN orig/arch/arm/mm/init.c linux/arch/arm/mm/init.c --- orig/arch/arm/mm/init.c Mon May 5 17:38:45 2003 +++ linux/arch/arm/mm/init.c Tue May 27 11:37:59 2003 @@ -47,7 +47,7 @@ #define TABLE_SIZE ((TABLE_OFFSET + PTRS_PER_PTE) * sizeof(pte_t)) -struct mmu_gather mmu_gathers[NR_CPUS]; +DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; extern char _stext, _text, _etext, _end, __init_begin, __init_end; diff -urN orig/arch/arm/mm/ioremap.c linux/arch/arm/mm/ioremap.c --- orig/arch/arm/mm/ioremap.c Tue Nov 5 12:50:57 2002 +++ linux/arch/arm/mm/ioremap.c Sun Nov 3 22:12:18 2002 @@ -13,15 +13,18 @@ * virtual space. One should *only* use readl, writel, memcpy_toio and * so on with such remapped areas. * - * Because the ARM only has a 32-bit address space we can't address the - * whole of the (physical) PCI space at once. PCI huge-mode addressing - * allows us to circumvent this restriction by splitting PCI space into - * two 2GB chunks and mapping only one at a time into processor memory. - * We use MMU protection domains to trap any attempt to access the bank - * that is not currently mapped. (This isn't fully implemented yet.) + * ioremap support tweaked to allow support for large page mappings. We + * have several issues that needs to be resolved first however: + * + * 1. We need set_pte, or something like set_pte to understand large + * page mappings. + * + * 2. we need the unmap_* functions to likewise understand large page + * mappings. */ #include #include +#include #include #include @@ -29,31 +32,159 @@ #include #include -static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, - unsigned long phys_addr, pgprot_t pgprot) +extern rwlock_t vmlist_lock; +extern struct vm_struct *vmlist; + +static struct vm_struct * +get_io_vm_area(unsigned long size, unsigned long align, unsigned long flags) { + struct vm_struct **p, *tmp, *area; + unsigned long addr; + + area = (struct vm_struct *)kmalloc(sizeof(*area), GFP_KERNEL); + if (!area) + return NULL; + + align -= 1; + + size += PAGE_SIZE; + addr = VMALLOC_START; + write_lock(&vmlist_lock); + for (p = &vmlist; (tmp = *p); p = &tmp->next) { + if ((size + addr) < addr) + goto out; + if (size + addr <= (unsigned long) tmp->addr) + break; + addr = tmp->size + (unsigned long) tmp->addr; + if ((addr + align) < addr) + goto out; + addr = (addr + align) & ~align; + if (addr > VMALLOC_END - size) + goto out; + } + area->flags = flags; + area->addr = (void *)addr; + area->size = size; + area->next = *p; + *p = area; + write_unlock(&vmlist_lock); + return area; + +out: + write_unlock(&vmlist_lock); + kfree(area); + return NULL; +} + +static inline void unmap_area_pte(pmd_t *pmd, unsigned long address, unsigned long size) +{ + pte_t *ptep; unsigned long end; + if (pmd_none(*pmd)) + return; + if (pmd_bad(*pmd)) { + pmd_ERROR(*pmd); + pmd_clear(pmd); + return; + } + ptep = pte_offset_kernel(pmd, address); address &= ~PMD_MASK; end = address + size; if (end > PMD_SIZE) end = PMD_SIZE; - if (address >= end) - BUG(); do { - if (!pte_none(*pte)) { - printk("remap_area_pte: page already exists\n"); - BUG(); + pte_t pte; + pte = ptep_get_and_clear(ptep); + address += PAGE_SIZE; + ptep++; + if (pte_none(pte)) + continue; + if (pte_present(pte)) { + unsigned long pfn = pte_pfn(pte); + struct page *page; + + if (!pfn_valid(pfn)) + continue; + page = pfn_to_page(pfn); + if (!PageReserved(page)) + __free_page(page); + continue; } - set_pte(pte, pfn_pte(phys_addr >> PAGE_SHIFT, pgprot)); + printk(KERN_CRIT "Whee.. Swapped out page in kernel page table\n"); + } while (address < end); +} + +static inline void unmap_area_pmd(pgd_t *dir, unsigned long address, unsigned long size) +{ + pmd_t *pmd; + unsigned long end; + + if (pgd_none(*dir)) + return; + if (pgd_bad(*dir)) { + pgd_ERROR(*dir); + pgd_clear(dir); + return; + } + pmd = pmd_offset(dir, address); + address &= ~PGDIR_MASK; + end = address + size; + if (end > PGDIR_SIZE) + end = PGDIR_SIZE; + do { + unmap_area_pte(pmd, address, end - address); + address = (address + PMD_SIZE) & PMD_MASK; + pmd++; + } while (address < end); +} + +static void +unmap_area_pages(unsigned long address, unsigned long size) +{ + unsigned long end = address + size; + pgd_t *dir; + + dir = pgd_offset_k(address); + flush_cache_all(); + do { + unmap_area_pmd(dir, address, end - address); + address = (address + PGDIR_SIZE) & PGDIR_MASK; + dir++; + } while (address && (address < end)); + flush_tlb_all(); +} + +static inline void +remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, + unsigned long pfn, pgprot_t pgprot) +{ + unsigned long end; + + address &= ~PMD_MASK; + end = address + size; + if (end > PMD_SIZE) + end = PMD_SIZE; + BUG_ON(address >= end); + do { + if (!pte_none(*pte)) + goto bad; + + set_pte(pte, pfn_pte(pfn, pgprot)); address += PAGE_SIZE; - phys_addr += PAGE_SIZE; + pfn++; pte++; } while (address && (address < end)); + return; + + bad: + printk("remap_area_pte: page already exists\n"); + BUG(); } -static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, - unsigned long phys_addr, unsigned long flags) +static inline int +remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, + unsigned long pfn, unsigned long flags) { unsigned long end; pgprot_t pgprot; @@ -64,34 +195,33 @@ if (end > PGDIR_SIZE) end = PGDIR_SIZE; - phys_addr -= address; - if (address >= end) - BUG(); + pfn -= address >> PAGE_SHIFT; + BUG_ON(address >= end); pgprot = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | L_PTE_WRITE | flags); do { pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address); if (!pte) return -ENOMEM; - remap_area_pte(pte, address, end - address, address + phys_addr, pgprot); + remap_area_pte(pte, address, end - address, pfn + (address >> PAGE_SHIFT), pgprot); address = (address + PMD_SIZE) & PMD_MASK; pmd++; } while (address && (address < end)); return 0; } -static int remap_area_pages(unsigned long address, unsigned long phys_addr, - unsigned long size, unsigned long flags) +static int +remap_area_pages(unsigned long address, unsigned long pfn, + unsigned long size, unsigned long flags) { int error; pgd_t * dir; unsigned long end = address + size; - phys_addr -= address; + pfn -= address >> PAGE_SHIFT; dir = pgd_offset(&init_mm, address); flush_cache_all(); - if (address >= end) - BUG(); + BUG_ON(address >= end); spin_lock(&init_mm.page_table_lock); do { pmd_t *pmd; @@ -100,7 +230,7 @@ if (!pmd) break; if (remap_area_pmd(pmd, address, end - address, - phys_addr + address, flags)) + pfn + (address >> PAGE_SHIFT), flags)) break; error = 0; address = (address + PGDIR_SIZE) & PGDIR_MASK; @@ -146,11 +276,11 @@ /* * Ok, go for it.. */ - area = get_vm_area(size, VM_IOREMAP); + area = get_io_vm_area(size, align, VM_IOREMAP); if (!area) return NULL; addr = area->addr; - if (remap_area_pages(VMALLOC_VMADDR(addr), phys_addr, size, flags)) { + if (remap_area_pages(VMALLOC_VMADDR(addr), phys_addr >> PAGE_SHIFT, size, flags)) { vfree(addr); return NULL; } @@ -159,5 +289,26 @@ void __iounmap(void *addr) { - vfree((void *) (PAGE_MASK & (unsigned long) addr)); + struct vm_struct **p, *tmp; + + if (!addr) + return; + + if ((PAGE_SIZE - 1) & (unsigned long)addr) { + printk(KERN_ERR "Trying to iounmap() bad address (%p)\n", addr); + return; + } + + write_lock(&vmlist_lock); + for (p = &vmlist; (tmp = *p); p = &tmp->next) { + if (tmp->addr == addr) { + *p = tmp->next; + unmap_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size); + write_unlock(&vmlist_lock); + kfree(tmp); + return; + } + } + write_unlock(&vmlist_lock); + printk(KERN_ERR "Trying to iounmap nonexistent area (%p)\n", addr); } diff -urN orig/arch/arm/mm/mm-armv.c linux/arch/arm/mm/mm-armv.c --- orig/arch/arm/mm/mm-armv.c Mon May 5 17:38:45 2003 +++ linux/arch/arm/mm/mm-armv.c Wed May 28 10:02:13 2003 @@ -309,9 +309,9 @@ const char *policy; /* - * ARMv5 can use ECC memory. + * ARMv5 and higher can use ECC memory. */ - if (cpu_arch == CPU_ARCH_ARMv5) { + if (cpu_arch >= CPU_ARCH_ARMv5) { mem_types[MT_VECTORS].prot_l1 |= ecc_mask; mem_types[MT_MEMORY].prot_sect |= ecc_mask; } else { diff -urN orig/arch/arm/mm/proc-arm926.S linux/arch/arm/mm/proc-arm926.S --- orig/arch/arm/mm/proc-arm926.S Mon May 5 17:38:45 2003 +++ linux/arch/arm/mm/proc-arm926.S Thu May 8 17:55:11 2003 @@ -435,7 +435,7 @@ */ .type arm926_processor_functions, #object arm926_processor_functions: - .word v5tej_early_abort + .word v5tj_early_abort .word cpu_arm926_proc_init .word cpu_arm926_proc_fin .word cpu_arm926_reset diff -urN orig/arch/arm/mm/proc-xscale.S linux/arch/arm/mm/proc-xscale.S --- orig/arch/arm/mm/proc-xscale.S Mon May 5 17:38:45 2003 +++ linux/arch/arm/mm/proc-xscale.S Tue May 13 14:59:25 2003 @@ -235,6 +235,9 @@ * * - start - virtual start address * - end - virtual end address + * + * Note: single I-cache line invalidation isn't used here since + * it also trashes the mini I-cache used by JTAG debuggers. */ ENTRY(xscale_coherent_kern_range) bic r0, r0, #CACHELINESIZE - 1 @@ -611,7 +614,7 @@ .type xscale_processor_functions, #object ENTRY(xscale_processor_functions) - .word xscale_abort + .word ev5t_early_abort .word cpu_xscale_proc_init .word cpu_xscale_proc_fin .word cpu_xscale_reset diff -urN orig/arch/arm/tools/mach-types linux/arch/arm/tools/mach-types --- orig/arch/arm/tools/mach-types Tue May 27 10:04:13 2003 +++ linux/arch/arm/tools/mach-types Tue Jun 10 21:13:11 2003 @@ -6,7 +6,7 @@ # To add an entry into this database, please see Documentation/arm/README, # or contact rmk@arm.linux.org.uk # -# Last update: Wed May 7 23:43:08 2003 +# Last update: Tue Jun 10 21:13:10 2003 # # machine_is_xxx CONFIG_xxxx MACH_TYPE_xxx number # @@ -259,7 +259,7 @@ stork_egg ARCH_STORK_EGG STORK_EGG 248 wismo SA1100_WISMO WISMO 249 ezlinx ARCH_EZLINX EZLINX 250 -at91 ARCH_AT91 AT91 251 +at91rm9200 ARCH_AT91 AT91 251 orion ARCH_ORION ORION 252 neptune ARCH_NEPTUNE NEPTUNE 253 hackkit SA1100_HACKKIT HACKKIT 254 @@ -332,10 +332,24 @@ bandon ARCH_BANDON BANDON 321 pcm7210 ARCH_PCM7210 PCM7210 322 nms9200 ARCH_NMS9200 NMS9200 323 -gealog ARCH_GEALOG GEALOG 324 +logodl ARCH_LOGODL LOGODL 324 m7140 SA1100_M7140 M7140 325 korebot ARCH_KOREBOT KOREBOT 326 iq31244 ARCH_IQ31244 IQ31244 327 koan393 SA1100_KOAN393 KOAN393 328 inhandftip3 ARCH_INHANDFTIP3 INHANDFTIP3 329 gonzo ARCH_GONZO GONZO 330 +bast ARCH_BAST BAST 331 +scanpass ARCH_SCANPASS SCANPASS 332 +ep7312_pooh ARCH_EP7312_POOH EP7312_POOH 333 +ta7s ARCH_TA7S TA7S 334 +ta7v ARCH_TA7V TA7V 335 +icarus SA1100_ICARUS ICARUS 336 +h1900 ARCH_H1900 H1900 337 +gemini SA1100_GEMINI GEMINI 338 +axim ARCH_AXIM AXIM 339 +audiotron ARCH_AUDIOTRON AUDIOTRON 340 +h2200 ARCH_H2200 H2200 341 +loox600 ARCH_LOOX600 LOOX600 342 +niop ARCH_NIOP NIOP 343 +dm310 ARCH_DM310 DM310 344 diff -urN orig/drivers/Makefile linux/drivers/Makefile --- orig/drivers/Makefile Tue May 27 10:04:24 2003 +++ linux/drivers/Makefile Tue May 27 10:48:55 2003 @@ -11,6 +11,8 @@ # PnP must come after ACPI since it will eventually need to check if acpi # was used and do nothing if so obj-$(CONFIG_PNP) += pnp/ +obj-$(CONFIG_I2C) += i2c/ +obj-$(CONFIG_L3) += l3/ # char/ comes before serial/ etc so that the VT console is the boot-time # default. @@ -34,7 +36,7 @@ obj-$(CONFIG_ALL_PPC) += macintosh/ obj-$(CONFIG_MAC) += macintosh/ obj-$(CONFIG_SGI) += sgi/ -obj-$(CONFIG_PARIDE) += block/paride/ +obj-$(CONFIG_PARIDE) += block/paride/ obj-$(CONFIG_TC) += tc/ obj-$(CONFIG_USB) += usb/ obj-$(CONFIG_USB_GADGET) += usb/gadget/ @@ -42,7 +44,6 @@ obj-$(CONFIG_GAMEPORT) += input/gameport/ obj-$(CONFIG_SERIO) += input/serio/ obj-$(CONFIG_I2O) += message/ -obj-$(CONFIG_I2C) += i2c/ obj-$(CONFIG_PHONE) += telephony/ obj-$(CONFIG_MD) += md/ obj-$(CONFIG_BT) += bluetooth/ @@ -51,3 +52,4 @@ obj-$(CONFIG_MCA) += mca/ obj-$(CONFIG_EISA) += eisa/ obj-$(CONFIG_CPU_FREQ) += cpufreq/ +obj-$(CONFIG_ARCH_CAMELOT) += pld/ diff -urN orig/drivers/acorn/char/Makefile linux/drivers/acorn/char/Makefile --- orig/drivers/acorn/char/Makefile Mon Dec 23 18:43:48 2002 +++ linux/drivers/acorn/char/Makefile Tue May 27 17:05:20 2003 @@ -2,22 +2,6 @@ # Makefile for the acorn character device drivers. # -obj-arc := keyb_arc.o defkeymap-acorn.o - obj-$(CONFIG_ARCH_ACORN) += i2c.o pcf8583.o obj-$(CONFIG_L7200_KEYB) += defkeymap-l7200.o keyb_l7200.o obj-y += $(obj-$(MACHINE)) - -$(obj)/defkeymap-acorn.o: $(obj)/defkeymap-acorn.c - -# Uncomment if you're changing the keymap and have an appropriate -# loadkeys version for the map. By default, we'll use the shipped -# versions. -# GENERATE_KEYMAP := 1 - -ifdef GENERATE_KEYMAP - -$(obj)/defkeymap-acorn.c: $(obj)/%.c: $(src)/%.map - loadkeys --mktable $< > $@ - -endif diff -urN orig/drivers/acorn/char/defkeymap-acorn.c_shipped linux/drivers/acorn/char/defkeymap-acorn.c_shipped --- orig/drivers/acorn/char/defkeymap-acorn.c_shipped Sun Jul 7 23:21:11 2002 +++ linux/drivers/acorn/char/defkeymap-acorn.c_shipped Thu Jan 1 01:00:00 1970 @@ -1,262 +0,0 @@ -/* Do not edit this file! It was automatically generated by */ -/* loadkeys --mktable defkeymap.map > defkeymap.c */ - -#include -#include -#include - -u_short plain_map[NR_KEYS] = { - 0xf01b, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 0xf105, 0xf106, - 0xf107, 0xf108, 0xf109, 0xf10a, 0xf10b, 0xf200, 0xf209, 0xf205, - 0xf060, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037, - 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf0a3, 0xf07f, 0xf115, - 0xf114, 0xf118, 0xf208, 0xf30d, 0xf30c, 0xf314, 0xf009, 0xfb71, - 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, 0xfb6f, - 0xfb70, 0xf05b, 0xf05d, 0xf05c, 0xf116, 0xf117, 0xf119, 0xf307, - 0xf308, 0xf309, 0xf30b, 0xf702, 0xfb61, 0xfb73, 0xfb64, 0xfb66, - 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03b, 0xf027, 0xf201, - 0xf304, 0xf305, 0xf306, 0xf30a, 0xf700, 0xf200, 0xfb7a, 0xfb78, - 0xfb63, 0xfb76, 0xfb62, 0xfb6e, 0xfb6d, 0xf02c, 0xf02e, 0xf02f, - 0xf700, 0xf603, 0xf301, 0xf302, 0xf303, 0xf207, 0xf703, 0xf020, - 0xf701, 0xf702, 0xf601, 0xf600, 0xf602, 0xf300, 0xf310, 0xf30e, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, -}; - -u_short shift_map[NR_KEYS] = { - 0xf01b, 0xf10a, 0xf10b, 0xf10c, 0xf10d, 0xf10e, 0xf10f, 0xf110, - 0xf111, 0xf112, 0xf113, 0xf10a, 0xf10b, 0xf200, 0xf203, 0xf205, - 0xf07e, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 0xf026, - 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf0a4, 0xf07f, 0xf115, - 0xf114, 0xf20b, 0xf208, 0xf30d, 0xf30c, 0xf314, 0xf009, 0xfb51, - 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, 0xfb4f, - 0xfb50, 0xf07b, 0xf07d, 0xf07c, 0xf116, 0xf117, 0xf20a, 0xf307, - 0xf308, 0xf309, 0xf30b, 0xf702, 0xfb41, 0xfb53, 0xfb44, 0xfb46, - 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03a, 0xf022, 0xf201, - 0xf304, 0xf305, 0xf306, 0xf30a, 0xf700, 0xf200, 0xfb5a, 0xfb58, - 0xfb43, 0xfb56, 0xfb42, 0xfb4e, 0xfb4d, 0xf03c, 0xf03e, 0xf03f, - 0xf700, 0xf603, 0xf301, 0xf302, 0xf303, 0xf207, 0xf703, 0xf020, - 0xf701, 0xf702, 0xf601, 0xf600, 0xf602, 0xf300, 0xf310, 0xf30e, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, -}; - -u_short altgr_map[NR_KEYS] = { - 0xf200, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, 0xf511, 0xf512, - 0xf513, 0xf514, 0xf515, 0xf516, 0xf517, 0xf200, 0xf202, 0xf205, - 0xf200, 0xf200, 0xf040, 0xf200, 0xf024, 0xf200, 0xf200, 0xf07b, - 0xf05b, 0xf05d, 0xf07d, 0xf05c, 0xf200, 0xf200, 0xf200, 0xf115, - 0xf114, 0xf118, 0xf208, 0xf30d, 0xf30c, 0xf314, 0xf200, 0xfb71, - 0xfb77, 0xf918, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, 0xfb6f, - 0xfb70, 0xf200, 0xf07e, 0xf200, 0xf116, 0xf117, 0xf119, 0xf911, - 0xf912, 0xf913, 0xf30b, 0xf702, 0xf914, 0xfb73, 0xf917, 0xf919, - 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf200, 0xf200, 0xf201, - 0xf90e, 0xf90f, 0xf910, 0xf30a, 0xf700, 0xf200, 0xfb7a, 0xfb78, - 0xf916, 0xfb76, 0xf915, 0xfb6e, 0xfb6d, 0xf200, 0xf200, 0xf200, - 0xf700, 0xf603, 0xf90b, 0xf90c, 0xf90d, 0xf207, 0xf703, 0xf200, - 0xf701, 0xf702, 0xf601, 0xf600, 0xf602, 0xf90a, 0xf310, 0xf30e, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, -}; - -u_short ctrl_map[NR_KEYS] = { - 0xf200, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, 0xf105, 0xf106, - 0xf107, 0xf108, 0xf109, 0xf10a, 0xf10b, 0xf200, 0xf204, 0xf205, - 0xf200, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f, - 0xf07f, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf200, 0xf008, 0xf115, - 0xf114, 0xf118, 0xf208, 0xf30d, 0xf30c, 0xf314, 0xf200, 0xf011, - 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 0xf00f, - 0xf010, 0xf01b, 0xf01d, 0xf01c, 0xf116, 0xf117, 0xf119, 0xf307, - 0xf308, 0xf309, 0xf30b, 0xf702, 0xf001, 0xf013, 0xf004, 0xf006, - 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 0xf007, 0xf201, - 0xf304, 0xf305, 0xf306, 0xf30a, 0xf700, 0xf200, 0xf01a, 0xf018, - 0xf003, 0xf016, 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf20e, 0xf07f, - 0xf700, 0xf603, 0xf301, 0xf302, 0xf303, 0xf207, 0xf703, 0xf000, - 0xf701, 0xf702, 0xf601, 0xf600, 0xf602, 0xf300, 0xf310, 0xf30e, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, -}; - -u_short shift_ctrl_map[NR_KEYS] = { - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf209, 0xf205, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf200, 0xf200, 0xf115, - 0xf114, 0xf118, 0xf208, 0xf30d, 0xf30c, 0xf314, 0xf200, 0xf011, - 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, 0xf00f, - 0xf010, 0xf200, 0xf200, 0xf200, 0xf116, 0xf117, 0xf119, 0xf307, - 0xf308, 0xf309, 0xf30b, 0xf702, 0xf001, 0xf013, 0xf004, 0xf006, - 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, 0xf200, 0xf201, - 0xf304, 0xf305, 0xf306, 0xf30a, 0xf700, 0xf200, 0xf01a, 0xf018, - 0xf003, 0xf016, 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf200, 0xf200, - 0xf700, 0xf603, 0xf301, 0xf302, 0xf303, 0xf207, 0xf703, 0xf200, - 0xf701, 0xf702, 0xf601, 0xf600, 0xf602, 0xf300, 0xf310, 0xf30e, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, -}; - -u_short alt_map[NR_KEYS] = { - 0xf81b, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 0xf505, 0xf506, - 0xf507, 0xf508, 0xf509, 0xf50a, 0xf50b, 0xf200, 0xf209, 0xf205, - 0xf860, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, 0xf837, - 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf8a3, 0xf87f, 0xf115, - 0xf114, 0xf118, 0xf208, 0xf30d, 0xf30c, 0xf314, 0xf809, 0xf871, - 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, 0xf86f, - 0xf870, 0xf85b, 0xf85d, 0xf85c, 0xf116, 0xf117, 0xf119, 0xf907, - 0xf908, 0xf909, 0xf30b, 0xf702, 0xf861, 0xf873, 0xf864, 0xf866, - 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b, 0xf827, 0xf80d, - 0xf904, 0xf905, 0xf906, 0xf30a, 0xf700, 0xf200, 0xf87a, 0xf878, - 0xf863, 0xf876, 0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, - 0xf700, 0xf603, 0xf901, 0xf902, 0xf903, 0xf207, 0xf703, 0xf820, - 0xf701, 0xf702, 0xf210, 0xf600, 0xf211, 0xf900, 0xf310, 0xf30e, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, -}; - -u_short ctrl_alt_map[NR_KEYS] = { - 0xf200, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, 0xf505, 0xf506, - 0xf507, 0xf508, 0xf509, 0xf50a, 0xf50b, 0xf200, 0xf209, 0xf205, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf115, - 0xf114, 0xf118, 0xf208, 0xf30d, 0xf30c, 0xf314, 0xf200, 0xf811, - 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, 0xf80f, - 0xf810, 0xf200, 0xf200, 0xf200, 0xf20c, 0xf117, 0xf119, 0xf307, - 0xf308, 0xf309, 0xf30b, 0xf702, 0xf801, 0xf813, 0xf804, 0xf806, - 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, 0xf200, 0xf201, - 0xf304, 0xf305, 0xf306, 0xf30a, 0xf700, 0xf200, 0xf81a, 0xf818, - 0xf803, 0xf816, 0xf802, 0xf80e, 0xf80d, 0xf200, 0xf200, 0xf200, - 0xf700, 0xf603, 0xf301, 0xf302, 0xf303, 0xf207, 0xf703, 0xf200, - 0xf701, 0xf702, 0xf601, 0xf600, 0xf602, 0xf300, 0xf20c, 0xf30e, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, - 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, -}; - -ushort *key_maps[MAX_NR_KEYMAPS] = { - plain_map, shift_map, altgr_map, 0, - ctrl_map, shift_ctrl_map, 0, 0, - alt_map, 0, 0, 0, - ctrl_alt_map, 0 -}; - -unsigned int keymap_count = 7; - -/* - * Philosophy: most people do not define more strings, but they who do - * often want quite a lot of string space. So, we statically allocate - * the default and allocate dynamically in chunks of 512 bytes. - */ - -char func_buf[] = { - '\033', '[', '[', 'A', 0, - '\033', '[', '[', 'B', 0, - '\033', '[', '[', 'C', 0, - '\033', '[', '[', 'D', 0, - '\033', '[', '[', 'E', 0, - '\033', '[', '1', '7', '~', 0, - '\033', '[', '1', '8', '~', 0, - '\033', '[', '1', '9', '~', 0, - '\033', '[', '2', '0', '~', 0, - '\033', '[', '2', '1', '~', 0, - '\033', '[', '2', '3', '~', 0, - '\033', '[', '2', '4', '~', 0, - '\033', '[', '2', '5', '~', 0, - '\033', '[', '2', '6', '~', 0, - '\033', '[', '2', '8', '~', 0, - '\033', '[', '2', '9', '~', 0, - '\033', '[', '3', '1', '~', 0, - '\033', '[', '3', '2', '~', 0, - '\033', '[', '3', '3', '~', 0, - '\033', '[', '3', '4', '~', 0, - '\033', '[', '1', '~', 0, - '\033', '[', '2', '~', 0, - '\033', '[', '3', '~', 0, - '\033', '[', '4', '~', 0, - '\033', '[', '5', '~', 0, - '\033', '[', '6', '~', 0, - '\033', '[', 'M', 0, - '\033', '[', 'P', 0, -}; - -char *funcbufptr = func_buf; -int funcbufsize = sizeof(func_buf); -int funcbufleft = 0; /* space left */ - -char *func_table[MAX_NR_FUNC] = { - func_buf + 0, - func_buf + 5, - func_buf + 10, - func_buf + 15, - func_buf + 20, - func_buf + 25, - func_buf + 31, - func_buf + 37, - func_buf + 43, - func_buf + 49, - func_buf + 55, - func_buf + 61, - func_buf + 67, - func_buf + 73, - func_buf + 79, - func_buf + 85, - func_buf + 91, - func_buf + 97, - func_buf + 103, - func_buf + 109, - func_buf + 115, - func_buf + 120, - func_buf + 125, - func_buf + 130, - func_buf + 135, - func_buf + 140, - func_buf + 145, - 0, - 0, - func_buf + 149, - 0, -}; - -struct kbdiacr accent_table[MAX_DIACR] = { - {'`', 'A', '\300'}, {'`', 'a', '\340'}, - {'\'', 'A', '\301'}, {'\'', 'a', '\341'}, - {'^', 'A', '\302'}, {'^', 'a', '\342'}, - {'~', 'A', '\303'}, {'~', 'a', '\343'}, - {'"', 'A', '\304'}, {'"', 'a', '\344'}, - {'O', 'A', '\305'}, {'o', 'a', '\345'}, - {'0', 'A', '\305'}, {'0', 'a', '\345'}, - {'A', 'A', '\305'}, {'a', 'a', '\345'}, - {'A', 'E', '\306'}, {'a', 'e', '\346'}, - {',', 'C', '\307'}, {',', 'c', '\347'}, - {'`', 'E', '\310'}, {'`', 'e', '\350'}, - {'\'', 'E', '\311'}, {'\'', 'e', '\351'}, - {'^', 'E', '\312'}, {'^', 'e', '\352'}, - {'"', 'E', '\313'}, {'"', 'e', '\353'}, - {'`', 'I', '\314'}, {'`', 'i', '\354'}, - {'\'', 'I', '\315'}, {'\'', 'i', '\355'}, - {'^', 'I', '\316'}, {'^', 'i', '\356'}, - {'"', 'I', '\317'}, {'"', 'i', '\357'}, - {'-', 'D', '\320'}, {'-', 'd', '\360'}, - {'~', 'N', '\321'}, {'~', 'n', '\361'}, - {'`', 'O', '\322'}, {'`', 'o', '\362'}, - {'\'', 'O', '\323'}, {'\'', 'o', '\363'}, - {'^', 'O', '\324'}, {'^', 'o', '\364'}, - {'~', 'O', '\325'}, {'~', 'o', '\365'}, - {'"', 'O', '\326'}, {'"', 'o', '\366'}, - {'/', 'O', '\330'}, {'/', 'o', '\370'}, - {'`', 'U', '\331'}, {'`', 'u', '\371'}, - {'\'', 'U', '\332'}, {'\'', 'u', '\372'}, - {'^', 'U', '\333'}, {'^', 'u', '\373'}, - {'"', 'U', '\334'}, {'"', 'u', '\374'}, - {'\'', 'Y', '\335'}, {'\'', 'y', '\375'}, - {'T', 'H', '\336'}, {'t', 'h', '\376'}, - {'s', 's', '\337'}, {'"', 'y', '\377'}, - {'s', 'z', '\337'}, {'i', 'j', '\377'}, -}; - -unsigned int accent_table_size = 68; diff -urN orig/drivers/acorn/char/defkeymap-acorn.map linux/drivers/acorn/char/defkeymap-acorn.map --- orig/drivers/acorn/char/defkeymap-acorn.map Mon Sep 3 14:14:43 2001 +++ linux/drivers/acorn/char/defkeymap-acorn.map Thu Jan 1 01:00:00 1970 @@ -1,356 +0,0 @@ -# Default kernel keymap for Acorn machines. This uses 7 modifier combinations. -keymaps 0-2,4-5,8,12 -# Change the above line into -# keymaps 0-2,4-6,8,12 -# in case you want the entries -# altgr control keycode 52 = Boot -# altgr control keycode 102 = Boot -# below. -# -# In fact AltGr is used very little, and one more keymap can -# be saved by mapping AltGr to Alt (and adapting a few entries): -# keycode 96 = Alt -# -keycode 0 = Escape - shift keycode 0 = Escape - alt keycode 0 = Meta_Escape -keycode 1 = F1 F11 Console_13 - control keycode 1 = F1 - alt keycode 1 = Console_1 - control alt keycode 1 = Console_1 -keycode 2 = F2 F12 Console_14 - control keycode 2 = F2 - alt keycode 2 = Console_2 - control alt keycode 2 = Console_2 -keycode 3 = F3 F13 Console_15 - control keycode 3 = F3 - alt keycode 3 = Console_3 - control alt keycode 3 = Console_3 -keycode 4 = F4 F14 Console_16 - control keycode 4 = F4 - alt keycode 4 = Console_4 - control alt keycode 4 = Console_4 -keycode 5 = F5 F15 Console_17 - control keycode 5 = F5 - alt keycode 5 = Console_5 - control alt keycode 5 = Console_5 -keycode 6 = F6 F16 Console_18 - control keycode 6 = F6 - alt keycode 6 = Console_6 - control alt keycode 6 = Console_6 -keycode 7 = F7 F17 Console_19 - control keycode 7 = F7 - alt keycode 7 = Console_7 - control alt keycode 7 = Console_7 -keycode 8 = F8 F18 Console_20 - control keycode 8 = F8 - alt keycode 8 = Console_8 - control alt keycode 8 = Console_8 -keycode 9 = F9 F19 Console_21 - control keycode 9 = F9 - alt keycode 9 = Console_9 - control alt keycode 9 = Console_9 -keycode 10 = F10 F20 Console_22 - control keycode 10 = F10 - alt keycode 10 = Console_10 - control alt keycode 10 = Console_10 -keycode 11 = F11 F11 Console_23 - control keycode 11 = F11 - alt keycode 11 = Console_11 - control alt keycode 11 = Console_11 -keycode 12 = F12 F12 Console_24 - control keycode 12 = F12 - alt keycode 12 = Console_12 - control alt keycode 12 = Console_12 -keycode 13 = -keycode 14 = Scroll_Lock - shift keycode 14 = Show_Memory - altgr keycode 14 = Show_Registers - control keycode 14 = Show_State - alt keycode 14 = Scroll_Lock -keycode 15 = Break -keycode 16 = grave asciitilde - alt keycode 16 = Meta_grave -keycode 17 = one exclam - alt keycode 17 = Meta_one -keycode 18 = two at at - control keycode 18 = nul - alt keycode 18 = Meta_two -keycode 19 = three numbersign - control keycode 19 = Escape - alt keycode 19 = Meta_three -keycode 20 = four dollar dollar - control keycode 20 = Control_backslash - alt keycode 20 = Meta_four -keycode 21 = five percent - control keycode 21 = Control_bracketright - alt keycode 21 = Meta_five -keycode 22 = six asciicircum - control keycode 22 = Control_asciicircum - alt keycode 22 = Meta_six -keycode 23 = seven ampersand braceleft - control keycode 23 = Control_underscore - alt keycode 23 = Meta_seven -keycode 24 = eight asterisk bracketleft - control keycode 24 = Delete - alt keycode 24 = Meta_eight -keycode 25 = nine parenleft bracketright - alt keycode 25 = Meta_nine -keycode 26 = zero parenright braceright - alt keycode 26 = Meta_zero -keycode 27 = minus underscore backslash - control keycode 27 = Control_underscore - shift control keycode 27 = Control_underscore - alt keycode 27 = Meta_minus -keycode 28 = equal plus - alt keycode 28 = Meta_equal -keycode 29 = sterling currency - alt keycode 29 = 0x08a3 -keycode 30 = Delete Delete - control keycode 30 = BackSpace - alt keycode 30 = Meta_Delete -keycode 31 = Insert -keycode 32 = Find -keycode 33 = Prior - shift keycode 33 = Scroll_Backward -keycode 34 = Num_Lock -keycode 35 = KP_Divide -keycode 36 = KP_Multiply -keycode 37 = 0x0314 -keycode 38 = Tab Tab - alt keycode 38 = Meta_Tab -keycode 39 = q -keycode 40 = w -keycode 41 = e - altgr keycode 41 = Hex_E -keycode 42 = r -keycode 43 = t -keycode 44 = y -keycode 45 = u -keycode 46 = i -keycode 47 = o -keycode 48 = p -keycode 49 = bracketleft braceleft - control keycode 49 = Escape - alt keycode 49 = Meta_bracketleft -keycode 50 = bracketright braceright asciitilde - control keycode 50 = Control_bracketright - alt keycode 50 = Meta_bracketright -keycode 51 = backslash bar - control keycode 51 = Control_backslash - alt keycode 51 = Meta_backslash -keycode 52 = Remove -# altgr control keycode 52 = Boot - control alt keycode 52 = Boot -keycode 53 = Select -keycode 54 = Next - shift keycode 54 = Scroll_Forward -keycode 55 = KP_7 - altgr keycode 55 = Hex_7 - alt keycode 55 = Ascii_7 -keycode 56 = KP_8 - altgr keycode 56 = Hex_8 - alt keycode 56 = Ascii_8 -keycode 57 = KP_9 - altgr keycode 57 = Hex_9 - alt keycode 57 = Ascii_9 -keycode 58 = KP_Subtract -keycode 59 = Control -keycode 60 = a - altgr keycode 60 = Hex_A -keycode 61 = s -keycode 62 = d - altgr keycode 62 = Hex_D -keycode 63 = f - altgr keycode 63 = Hex_F -keycode 64 = g -keycode 65 = h -keycode 66 = j -keycode 67 = k -keycode 68 = l -keycode 69 = semicolon colon - alt keycode 69 = Meta_semicolon -keycode 70 = apostrophe quotedbl - control keycode 70 = Control_g - alt keycode 70 = Meta_apostrophe -keycode 71 = Return - alt keycode 71 = Meta_Control_m -keycode 72 = KP_4 - altgr keycode 72 = Hex_4 - alt keycode 72 = Ascii_4 -keycode 73 = KP_5 - altgr keycode 73 = Hex_5 - alt keycode 73 = Ascii_5 -keycode 74 = KP_6 - altgr keycode 74 = Hex_6 - alt keycode 74 = Ascii_6 -keycode 75 = KP_Add -keycode 76 = Shift -keycode 77 = -keycode 78 = z -keycode 79 = x -keycode 80 = c - altgr keycode 80 = Hex_C -keycode 81 = v -keycode 82 = b - altgr keycode 82 = Hex_B -keycode 83 = n -keycode 84 = m -keycode 85 = comma less - alt keycode 85 = Meta_comma -keycode 86 = period greater - control keycode 86 = Compose - alt keycode 86 = Meta_period -keycode 87 = slash question - control keycode 87 = Delete - alt keycode 87 = Meta_slash -keycode 88 = Shift -keycode 89 = Up -keycode 90 = KP_1 - altgr keycode 90 = Hex_1 - alt keycode 90 = Ascii_1 -keycode 91 = KP_2 - altgr keycode 91 = Hex_2 - alt keycode 91 = Ascii_2 -keycode 92 = KP_3 - altgr keycode 92 = Hex_3 - alt keycode 92 = Ascii_3 -keycode 93 = Caps_Lock -keycode 94 = Alt -keycode 95 = space space - control keycode 95 = nul - alt keycode 95 = Meta_space -keycode 96 = AltGr -keycode 97 = Control -keycode 98 = Left - alt keycode 98 = Decr_Console -keycode 99 = Down -keycode 100 = Right - alt keycode 100 = Incr_Console -keycode 101 = KP_0 - altgr keycode 101 = Hex_0 - alt keycode 101 = Ascii_0 -keycode 102 = KP_Period -# altgr control keycode 102 = Boot - control alt keycode 102 = Boot -keycode 103 = KP_Enter -keycode 104 = -keycode 105 = -keycode 106 = -keycode 107 = -keycode 108 = -keycode 109 = -keycode 110 = -keycode 111 = -keycode 112 = -keycode 113 = -keycode 114 = -keycode 115 = -keycode 116 = -keycode 117 = -keycode 118 = -keycode 119 = -keycode 120 = -keycode 121 = -keycode 122 = -keycode 123 = -keycode 124 = -keycode 125 = -keycode 126 = -keycode 127 = -string F1 = "\033[[A" -string F2 = "\033[[B" -string F3 = "\033[[C" -string F4 = "\033[[D" -string F5 = "\033[[E" -string F6 = "\033[17~" -string F7 = "\033[18~" -string F8 = "\033[19~" -string F9 = "\033[20~" -string F10 = "\033[21~" -string F11 = "\033[23~" -string F12 = "\033[24~" -string F13 = "\033[25~" -string F14 = "\033[26~" -string F15 = "\033[28~" -string F16 = "\033[29~" -string F17 = "\033[31~" -string F18 = "\033[32~" -string F19 = "\033[33~" -string F20 = "\033[34~" -string Find = "\033[1~" -string Insert = "\033[2~" -string Remove = "\033[3~" -string Select = "\033[4~" -string Prior = "\033[5~" -string Next = "\033[6~" -string Macro = "\033[M" -string Pause = "\033[P" -compose '`' 'A' to 'À' -compose '`' 'a' to 'à' -compose '\'' 'A' to 'Á' -compose '\'' 'a' to 'á' -compose '^' 'A' to 'Â' -compose '^' 'a' to 'â' -compose '~' 'A' to 'Ã' -compose '~' 'a' to 'ã' -compose '"' 'A' to 'Ä' -compose '"' 'a' to 'ä' -compose 'O' 'A' to 'Å' -compose 'o' 'a' to 'å' -compose '0' 'A' to 'Å' -compose '0' 'a' to 'å' -compose 'A' 'A' to 'Å' -compose 'a' 'a' to 'å' -compose 'A' 'E' to 'Æ' -compose 'a' 'e' to 'æ' -compose ',' 'C' to 'Ç' -compose ',' 'c' to 'ç' -compose '`' 'E' to 'È' -compose '`' 'e' to 'è' -compose '\'' 'E' to 'É' -compose '\'' 'e' to 'é' -compose '^' 'E' to 'Ê' -compose '^' 'e' to 'ê' -compose '"' 'E' to 'Ë' -compose '"' 'e' to 'ë' -compose '`' 'I' to 'Ì' -compose '`' 'i' to 'ì' -compose '\'' 'I' to 'Í' -compose '\'' 'i' to 'í' -compose '^' 'I' to 'Î' -compose '^' 'i' to 'î' -compose '"' 'I' to 'Ï' -compose '"' 'i' to 'ï' -compose '-' 'D' to 'Ð' -compose '-' 'd' to 'ð' -compose '~' 'N' to 'Ñ' -compose '~' 'n' to 'ñ' -compose '`' 'O' to 'Ò' -compose '`' 'o' to 'ò' -compose '\'' 'O' to 'Ó' -compose '\'' 'o' to 'ó' -compose '^' 'O' to 'Ô' -compose '^' 'o' to 'ô' -compose '~' 'O' to 'Õ' -compose '~' 'o' to 'õ' -compose '"' 'O' to 'Ö' -compose '"' 'o' to 'ö' -compose '/' 'O' to 'Ø' -compose '/' 'o' to 'ø' -compose '`' 'U' to 'Ù' -compose '`' 'u' to 'ù' -compose '\'' 'U' to 'Ú' -compose '\'' 'u' to 'ú' -compose '^' 'U' to 'Û' -compose '^' 'u' to 'û' -compose '"' 'U' to 'Ü' -compose '"' 'u' to 'ü' -compose '\'' 'Y' to 'Ý' -compose '\'' 'y' to 'ý' -compose 'T' 'H' to 'Þ' -compose 't' 'h' to 'þ' -compose 's' 's' to 'ß' -compose '"' 'y' to 'ÿ' -compose 's' 'z' to 'ß' -compose 'i' 'j' to 'ÿ' diff -urN orig/drivers/acorn/char/keyb_arc.c linux/drivers/acorn/char/keyb_arc.c --- orig/drivers/acorn/char/keyb_arc.c Sun Feb 16 11:46:00 2003 +++ linux/drivers/acorn/char/keyb_arc.c Thu Jan 1 01:00:00 1970 @@ -1,450 +0,0 @@ -/* - * linux/drivers/acorn/char/keyb_arc.c - * - * Copyright (C) 2000 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. - * - * Acorn keyboard driver for ARM Linux. - * - * The Acorn keyboard appears to have a ***very*** buggy reset protocol - - * every reset behaves differently. We try to get round this by attempting - * a few things... - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "../../char/busmouse.h" - -extern struct tasklet_struct keyboard_tasklet; -extern void kbd_reset_kdown(void); - -#define VERSION 108 - -#define KBD_REPORT_ERR -#define KBD_REPORT_UNKN - -#include -#include - -static char kbd_txval[4]; -static unsigned char kbd_txhead, kbd_txtail; -#define KBD_INCTXPTR(ptr) ((ptr) = ((ptr) + 1) & 3) -static int kbd_id = -1; -static DECLARE_WAIT_QUEUE_HEAD(kbd_waitq); -#ifdef CONFIG_KBDMOUSE -static int mousedev; -#endif - -/* - * Protocol codes to send the keyboard. - */ -#define HRST 0xff /* reset keyboard */ -#define RAK1 0xfe /* reset response */ -#define RAK2 0xfd /* reset response */ -#define BACK 0x3f /* Ack for first keyboard pair */ -#define SMAK 0x33 /* Last data byte ack (key scanning + mouse movement scanning) */ -#define MACK 0x32 /* Last data byte ack (mouse movement scanning) */ -#define SACK 0x31 /* Last data byte ack (key scanning) */ -#define NACK 0x30 /* Last data byte ack (no scanning, mouse data) */ -#define RQMP 0x22 /* Request mouse data */ -#define PRST 0x21 /* nothing */ -#define RQID 0x20 /* Request ID */ - -#define UP_FLAG 1 - -#ifdef CONFIG_MAGIC_SYSRQ -unsigned char a5kkbd_sysrq_xlate[] = -{ - 27, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - '`', '1', '2', '3', '4', '5', '6', '7', - '8', '9', '0', '-', '=', '£', 127, 0, - 0, 0, 0, '/', '*', '#', 9, 'q', - 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', - 'p', '[', ']', '\\', 22, 23, 25, '7', - '8', '9', '-', 0, 'a', 's', 'd', 'f', - 'g', 'h', 'j', 'k', 'l', ';', '\'', 13, - '4', '5', '6', '+', 0, 0, 'z', 'x', - 'c', 'v', 'b', 'n', 'm', ',', '.', '/', - 0, 0, '1', '2', '3', 0, 0, ' ', - 0, 0, 0, 0, 0, '0', '.', 10, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, -}; -#endif - -/* - * This array converts the scancode that we get from the keyboard to the - * real rows/columns on the A5000 keyboard. This might be keyboard specific... - * - * It is these values that we use to maintain the key down array. That way, we - * should pick up on the ghost key presses (which is what happens when you press - * three keys, and the keyboard thinks you have pressed four!) - * - * Row 8 (0x80+c) is actually a column with one key per row. It is isolated from - * the other keys, and can't cause these problems (its used for shift, ctrl, alt etc). - * - * Illegal scancodes are denoted by an 0xff (in other words, we don't know about - * them, and can't process them for ghosts). This does however, cause problems with - * autorepeat processing... - */ -static unsigned char scancode_2_colrow[256] = { - 0x01, 0x42, 0x32, 0x33, 0x43, 0x56, 0x5a, 0x6c, 0x7c, 0x5c, 0x5b, 0x6b, 0x7b, 0x84, 0x70, 0x60, - 0x11, 0x51, 0x62, 0x63, 0x44, 0x54, 0x55, 0x45, 0x46, 0x4a, 0x3c, 0x4b, 0x59, 0x49, 0x69, 0x79, - 0x83, 0x40, 0x30, 0x3b, 0x39, 0x38, 0x31, 0x61, 0x72, 0x73, 0x64, 0x74, 0x75, 0x65, 0x66, 0x6a, - 0x1c, 0x2c, 0x7a, 0x36, 0x48, 0x68, 0x78, 0x20, 0x2b, 0x29, 0x28, 0x81, 0x71, 0x22, 0x23, 0x34, - 0x24, 0x25, 0x35, 0x26, 0x3a, 0x0c, 0x2a, 0x76, 0x10, 0x1b, 0x19, 0x18, 0x82, 0xff, 0x21, 0x12, - 0x13, 0x14, 0x04, 0x05, 0x15, 0x16, 0x1a, 0x0a, 0x85, 0x77, 0x00, 0x0b, 0x09, 0x02, 0x80, 0x03, - 0x87, 0x86, 0x06, 0x17, 0x27, 0x07, 0x37, 0x08, 0xff, -}; - -#define BITS_PER_SHORT (8*sizeof(unsigned short)) -static unsigned short ghost_down[128/BITS_PER_SHORT]; - -static void a5kkbd_key(unsigned int keycode, unsigned int up_flag) -{ - unsigned int real_keycode; - - if (keycode > 0x72) { -#ifdef KBD_REPORT_UNKN - printk ("kbd: unknown scancode 0x%04x\n", keycode); -#endif - return; - } - if (keycode >= 0x70) { -#ifdef CONFIG_KBDMOUSE - if (mousedev >= 0) - switch (keycode) { - case 0x70: /* Left mouse button */ - busmouse_add_buttons(mousedev, 4, up_flag ? 4 : 0); - break; - - case 0x71: /* Middle mouse button */ - busmouse_add_buttons(mousedev, 2, up_flag ? 2 : 0); - break; - - case 0x72:/* Right mouse button */ - busmouse_add_buttons(mousedev, 1, up_flag ? 1 : 0); - break; - } -#endif - return; - } - - /* - * We have to work out if we accept this key press as a real key, or - * if it is a ghost. IE. If you press three keys, the keyboard will think - * that you've pressed a fourth: (@ = key down, # = ghost) - * - * 0 1 2 3 4 5 6 7 - * | | | | | | | | - * 0-+-+-+-+-+-+-+-+- - * | | | | | | | | - * 1-+-@-+-+-+-@-+-+- - * | | | | | | | | - * 2-+-+-+-+-+-+-+-+- - * | | | | | | | | - * 3-+-@-+-+-+-#-+-+- - * | | | | | | | | - * - * This is what happens when you have a matrix keyboard... - */ - - real_keycode = scancode_2_colrow[keycode]; - - if ((real_keycode & 0x80) == 0) { - int rr, kc = (real_keycode >> 4) & 7; - int cc; - unsigned short res, kdownkc; - - kdownkc = ghost_down[kc] | (1 << (real_keycode & 15)); - - for (rr = 0; rr < 128/BITS_PER_SHORT; rr++) - if (rr != kc && (res = ghost_down[rr] & kdownkc)) { - /* - * we have found a second row with at least one key pressed in the - * same column. - */ - for (cc = 0; res; res >>= 1) - cc += (res & 1); - if (cc > 1) - return; /* ignore it */ - } - if (up_flag) - clear_bit (real_keycode, ghost_down); - else - set_bit (real_keycode, ghost_down); - } - - handle_scancode(keycode, !up_flag); -} - -static inline void a5kkbd_sendbyte(unsigned char val) -{ - kbd_txval[kbd_txhead] = val; - KBD_INCTXPTR(kbd_txhead); - enable_irq(IRQ_KEYBOARDTX); -} - -static inline void a5kkbd_reset(void) -{ - int i; - - for (i = 0; i < NR_SCANCODES/BITS_PER_SHORT; i++) - ghost_down[i] = 0; - - kbd_reset_kdown(); -} - -void a5kkbd_leds(unsigned char leds) -{ - leds = ((leds & (1<= 0) - busmouse_add_movement(mousedev, (int)kbd_mousedx, (int)kbd_mousedy); -#endif - } - } - return kbd_state == KBD_IDLE ? 1 : 0; - -kbd_wontreset: -#ifdef KBD_REPORT_ERR - printk ("kbd: keyboard won't reset (kbdstate %d, keyval %02X)\n", - kbd_state, keyval); -#endif - mdelay(1); - ioc_readb(IOC_KARTRX); - a5kkbd_sendbyte (HRST); - kbd_state = KBD_INITRST; - return 0; - -kbd_error: -#ifdef KBD_REPORT_ERR - printk ("kbd: keyboard out of sync - resetting\n"); -#endif - a5kkbd_sendbyte (HRST); - kbd_state = KBD_INITRST; - return 0; -} - -static void a5kkbd_rx(int irq, void *dev_id, struct pt_regs *regs) -{ - if (handle_rawcode(ioc_readb(IOC_KARTRX))) - tasklet_schedule(&keyboard_tasklet); -} - -static void a5kkbd_tx(int irq, void *dev_id, struct pt_regs *regs) -{ - ioc_writeb (kbd_txval[kbd_txtail], IOC_KARTTX); - KBD_INCTXPTR(kbd_txtail); - if (kbd_txtail == kbd_txhead) - disable_irq(irq); -} - -#ifdef CONFIG_KBDMOUSE -static struct busmouse a5kkbd_mouse = { - 6, "kbdmouse", NULL, NULL, NULL, 7 -}; -#endif - -void __init a5kkbd_init_hw (void) -{ - if (request_irq (IRQ_KEYBOARDTX, a5kkbd_tx, 0, "keyboard", NULL) != 0) - panic("Could not allocate keyboard transmit IRQ!"); - (void)ioc_readb(IOC_KARTRX); - if (request_irq (IRQ_KEYBOARDRX, a5kkbd_rx, 0, "keyboard", NULL) != 0) - panic("Could not allocate keyboard receive IRQ!"); - - a5kkbd_sendbyte (HRST); /* send HRST (expect HRST) */ - - /* wait 1s for keyboard to initialise */ - interruptible_sleep_on_timeout(&kbd_waitq, HZ); - -#ifdef CONFIG_KBDMOUSE - mousedev = register_busmouse(&a5kkbd_mouse); - if (mousedev < 0) - printk(KERN_ERR "Unable to register mouse driver\n"); -#endif - - printk (KERN_INFO "Keyboard driver v%d.%02d. (", VERSION/100, VERSION%100); - if (kbd_id != -1) - printk ("id=%d ", kbd_id); - printk ("English)\n"); -} diff -urN orig/drivers/acorn/char/mouse_ps2.c linux/drivers/acorn/char/mouse_ps2.c --- orig/drivers/acorn/char/mouse_ps2.c Sat Oct 12 10:01:35 2002 +++ linux/drivers/acorn/char/mouse_ps2.c Thu Jan 1 01:00:00 1970 @@ -1,294 +0,0 @@ -/* - * Driver for PS/2 mouse on IOMD interface - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -/* - * PS/2 Auxiliary Device - */ - -static struct aux_queue *queue; /* Mouse data buffer. */ -static int aux_count = 0; -/* used when we send commands to the mouse that expect an ACK. */ -static unsigned char mouse_reply_expected = 0; - -#define MAX_RETRIES 60 /* some aux operations take long time*/ - -/* - * Mouse Commands - */ - -#define AUX_SET_RES 0xE8 /* Set resolution */ -#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */ -#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */ -#define AUX_GET_SCALE 0xE9 /* Get scaling factor */ -#define AUX_SET_STREAM 0xEA /* Set stream mode */ -#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */ -#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */ -#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */ -#define AUX_RESET 0xFF /* Reset aux device */ -#define AUX_ACK 0xFA /* Command byte ACK. */ - -#define AUX_BUF_SIZE 2048 /* This might be better divisible by - three to make overruns stay in sync - but then the read function would - need a lock etc - ick */ - -struct aux_queue { - unsigned long head; - unsigned long tail; - wait_queue_head_t proc_list; - struct fasync_struct *fasync; - unsigned char buf[AUX_BUF_SIZE]; -}; - -/* - * Send a byte to the mouse. - */ -static void aux_write_dev(int val) -{ - while (!(iomd_readb(IOMD_MSECTL) & 0x80)); - iomd_writeb(val, IOMD_MSEDAT); -} - -/* - * Send a byte to the mouse & handle returned ack - */ -static void aux_write_ack(int val) -{ - while (!(iomd_readb(IOMD_MSECTL) & 0x80)); - iomd_writeb(val, IOMD_MSEDAT); - - /* we expect an ACK in response. */ - mouse_reply_expected++; -} - -static unsigned char get_from_queue(void) -{ - unsigned char result; - - result = queue->buf[queue->tail]; - queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1); - return result; -} - -static void psaux_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - int val = iomd_readb(IOMD_MSEDAT); - - if (mouse_reply_expected) { - if (val == AUX_ACK) { - mouse_reply_expected--; - return; - } - mouse_reply_expected = 0; - } - - add_mouse_randomness(val); - if (aux_count) { - int head = queue->head; - - queue->buf[head] = val; - head = (head + 1) & (AUX_BUF_SIZE-1); - if (head != queue->tail) { - queue->head = head; - kill_fasync(&queue->fasync, SIGIO, POLL_IN); - wake_up_interruptible(&queue->proc_list); - } - } -} - -static inline int queue_empty(void) -{ - return queue->head == queue->tail; -} - -static int fasync_aux(int fd, struct file *filp, int on) -{ - int retval; - - retval = fasync_helper(fd, filp, on, &queue->fasync); - if (retval < 0) - return retval; - return 0; -} - - -/* - * Random magic cookie for the aux device - */ -#define AUX_DEV ((void *)queue) - -static int release_aux(struct inode * inode, struct file * file) -{ - fasync_aux(-1, file, 0); - if (--aux_count) - return 0; - free_irq(IRQ_MOUSERX, AUX_DEV); - return 0; -} - -/* - * Install interrupt handler. - * Enable auxiliary device. - */ - -static int open_aux(struct inode * inode, struct file * file) -{ - if (aux_count++) - return 0; - - queue->head = queue->tail = 0; /* Flush input queue */ - if (request_irq(IRQ_MOUSERX, psaux_interrupt, SA_SHIRQ, "ps/2 mouse", - AUX_DEV)) { - aux_count--; - return -EBUSY; - } - - aux_write_ack(AUX_ENABLE_DEV); /* Enable aux device */ - - return 0; -} - -/* - * Put bytes from input queue to buffer. - */ - -static ssize_t read_aux(struct file * file, char * buffer, - size_t count, loff_t *ppos) -{ - DECLARE_WAITQUEUE(wait, current); - ssize_t i = count; - unsigned char c; - - if (queue_empty()) { - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; - add_wait_queue(&queue->proc_list, &wait); -repeat: - current->state = TASK_INTERRUPTIBLE; - if (queue_empty() && !signal_pending(current)) { - schedule(); - goto repeat; - } - current->state = TASK_RUNNING; - remove_wait_queue(&queue->proc_list, &wait); - } - while (i > 0 && !queue_empty()) { - c = get_from_queue(); - put_user(c, buffer++); - i--; - } - if (count-i) { - file->f_dentry->d_inode->i_atime = CURRENT_TIME; - return count-i; - } - if (signal_pending(current)) - return -ERESTARTSYS; - return 0; -} - -/* - * Write to the aux device. - */ - -static ssize_t write_aux(struct file * file, const char * buffer, - size_t count, loff_t *ppos) -{ - ssize_t retval = 0; - - if (count) { - ssize_t written = 0; - - if (count > 32) - count = 32; /* Limit to 32 bytes. */ - do { - char c; - get_user(c, buffer++); - aux_write_dev(c); - written++; - } while (--count); - retval = -EIO; - if (written) { - retval = written; - file->f_dentry->d_inode->i_mtime = CURRENT_TIME; - } - } - - return retval; -} - -static unsigned int aux_poll(struct file *file, poll_table * wait) -{ - poll_wait(file, &queue->proc_list, wait); - if (!queue_empty()) - return POLLIN | POLLRDNORM; - return 0; -} - -struct file_operations psaux_fops = { - .read = read_aux, - .write = write_aux, - .poll = aux_poll, - .open = open_aux, - .release = release_aux, - .fasync = fasync_aux, -}; - -/* - * Initialize driver. - */ -static struct miscdevice psaux_mouse = { - PSMOUSE_MINOR, "psaux", &psaux_fops -}; - -int __init psaux_init(void) -{ - /* Reset the mouse state machine. */ - iomd_writeb(0, IOMD_MSECTL); - iomd_writeb(8, IOMD_MSECTL); - - queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL); - if (queue == NULL) - return -ENOMEM; - - if (misc_register(&psaux_mouse)) { - kfree(queue); - return -ENODEV; - } - - memset(queue, 0, sizeof(*queue)); - queue->head = queue->tail = 0; - init_waitqueue_head(&queue->proc_list); - - aux_write_ack(AUX_SET_SAMPLE); - aux_write_ack(100); /* 100 samples/sec */ - aux_write_ack(AUX_SET_RES); - aux_write_ack(3); /* 8 counts per mm */ - aux_write_ack(AUX_SET_SCALE21); /* 2:1 scaling */ - - return 0; -} diff -urN orig/drivers/block/Makefile linux/drivers/block/Makefile --- orig/drivers/block/Makefile Mon May 5 17:38:58 2003 +++ linux/drivers/block/Makefile Mon May 5 17:40:28 2003 @@ -30,3 +30,5 @@ obj-$(CONFIG_BLK_DEV_UMEM) += umem.o obj-$(CONFIG_BLK_DEV_NBD) += nbd.o + +obj-$(CONFIG_ARCH_ACORN) += ../acorn/block/ diff -urN orig/drivers/cdrom/cdrom.c linux/drivers/cdrom/cdrom.c --- orig/drivers/cdrom/cdrom.c Mon Mar 24 23:47:15 2003 +++ linux/drivers/cdrom/cdrom.c Mon Mar 24 23:56:22 2003 @@ -246,8 +246,8 @@ #define CD_DVD 0x80 /* Define this to remove _all_ the debugging messages */ -/* #define ERRLOGMASK CD_NOTHING */ -#define ERRLOGMASK CD_WARNING +#define ERRLOGMASK CD_NOTHING +/* #define ERRLOGMASK CD_WARNING */ /* #define ERRLOGMASK (CD_WARNING|CD_OPEN|CD_COUNT_TRACKS|CD_CLOSE) */ /* #define ERRLOGMASK (CD_WARNING|CD_REG_UNREG|CD_DO_IOCTL|CD_OPEN|CD_CLOSE|CD_COUNT_TRACKS) */ diff -urN orig/drivers/char/Kconfig linux/drivers/char/Kconfig --- orig/drivers/char/Kconfig Mon May 5 17:39:00 2003 +++ linux/drivers/char/Kconfig Mon May 5 17:40:39 2003 @@ -437,6 +437,10 @@ source "drivers/serial/Kconfig" +config TOUCHSCREEN_ANAKIN + tristate "Anakin touchscreen support" + depends on ARCH_ANAKIN + config UNIX98_PTYS bool "Unix98 PTY support" ---help--- @@ -587,7 +591,7 @@ depends on PC9800_OLDLP source "drivers/i2c/Kconfig" - +source "drivers/l3/Kconfig" menu "Mice" @@ -844,6 +848,10 @@ If unsure, say N. +config SA1100_RTC + tristate "SA1100 Real Time Clock" + depends on ARCH_SA1100 + config DTLK tristate "Double Talk PC internal speech card support" help diff -urN orig/drivers/char/Makefile linux/drivers/char/Makefile --- orig/drivers/char/Makefile Mon Mar 24 23:47:15 2003 +++ linux/drivers/char/Makefile Mon Mar 24 23:56:23 2003 @@ -54,6 +54,7 @@ obj-$(CONFIG_RTC) += rtc.o obj-$(CONFIG_GEN_RTC) += genrtc.o obj-$(CONFIG_EFI_RTC) += efirtc.o +obj-$(CONFIG_SA1100_RTC) += sa1100-rtc.o ifeq ($(CONFIG_PPC),) obj-$(CONFIG_NVRAM) += nvram.o endif @@ -65,7 +66,6 @@ obj-$(CONFIG_FTAPE) += ftape/ obj-$(CONFIG_H8) += h8.o obj-$(CONFIG_PPDEV) += ppdev.o -obj-$(CONFIG_DZ) += dz.o obj-$(CONFIG_NWBUTTON) += nwbutton.o obj-$(CONFIG_NWFLASH) += nwflash.o obj-$(CONFIG_SCx200_GPIO) += scx200_gpio.o @@ -76,8 +76,10 @@ obj-$(CONFIG_DRM) += drm/ obj-$(CONFIG_PCMCIA) += pcmcia/ obj-$(CONFIG_IPMI_HANDLER) += ipmi/ - + obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o +obj-$(CONFIG_ARCH_ACORN) += ../acorn/char/ + # Files generated that shall be removed upon make clean clean-files := consolemap_deftbl.c defkeymap.c qtronixmap.c diff -urN orig/drivers/char/anakin_ts.c linux/drivers/char/anakin_ts.c --- orig/drivers/char/anakin_ts.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/char/anakin_ts.c Tue Oct 23 17:06:03 2001 @@ -0,0 +1,208 @@ +/* + * linux/drivers/char/anakin_ts.c + * + * Copyright (C) 2001 Aleph One Ltd. for Acunia N.V. + * + * 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. + * + * Changelog: + * 18-Apr-2001 TTC Created + * 23-Oct-2001 dwmw2 Cleanup + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * TSBUF_SIZE must be a power of two + */ +#define ANAKIN_TS_MINOR 16 +#define TSBUF_SIZE 256 +#define NEXT(index) (((index) + 1) & (TSBUF_SIZE - 1)) + +static unsigned short buffer[TSBUF_SIZE][4]; +static int head, tail; +static DECLARE_WAIT_QUEUE_HEAD(queue); +static DECLARE_MUTEX(open_sem); +static spinlock_t tailptr_lock = SPIN_LOCK_UNLOCKED; +static struct fasync_struct *fasync; + +/* + * Interrupt handler and standard file operations + */ +static void +anakin_ts_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned int status = __raw_readl(IO_BASE + IO_CONTROLLER + 0x24); + + /* + * iPAQ format (u16 pressure, x, y, millisecs) + */ + switch (status >> 20 & 3) { + case 0: + return; + case 2: + buffer[head][0] = 0; + break; + default: + buffer[head][0] = 0x7f; + } + + if (unlikely((volatile int)tail == NEXT(head))) { + /* Run out of space in the buffer. Move the tail pointer */ + spin_lock(&tailptr_lock); + + if ((volatile int)tail == NEXT(head)) { + tail = NEXT(NEXT(head)); + } + spin_unlock(&tailptr_lock); + } + + buffer[head][1] = status >> 2 & 0xff; + buffer[head][2] = status >> 12 & 0xff; + buffer[head][3] = jiffies; + mb(); + head = NEXT(head); + + wake_up_interruptible(&queue); + kill_fasync(&fasync, SIGIO, POLL_IN); + +} + +static ssize_t +anakin_ts_read(struct file *filp, char *buf, size_t count, loff_t *l) +{ + unsigned short data[4]; + ssize_t written = 0; + + if (head == tail) { + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + if (wait_event_interruptible(queue, (volatile int)head != (volatile int)tail)) + return -ERESTARTSYS; + } + + while ((volatile int)head != (volatile int)tail && count >= sizeof data) { + /* Copy the data out with the spinlock held, so the + interrupt can't fill the buffer and move the tail + pointer while we're doing it */ + spin_lock_irq(&tailptr_lock); + + memcpy(data, buffer[tail], sizeof data); + tail = NEXT(tail); + + spin_unlock_irq(&tailptr_lock); + + if (copy_to_user(buf, data, sizeof data)) + return -EFAULT; + count -= sizeof data; + buf += sizeof data; + written += sizeof data; + } + return written ? written : -EINVAL; +} + +static unsigned int +anakin_ts_poll(struct file *filp, poll_table *wait) +{ + poll_wait(filp, &queue, wait); + return head != tail ? POLLIN | POLLRDNORM : 0; +} + +static int +anakin_ts_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + /* + * Future ioctl goes here + */ + return 0; +} + +static int +anakin_ts_open(struct inode *inode, struct file *filp) +{ + if (down_trylock(&open_sem)) + return -EBUSY; + return 0; +} + +static int +anakin_ts_fasync(int fd, struct file *filp, int on) +{ + return fasync_helper(fd, filp, on, &fasync); +} + +static int +anakin_ts_release(struct inode *inode, struct file *filp) +{ + anakin_ts_fasync(-1, filp, 0); + up(&open_sem); + return 0; +} + +static struct file_operations anakin_ts_fops = { + owner: THIS_MODULE, + read: anakin_ts_read, + poll: anakin_ts_poll, + ioctl: anakin_ts_ioctl, + open: anakin_ts_open, + release: anakin_ts_release, + fasync: anakin_ts_fasync, +}; + +static struct miscdevice anakin_ts_miscdev = { + ANAKIN_TS_MINOR, + "anakin_ts", + &anakin_ts_fops +}; + +/* + * Initialization and exit routines + */ +int __init +anakin_ts_init(void) +{ + int retval; + + if ((retval = request_irq(IRQ_TOUCHSCREEN, anakin_ts_handler, + SA_INTERRUPT, "anakin_ts", 0))) { + printk(KERN_WARNING "anakin_ts: failed to get IRQ\n"); + return retval; + } + __raw_writel(1, IO_BASE + IO_CONTROLLER + 8); + misc_register(&anakin_ts_miscdev); + + printk(KERN_NOTICE "Anakin touchscreen driver initialised\n"); + + return 0; +} + +void __exit +anakin_ts_exit(void) +{ + __raw_writel(0, IO_BASE + IO_CONTROLLER + 8); + free_irq(IRQ_TOUCHSCREEN, 0); + misc_deregister(&anakin_ts_miscdev); +} + +module_init(anakin_ts_init); +module_exit(anakin_ts_exit); + +MODULE_AUTHOR("Tak-Shing Chan "); +MODULE_DESCRIPTION("Anakin touchscreen driver"); +MODULE_SUPPORTED_DEVICE("touchscreen/anakin"); diff -urN orig/drivers/char/cerf_keyb.c linux/drivers/char/cerf_keyb.c --- orig/drivers/char/cerf_keyb.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/char/cerf_keyb.c Wed Oct 17 16:13:27 2001 @@ -0,0 +1,380 @@ +/* + cerf_keyb.c: This is the end. Daniel is writing a device driver!!! +*/ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define KBD_REPORT_UNKN + +#define KBD_REPORT_ERR /* Report keyboard errors */ +#define KBD_REPORT_UNKN /* Report unknown scan codes */ +#define KBD_REPORT_TIMEOUTS /* Report keyboard timeouts */ +#define KBD_NO_DATA (-1) /* No data */ +#define KBD_REPEAT_START (0x20) +#define KBD_REPEAT_CONTINUE (0x05) +#define KBD_KEY_DOWN_MAX (0x10) +#define UINT_LEN (20) +#define SC_LIM (69) +#define KBD_ROWS (5) +#define KBD_COLUMNS (8) + +#define KBD_KEYUP (0x80) +#define KBD_MODESCAN (0x7f) +#define KBD_CAPSSCAN (0x3a) +#define KBD_SHIFTSCAN (0x2a) +#define KBD_NUMCURSCAN (0x7c) +#define KBD_CTRLSCAN (0x1d) +#define KBD_ALTSCAN (0x38) + +#define KBD_UP_OFF (0) +#define KBD_UP_ON (1) +#define KBD_DOWN (2) +#define KBD_DOWN_HOLD (3) + + + +static unsigned char handle_kbd_event(void); +static unsigned char kbd_read_input(void); +static void column_set(unsigned int column); +static int scancodes(unsigned char codeval[KBD_ROWS][KBD_COLUMNS]); + +static spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED; +static struct timer_list kbd_timer; + +static short mode_ena = 0; +static short numcur_ena = 0; +static short shift_ena = 0; + +#define E0_KPENTER 96 +#define E0_RCTRL 97 +#define E0_KPSLASH 98 +#define E0_PRSCR 99 +#define E0_RALT 100 +#define E0_BREAK 101 /* (control-pause) */ +#define E0_HOME 102 +#define E0_UP 103 +#define E0_PGUP 104 +#define E0_LEFT 105 +#define E0_RIGHT 106 +#define E0_END 107 +#define E0_DOWN 108 +#define E0_PGDN 109 +#define E0_INS 110 +#define E0_DEL 111 +#define E1_PAUSE 119 +#define E0_MACRO 112 +#define E0_F13 113 +#define E0_F14 114 +#define E0_HELP 115 +#define E0_DO 116 +#define E0_F17 117 +#define E0_KPMINPLUS 118 +#define E0_OK 124 +#define E0_MSLW 125 +#define E0_MSRW 126 +#define E0_MSTM 127 + +static unsigned char e0_keys[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */ + 0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */ + 0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */ + E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */ + E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */ + E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */ + E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */ + 0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0, /* 0x58-0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ + 0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */ + 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */ +}; + +static unsigned char cerf_normal_map[KBD_ROWS][KBD_COLUMNS] = { + {KBD_ALTSCAN, KBD_MODESCAN, 0x1e, 0x30, 0x2e, 0x20, 0x00, 0x00}, + {0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x00}, + {0x26, 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x00}, + {0x1f, 0x14, 0x16, 0x2f, 0x11, 0x2d, 0x15, 0x00}, + {0x2c, KBD_SHIFTSCAN, KBD_CTRLSCAN, 0x39, KBD_NUMCURSCAN, 0x2b, 0x1c, 0x00} +}; + +static unsigned char cerf_mode_map[KBD_ROWS][KBD_COLUMNS] = { + {0x00, 0x00, 0x02, 0x03, 0x04, 0x05, 0x00, 0x00}, + {0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x00, 0x00}, // + {0x0d, 0x0c, 0x37, 0x35, 0x0d, 0x48, 0x28, 0x00}, + {0x01, 0x33, 0x34, 0x00, 0x4b, 0x27, 0x4d, 0x00}, // + {0x0f, 0x00, KBD_CAPSSCAN, 0x0e, 0x00, 0x50, 0x00, 0x00} +}; + +static unsigned char cerf_numcur_map[KBD_ROWS][KBD_COLUMNS] = { + {0x00, 0x00, 0x02, 0x03, 0x04, 0x05, 0x00, 0x00}, + {0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x00, 0x00}, + {0x0d, 0x0c, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x4b, 0x00, 0x4d, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00} +}; + +static void column_set(unsigned int column) +{ + if (column < 0) + { + CERF_PDA_CPLD_UnSet(CERF_PDA_CPLD_KEYPAD_A, 0xFF, 0xFF); + CERF_PDA_CPLD_UnSet(CERF_PDA_CPLD_KEYPAD_B, 0xFF, 0xFF); + } + else + { + if(column < 4) + { + CERF_PDA_CPLD_Set(CERF_PDA_CPLD_KEYPAD_A, 1 << (column % 4), 0xFF); + CERF_PDA_CPLD_UnSet(CERF_PDA_CPLD_KEYPAD_B, 0xFF, 0xFF); + } + else + { + CERF_PDA_CPLD_UnSet(CERF_PDA_CPLD_KEYPAD_A, 0xFF, 0xFF); + CERF_PDA_CPLD_Set(CERF_PDA_CPLD_KEYPAD_B, 1 << (column % 4), 0xFF); + } + } +} + +static int scancodes(unsigned char codeval[KBD_ROWS][KBD_COLUMNS]) +{ + int i, j; + + for(i = 0; i < KBD_COLUMNS; i++) + { + column_set(i); + udelay(50); + for(j = 0; j < KBD_ROWS; j++) + { + if(mode_ena) + codeval[j][i] = (GPLR & (1 << (20 + j)))?(cerf_mode_map[j][i]?cerf_mode_map[j][i]:cerf_normal_map[j][i]):0; + else if(numcur_ena) + codeval[j][i] = (GPLR & (1 << (20 + j)))?(cerf_numcur_map[j][i]?cerf_numcur_map[j][i]:cerf_normal_map[j][i]):0; + else + codeval[j][i] = (GPLR & (1 << (20 + j)))?cerf_normal_map[j][i]:0; + } + } + column_set(-1); + + return 0; +} + +static unsigned char kbd_read_input(void) +{ + int i, j, k, l; + unsigned char prev; + static unsigned char count = 0; + + static unsigned char oldcodes[KBD_ROWS][KBD_COLUMNS]={{0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0}, + {0,0,0,0,0,0,0,0}}; + unsigned char inputcode[KBD_ROWS][KBD_COLUMNS]; + + memset(inputcode, 0, sizeof(unsigned char) * (KBD_ROWS * KBD_COLUMNS)); + scancodes(inputcode); + + for(i = 0; i < KBD_COLUMNS; i++) + { + for(j = 0; j < KBD_ROWS; j++) + { +// if(oldcodes[j][i] == 0xe0) +// oldcodes[j][i] = + if(oldcodes[j][i] != inputcode[j][i]) + { + // Value of the key before entering this function + prev = oldcodes[j][i]; + + // KEYUP + if(inputcode[j][i] == 0 && oldcodes[j][i] != 0 && !(oldcodes[j][i] & KBD_KEYUP)) + { + oldcodes[j][i] |= KBD_KEYUP; + + if(mode_ena == KBD_UP_ON) + mode_ena = KBD_UP_OFF; + if(prev == KBD_MODESCAN) + if(mode_ena == KBD_DOWN_HOLD) + mode_ena = KBD_UP_OFF; + else if(mode_ena == KBD_DOWN) + mode_ena = KBD_UP_ON; + if(mode_ena == KBD_DOWN) + mode_ena = KBD_DOWN_HOLD; + } + // RESET KEYUP + else if(oldcodes[j][i] & KBD_KEYUP) + oldcodes[j][i] = 0; + // KEY DOWN + else + { + oldcodes[j][i] = inputcode[j][i]; + + // Parse out mode modifiers before the keyboard interpreter can touch them + if(inputcode[j][i] == KBD_MODESCAN) + { + if(!mode_ena) + mode_ena = KBD_DOWN; + continue; + } + if(inputcode[j][i] == KBD_NUMCURSCAN) + { + numcur_ena = numcur_ena?0:1; + continue; + } + } + //printk("Modified: (%#x,%#x), ipv:%#x, To: (%#.2x), From: (%#.2x), Flags:%d,%d,%d\r\n", j, i, inputcode[j][i], oldcodes[j][i], prev, mode_ena, shift_ena, numcur_ena); + return oldcodes[j][i]; + } + } + } + + return (unsigned char)(KBD_NO_DATA); +} + +int cerf_kbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode) +{ + static int prev_scancode; + + if (scancode == 0xe0 || scancode == 0xe1) { + prev_scancode = scancode; + return 0; + } + + if (scancode == 0x00 || scancode == 0xff) { + prev_scancode = 0; + return 0; + } + + scancode &= 0x7f; + + if (prev_scancode) { + if (prev_scancode != 0xe0) { + if (prev_scancode == 0xe1 && scancode == 0x1d) { + prev_scancode = 0x100; + return 0; + } else if (prev_scancode == 0x100 && scancode == 0x45) { + prev_scancode = 0; + } else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown e1 escape sequence\n"); +#endif + prev_scancode = 0; + return 0; + } + } else { + prev_scancode = 0; + if (scancode == 0x2a || scancode == 0x36) + return 0; + else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown scancode e0 %02x\n", + scancode); +#endif + return 0; + } + } + } else + *keycode = scancode; + return 1; +} + +static inline void handle_keyboard_event(unsigned char scancode) +{ + if(scancode != (unsigned char)(KBD_NO_DATA)) + { +#ifdef CONFIG_VT + handle_scancode(scancode, !(scancode & KBD_KEYUP)); +#endif + tasklet_schedule(&keyboard_tasklet); + } +} + +static unsigned char handle_kbd_event(void) +{ + unsigned char scancode; + + scancode = kbd_read_input(); + handle_keyboard_event(scancode); + + return 0; +} + +/* Handle the automatic interrupts handled by the timer */ +static void keyboard_interrupt(unsigned long foo) +{ + spin_lock_irq(&kbd_controller_lock); + handle_kbd_event(); + spin_unlock_irq(&kbd_controller_lock); + + kbd_timer.expires = 8 + jiffies; + kbd_timer.data = 0x00000000; + kbd_timer.function = (void(*)(unsigned long))&keyboard_interrupt; + + add_timer(&kbd_timer); +} + +void cerf_leds(unsigned char leds) +{ +} +char cerf_unexpected_up(unsigned char keycode) +{ +return 0; +} +int cerf_getkeycode(unsigned int scancode) +{ +return 0; +} +int cerf_setkeycode(unsigned int scancode, unsigned int keycode) +{ +return 0; +} + +void cerf_kbd_init_hw(void) +{ + printk("Starting Cerf PDA Keyboard Driver... "); + + k_setkeycode = cerf_setkeycode; + k_getkeycode = cerf_getkeycode; + k_translate = cerf_kbd_translate; + k_unexpected_up = cerf_unexpected_up; + k_leds = cerf_leds; + + GPDR &= ~(GPIO_GPIO(20) | GPIO_GPIO(21) | GPIO_GPIO(22) | GPIO_GPIO(23) | GPIO_GPIO(24)); + kbd_timer.expires = 40 + jiffies; + kbd_timer.data = 0x00000000; + kbd_timer.function = (void(*)(unsigned long))&keyboard_interrupt; + + add_timer(&kbd_timer); + + printk("Done\r\n"); +} diff -urN orig/drivers/char/clps711x_keyb.c linux/drivers/char/clps711x_keyb.c --- orig/drivers/char/clps711x_keyb.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/char/clps711x_keyb.c Thu Nov 29 20:15:01 2001 @@ -0,0 +1,547 @@ +/* + * drivers/char/clps711x_keyb.c + * + * Copyright (C) 2001 Thomas Gleixner + * + * based on drivers/edb7211_keyb.c, which is copyright (C) 2000 Bluemug Inc. + * + * Keyboard driver for ARM Linux on EP7xxx and CS89712 processors + * + * 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. See the file COPYING + * in the main directory of this archive for more details. + * + * + * Hardware: + * + * matrix scan keyboards based on EP7209,7211,7212,7312 and CS89712 + * on chip keyboard scanner. + * Adaption for different machines is done in init function. + * + * Basic Function: + * + * Basicly the driver is interrupt driven. It sets all column drivers + * high. If any key is pressed, a interrupt occures. Now a seperate scan of + * each column is done. This scan is timer based, because we use a keyboard + * interface with decoupling capacitors (neccecary if you want to survive + * EMC compliance tests). Always one line is set high. When next timer event + * occures the scan data on port A are valid. This makes also sure, that no + * spurious keys are scanned. The kbd int on these CPU's is not deglitched! + * After scanning all columns, we switch back to int mode, if no key is + * pressed. If any is pressed we reschedule the scan within a programmable + * delay. If we would switch back to interrupt mode as long as a key is pressed, + * we come right back to the interrupt, because the int. is level triggered ! + * The timer based scan of the seperate columns can also be done in one + * timer event (set fastscan to 1). + * + * Summary: + * The design of this keyboard controller chip is stupid at all ! + * + * Matrix translation: + * The matrix translation table is based on standard XT scancodes. Maybe + * you have to adjust the KEYISPRINTABLE macro if you set other codes. + * + * HandyKey: + * + * On small matrix keyboards you don't have enough keys for operation. + * The intention was to implement a operation mode as it's used on handys. + * You can rotate trough four scancode levels and produce e.g. with a 4x3 + * matrix 4*3*4 = 48 different keycodes. That's basicly enough for editing + * filenames or things like that. The HandyKey function takes care about + * nonprintable keys like cursors, backspace, del ... + * If a key is pressed and is a printable keycode, the code is put to the + * main keyboard handler and a cursor left is applied. If you press the same + * key again, the current character is deleted and the next level character + * is applied. (e.g. 1, a, b, c, 1 ....). If you press a different key, the + * driver applies cursor right, before processing the new key. + * The autocomplete feature moves the cursor right, if you do not press a + * key within a programmable time. + * If HandyKey is off, the keyboard behaviour is that of a standard keyboard + * HandyKey can be en/disabled from userspace with the proc/keyboard entry + * + * proc/keyboard: + * + * Read access gives back the actual state of the HandyKey function + * h:0 Disabled + * h:1 Enabled + * Write access has two functions. Changing the HandyKey mode and applying + * a different scancode translation table. + * Syntax is: h:0 disable Handykey + * h:1 enabled Handykey + * t:array[256] of bytes Transfer translation table + */ + +#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 + +void clps711x_kbd_init_hw(void); + +/* + * Values for the keyboard column scan control register. + */ +#define KBSC_HI 0x0 /* All driven high */ +#define KBSC_LO 0x1 /* All driven low */ +#define KBSC_X 0x2 /* All high impedance */ +#define KBSC_COL0 0x8 /* Column 0 high, others high impedance */ +#define KBSC_COL1 0x9 /* Column 1 high, others high impedance */ +#define KBSC_COL2 0xa /* Column 2 high, others high impedance */ +#define KBSC_COL3 0xb /* Column 3 high, others high impedance */ +#define KBSC_COL4 0xc /* Column 4 high, others high impedance */ +#define KBSC_COL5 0xd /* Column 5 high, others high impedance */ +#define KBSC_COL6 0xe /* Column 6 high, others high impedance */ +#define KBSC_COL7 0xf /* Column 7 high, others high impedance */ + +/* +* Keycodes for cursor left/right and delete (used by HandyKey) +*/ +#define KEYCODE_CLEFT 0x4b +#define KEYCODE_CRIGHT 0x4d +#define KEYCODE_DEL 0x53 +#define KEYISPRINTABLE(code) ( (code > 0x01 && code < 0x37 && code != 0x1c \ + && code != 0x0e) || code == 0x39) + +/* Simple translation table for the SysRq keys */ +#ifdef CONFIG_MAGIC_SYSRQ +unsigned char clps711x_kbd_sysrq_xlate[128] = + "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ + "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ + "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ + "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ + "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ + "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ + "\r\000/"; /* 0x60 - 0x6f */ +#endif + +/* + * This table maps row/column keyboard matrix positions to XT scancodes. + * It's a default table, which can be overriden by writing to proc/keyboard + */ +#ifdef CONFIG_ARCH_AUTCPU12 +static unsigned char autcpu12_scancode[256] = +{ +/* Column: + Row 0 1 2 3 4 5 6 7 */ +/* A0 */ 0x08, 0x09, 0x0a, 0x0e, 0x05, 0x06, 0x00, 0x00, +/* A1 */ 0x07, 0x53, 0x02, 0x03, 0x04, 0x0f, 0x00, 0x00, +/* A2 */ 0x0c, 0x0b, 0x33, 0x1c, 0xff, 0x4b, 0x00, 0x00, +/* A3 */ 0x48, 0x50, 0x4d, 0x3b, 0x3c, 0x3d, 0x00, 0x00, +/* A4 */ 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A5 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A6 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + +/* A0 */ 0x1e, 0x20, 0x22, 0x0e, 0x24, 0x32, 0x00, 0x00, +/* A1 */ 0x19, 0x53, 0x1f, 0x2f, 0x15, 0x0f, 0x00, 0x00, +/* A2 */ 0x0c, 0x39, 0x34, 0x1c, 0xff, 0x4b, 0x00, 0x00, +/* A3 */ 0x48, 0x50, 0x4d, 0x3b, 0x3c, 0x3d, 0x00, 0x00, +/* A4 */ 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A5 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A6 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + +/* A0 */ 0x30, 0x12, 0x23, 0x0e, 0x25, 0x31, 0x00, 0x00, +/* A1 */ 0x10, 0x53, 0x14, 0x11, 0x2c, 0x0f, 0x00, 0x00, +/* A2 */ 0x0c, 0x0b, 0x27, 0x1c, 0xff, 0x4b, 0x00, 0x00, +/* A3 */ 0x48, 0x50, 0x4d, 0x3b, 0x3c, 0x3d, 0x00, 0x00, +/* A4 */ 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A5 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A6 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + +/* A0 */ 0x2e, 0x21, 0x17, 0x0e, 0x26, 0x18, 0x00, 0x00, +/* A1 */ 0x13, 0x53, 0x16, 0x2D, 0x04, 0x0f, 0x00, 0x00, +/* A2 */ 0x0c, 0x39, 0x35, 0x1c, 0xff, 0x4b, 0x00, 0x00, +/* A3 */ 0x48, 0x50, 0x4d, 0x3b, 0x3c, 0x3d, 0x00, 0x00, +/* A4 */ 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A5 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A6 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* A7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +#endif + +static int keys[8]; +static int new_keys[8]; +static int previous_keys[8]; + +static int fastscan; +static int scan_interval; +static int scan_delay; +static int last_column; +static int key_is_pressed; + +static unsigned char *act_scancode; + +static struct kbd_handy_key { + int ena; + int code; + int shift; + int autocomplete; + unsigned long expires; + unsigned long delay; + unsigned char left; + unsigned char right; + unsigned char del; +} khandy; + +static struct tq_struct kbd_process_task; +static struct timer_list clps711x_kbd_timer; +static struct timer_list clps711x_kbdhandy_timer; +static struct proc_dir_entry *clps711x_keyboard_proc_entry = NULL; + +/* + * Translate a raw keycode to an XT keyboard scancode. + */ +static int clps711x_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode) +{ + *keycode = act_scancode[scancode]; + return 1; +} + +/* +* Initialize handykey structure +* clear code, clear shift +* scan scancode for cursor right/left and delete +*/ +static void clps711x_handykey_init(void) { + + int i; + + khandy.ena = 0; + khandy.code = 0; + khandy.shift = 0; + khandy.autocomplete = 0; + for(i = 0; i < 64; i++) { + switch(act_scancode[i]) { + case KEYCODE_CLEFT: khandy.left = i; break; + case KEYCODE_CRIGHT: khandy.right = i; break; + case KEYCODE_DEL: khandy.del = i; break; + } + } +} + +/* +* Check for handy key and process it +*/ +void inline clps711x_checkhandy(int col, int row) { + + int scode, down; + unsigned char kcode; + + scode = (row<<3) + col; + down = keys[col]>>row & 0x01; + kcode = act_scancode[scode]; + + if (!khandy.ena) { + if (khandy.code) { + handle_scancode(khandy.right,1); + handle_scancode(khandy.right,0); + } + khandy.code = 0; + khandy.shift = 0; + khandy.autocomplete = 0; + } + + if(!kcode) + return; + + if (!down || !khandy.ena) { + if (khandy.ena && KEYISPRINTABLE(act_scancode[scode])) + khandy.autocomplete = 1; + else + handle_scancode(scode + khandy.shift, down); + return; + } + + khandy.autocomplete = 0; + if (KEYISPRINTABLE(kcode)) { + if (khandy.code) { + if(khandy.code == (scode|0x100)) { + handle_scancode(khandy.del,1); + handle_scancode(khandy.del,0); + khandy.shift = khandy.shift < 3*64 ? khandy.shift + 64 : 0 ; + } else { + handle_scancode(khandy.right,1); + handle_scancode(khandy.right,0); + khandy.shift = 0; + } + } + handle_scancode(scode + khandy.shift, 1); + handle_scancode(scode + khandy.shift, 0); + khandy.code = scode | 0x100; + handle_scancode(khandy.left,1); + handle_scancode(khandy.left,0); + } else { + if (khandy.code) { + khandy.code = 0; + handle_scancode(khandy.right,1); + handle_scancode(khandy.right,0); + } + khandy.shift = 0; + handle_scancode(scode, down); + } +} + + +/* + * Process the new key data + */ +static void clps711x_kbd_process(void* data) +{ + int col,row,res; + + for (col = 0; col < 8; col++) { + if (( res = previous_keys[col] ^ keys[col]) == 0) + continue; + for(row = 0; row < 8; row++) { + if ( ((res >> row) & 0x01) != 0) + clps711x_checkhandy(col,row); + } + } + /* Update the state variables. */ + memcpy(previous_keys, keys, 8 * sizeof(int)); + + /* reschedule, if autocomplete pending */ + if (khandy.autocomplete) { + khandy.expires = jiffies + khandy.delay; + mod_timer(&clps711x_kbdhandy_timer,khandy.expires); + } + +} + +static char clps711x_unexpected_up(unsigned char scancode) +{ + return 0200; +} + +/* +* Handle timer event, for autocomplete function +* Reschedule keyboard process task +*/ +static void clps711x_kbdhandy_timeout(unsigned long data) +{ + if(khandy.autocomplete) { + khandy.code = 0; + khandy.shift = 0; + khandy.autocomplete = 0; + handle_scancode(khandy.right,1); + handle_scancode(khandy.right,0); + } +} + +/* +* Handle timer event, while in pollmode +*/ +static void clps711x_kbd_timeout(unsigned long data) +{ + int i; + unsigned long flags; + /* + * read bits of actual column or all columns in fastscan-mode + */ + for (i = 0; i < 8; i++) { + new_keys[last_column - KBSC_COL0] = clps_readb(PADR) & 0xff; + key_is_pressed |= new_keys[last_column - KBSC_COL0]; + last_column = last_column < KBSC_COL7 ? last_column + 1 : KBSC_COL0; + local_irq_save(flags); + clps_writel( (clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) + | last_column, SYSCON1); + local_irq_restore(flags); + /* + * For fastscan, apply a short delay to settle scanlines + * else break and wait for next timeout + */ + if (fastscan) + udelay(5); + else + break; + } + + if (key_is_pressed) + khandy.autocomplete = 0; + + /* + * switch to interupt mode, if all columns scanned and no key pressed + * else reschedule scan + */ + if (last_column == KBSC_COL0) { + if (!key_is_pressed) { + local_irq_save(flags); + clps_writel( (clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) + | KBSC_HI, SYSCON1); + local_irq_restore(flags); + clps_writel(0,KBDEOI); + enable_irq(IRQ_KBDINT); + } else { + clps711x_kbd_timer.expires = jiffies + scan_interval; + add_timer(&clps711x_kbd_timer); + } + key_is_pressed = 0; + memcpy(keys, new_keys, 8 * sizeof(int)); + for (i = 0; i < 8; i++) { + if (previous_keys[i] != keys[i]) { + queue_task(&kbd_process_task, &tq_timer); + return; + } + } + } else { + clps711x_kbd_timer.expires = jiffies + scan_delay; + add_timer(&clps711x_kbd_timer); + } +} + +/* +* Keyboard interrupt, change to scheduling mode +*/ +static void clps711x_kbd_int(int irq, void *dev_id, struct pt_regs *regs) +{ + +#ifdef CONFIG_VT + kbd_pt_regs = regs; +#endif + disable_irq(IRQ_KBDINT); + khandy.autocomplete = 0; + clps_writel( (clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) + | KBSC_COL0, SYSCON1); + clps711x_kbd_timer.expires = jiffies + scan_delay; + add_timer(&clps711x_kbd_timer); +} + + +static int clps711x_kbd_proc_keyboard_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + if (count < 2) + return -EINVAL; + + return sprintf(page,"h:%d\n",khandy.ena); +} + +static int clps711x_kbd_proc_keyboard_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + unsigned char buf[260]; + + if (count < 3|| count > 258) + return -EINVAL; + if (copy_from_user(buf, buffer, count)) + return -EFAULT; + if (buf[1] != ':') + return -EINVAL; + + if (buf[0] == 'h') { + switch (buf[2]) { + case '0': + case '1': + case '2': khandy.ena = buf[2]-'0'; return count; + } + } + + if (buf[0] == 't' && count == 258) { + memcpy(act_scancode,buf+2,256); + /* rescan cursor left/right and del */ + clps711x_handykey_init(); + return count; + } + + return -EINVAL; +} + + +/* + * Initialize the keyboard hardware. + * Set all columns high + * Install interrupt handler + * + * Machine dependent parameters: + * + * fastscan: 0 = timer based scan for each column + * 1 = full scan is done in one timer event + * scan_delay: time between column scans + * setup even if you use fastscan (leeds to timer mode) + * scan_interval: time between full scans + * handy.delay: timeout before last entry get's automatically valid + * + */ +void __init clps711x_kbd_init_hw(void) +{ + + /* + * put here machine dependent init stuff + */ + if (machine_is_autcpu12()) { + fastscan = 0; + scan_interval = 50*HZ/1000; + scan_delay = 20*HZ/1000; + khandy.delay = 750*HZ/1000; + act_scancode = autcpu12_scancode; + } else { + printk("No initialization, keyboard killed\n"); + return; + } + + last_column = KBSC_COL0; + key_is_pressed = 0; + + clps711x_handykey_init(); + + /* Register the /proc entry */ + clps711x_keyboard_proc_entry = create_proc_entry("keyboard", 0444, + &proc_root); + if (clps711x_keyboard_proc_entry == NULL) + printk("Couldn't create the /proc entry for the keyboard\n"); + else { + clps711x_keyboard_proc_entry->read_proc = + &clps711x_kbd_proc_keyboard_read; + clps711x_keyboard_proc_entry->write_proc = + &clps711x_kbd_proc_keyboard_write; + } + + /* Initialize the matrix processing task. */ + k_translate = clps711x_translate; + k_unexpected_up = clps711x_unexpected_up; + kbd_process_task.routine = clps711x_kbd_process; + kbd_process_task.data = 0; + + /* Setup the timer for keyboard polling, after kbd int */ + init_timer(&clps711x_kbd_timer); + clps711x_kbd_timer.function = clps711x_kbd_timeout; + clps711x_kbd_timer.data = 0; + init_timer(&clps711x_kbdhandy_timer); + clps711x_kbdhandy_timer.function = clps711x_kbdhandy_timeout; + clps711x_kbdhandy_timer.data = 1; + + /* Initialise scan hardware, request int */ + clps_writel( (clps_readl(SYSCON1) & ~SYSCON1_KBDSCANMASK) + | KBSC_HI, SYSCON1); + request_irq(IRQ_KBDINT, clps711x_kbd_int, 0,"keyboard", NULL); + + printk("clps711x keyboard init done\n"); + +} diff -urN orig/drivers/char/dz.c linux/drivers/char/dz.c --- orig/drivers/char/dz.c Tue May 27 10:04:31 2003 +++ linux/drivers/char/dz.c Thu Jan 1 01:00:00 1970 @@ -1,1619 +0,0 @@ -/* - * dz.c: Serial port driver for DECStations equiped - * with the DZ chipset. - * - * Copyright (C) 1998 Olivier A. D. Lebaillif - * - * Email: olivier.lebaillif@ifrsys.com - * - * [31-AUG-98] triemer - * Changed IRQ to use Harald's dec internals interrupts.h - * removed base_addr code - moving address assignment to setup.c - * Changed name of dz_init to rs_init to be consistent with tc code - * [13-NOV-98] triemer fixed code to receive characters - * after patches by harald to irq code. - * [09-JAN-99] triemer minor fix for schedule - due to removal of timeout - * field from "current" - somewhere between 2.1.121 and 2.1.131 -Qua Jun 27 15:02:26 BRT 2001 - * [27-JUN-2001] Arnaldo Carvalho de Melo - cleanups - * - * Parts (C) 1999 David Airlie, airlied@linux.ie - * [07-SEP-99] Bugfixes - */ - -/* #define DEBUG_DZ 1 */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* for definition of SERIAL */ - -/* for definition of struct console */ -#ifdef CONFIG_SERIAL_CONSOLE -#define CONSOLE_LINE (3) -#endif /* ifdef CONFIG_SERIAL_CONSOLE */ -#if defined(CONFIG_SERIAL_CONSOLE) || defined(DEBUG_DZ) -#include -#endif /* if defined(CONFIG_SERIAL_CONSOLE) || defined(DEBUG_DZ) */ - -#include -#include - -#include -#include -#include -#include -#include - -#ifdef DEBUG_DZ -#include -#include -#include - -extern int (*prom_printf) (char *,...); -#endif - - - -#include "dz.h" - -#define DZ_INTR_DEBUG 1 - -DECLARE_TASK_QUEUE(tq_serial); - -static struct dz_serial *lines[4]; -static unsigned char tmp_buffer[256]; - - - -#ifdef DEBUG_DZ -/* - * debugging code to send out chars via prom - */ -static void debug_console( const char *s,int count) -{ - unsigned i; - - for (i = 0; i < count; i++) { - if (*s == 10) - prom_printf("%c", 13); - prom_printf("%c", *s++); - } -} -#endif - -/* - * ------------------------------------------------------------ - * dz_in () and dz_out () - * - * These routines are used to access the registers of the DZ - * chip, hiding relocation differences between implementation. - * ------------------------------------------------------------ - */ - -static inline unsigned short dz_in (struct dz_serial *info, unsigned offset) -{ - volatile u16 *addr = (volatile u16 *)(info->port + offset); - - return *addr; -} - -static inline void dz_out (struct dz_serial *info, unsigned offset, - unsigned short value) -{ - volatile u16 *addr = (volatile u16 *)(info->port + offset); - *addr = value; -} - -/* - * ------------------------------------------------------------ - * rs_stop () and rs_start () - * - * These routines are called before setting or resetting - * tty->stopped. They enable or disable transmitter interrupts, - * as necessary. - * ------------------------------------------------------------ - */ - -static void dz_stop (struct tty_struct *tty) -{ - struct dz_serial *info; - unsigned short mask, tmp; - - if (!tty) - return; - - info = (struct dz_serial *)tty->driver_data; - - mask = 1 << info->line; - tmp = dz_in (info, DZ_TCR); /* read the TX flag */ - - tmp &= ~mask; /* clear the TX flag */ - dz_out (info, DZ_TCR, tmp); -} - -static void dz_start (struct tty_struct *tty) -{ - struct dz_serial *info = (struct dz_serial *)tty->driver_data; - unsigned short mask, tmp; - - mask = 1 << info->line; - tmp = dz_in (info, DZ_TCR); /* read the TX flag */ - - tmp |= mask; /* set the TX flag */ - dz_out (info, DZ_TCR, tmp); -} - -/* - * ------------------------------------------------------------ - * Here starts the interrupt handling routines. All of the - * following subroutines are declared as inline and are folded - * into dz_interrupt. They were separated out for readability's - * sake. - * - * Note: rs_interrupt() is a "fast" interrupt, which means that it - * runs with interrupts turned off. People who may want to modify - * rs_interrupt() should try to keep the interrupt handler as fast as - * possible. After you are done making modifications, it is not a bad - * idea to do: - * - * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer dz.c - * - * and look at the resulting assemble code in serial.s. - * - * ------------------------------------------------------------ - */ - -/* - * ------------------------------------------------------------ - * dz_sched_event () - * - * This routine is used by the interrupt handler to schedule - * processing in the software interrupt portion of the driver. - * ------------------------------------------------------------ - */ -static inline void dz_sched_event (struct dz_serial *info, int event) -{ - info->event |= 1 << event; - queue_task(&info->tqueue, &tq_serial); - mark_bh(SERIAL_BH); -} - -/* - * ------------------------------------------------------------ - * receive_char () - * - * This routine deals with inputs from any lines. - * ------------------------------------------------------------ - */ -static inline void receive_chars (struct dz_serial *info_in) -{ - struct dz_serial *info; - struct tty_struct *tty = 0; - struct async_icount *icount; - int ignore = 0; - unsigned short status, tmp; - unsigned char ch; - - /* - * This code is going to be a problem... the call to tty_flip_buffer - * is going to need to be rethought... - */ - do { - status = dz_in (info_in, DZ_RBUF); - info = lines[LINE(status)]; - - /* punt so we don't get duplicate characters */ - if (!(status & DZ_DVAL)) - goto ignore_char; - - ch = UCHAR(status); /* grab the char */ - -#if 0 - if (info->is_console) { - if (ch == 0) - return; /* it's a break ... */ - } -#endif - - tty = info->tty; /* now tty points to the proper dev */ - icount = &info->icount; - - if (!tty) - break; - if (tty->flip.count >= TTY_FLIPBUF_SIZE) break; - - *tty->flip.char_buf_ptr = ch; - *tty->flip.flag_buf_ptr = 0; - icount->rx++; - - /* keep track of the statistics */ - if (status & (DZ_OERR | DZ_FERR | DZ_PERR)) { - if (status & DZ_PERR) /* parity error */ - icount->parity++; - else if (status & DZ_FERR) /* frame error */ - icount->frame++; - if (status & DZ_OERR) /* overrun error */ - icount->overrun++; - - /* - * Check to see if we should ignore the character and - * mask off conditions that should be ignored - */ - - if (status & info->ignore_status_mask) { - if (++ignore > 100) - break; - goto ignore_char; - } - - /* mask off the error conditions we want to ignore */ - tmp = status & info->read_status_mask; - - if (tmp & DZ_PERR) { - *tty->flip.flag_buf_ptr = TTY_PARITY; -#ifdef DEBUG_DZ - debug_console("PERR\n",5); -#endif /* DEBUG_DZ */ - } else if (tmp & DZ_FERR) { - *tty->flip.flag_buf_ptr = TTY_FRAME; -#ifdef DEBUG_DZ - debug_console("FERR\n",5); -#endif /* DEBUG_DZ */ - } if (tmp & DZ_OERR) { -#ifdef DEBUG_DZ - debug_console("OERR\n",5); -#endif /* DEBUG_DZ */ - if (tty->flip.count < TTY_FLIPBUF_SIZE) { - tty->flip.count++; - tty->flip.flag_buf_ptr++; - tty->flip.char_buf_ptr++; - *tty->flip.flag_buf_ptr = TTY_OVERRUN; - } - } - } - tty->flip.flag_buf_ptr++; - tty->flip.char_buf_ptr++; - tty->flip.count++; -ignore_char: - ; - } while (status & DZ_DVAL); - - if (tty) - tty_flip_buffer_push(tty); -} - -/* - * ------------------------------------------------------------ - * transmit_char () - * - * This routine deals with outputs to any lines. - * ------------------------------------------------------------ - */ -static inline void transmit_chars (struct dz_serial *info) -{ - unsigned char tmp; - - if (info->x_char) { /* XON/XOFF chars */ - dz_out(info, DZ_TDR, info->x_char); - info->icount.tx++; - info->x_char = 0; - return; - } - - /* if nothing to do or stopped or hardware stopped */ - if ((info->xmit_cnt <= 0) || info->tty->stopped || - info->tty->hw_stopped) { - dz_stop(info->tty); - return; - } - - /* - * If something to do ... (rember the dz has no output fifo so we go - * one char at a time :-< - */ - tmp = (unsigned short) info->xmit_buf[info->xmit_tail++]; - dz_out(info, DZ_TDR, tmp); - info->xmit_tail = info->xmit_tail & (DZ_XMIT_SIZE - 1); - info->icount.tx++; - - if (--info->xmit_cnt < WAKEUP_CHARS) - dz_sched_event(info, DZ_EVENT_WRITE_WAKEUP); - - /* Are we done */ - if (info->xmit_cnt <= 0) - dz_stop(info->tty); -} - -/* - * ------------------------------------------------------------ - * check_modem_status () - * - * Only valid for the MODEM line duh ! - * ------------------------------------------------------------ - */ -static inline void check_modem_status (struct dz_serial *info) -{ - unsigned short status; - - /* if not ne modem line just return */ - if (info->line != DZ_MODEM) - return; - - status = dz_in(info, DZ_MSR); - - /* it's easy, since DSR2 is the only bit in the register */ - if (status) - info->icount.dsr++; -} - -/* - * ------------------------------------------------------------ - * dz_interrupt () - * - * this is the main interrupt routine for the DZ chip. - * It deals with the multiple ports. - * ------------------------------------------------------------ - */ -static void dz_interrupt (int irq, void *dev, struct pt_regs *regs) -{ - struct dz_serial *info; - unsigned short status; - - /* get the reason why we just got an irq */ - status = dz_in((struct dz_serial *)dev, DZ_CSR); - info = lines[LINE(status)]; /* re-arrange info the proper port */ - - if (status & DZ_RDONE) - receive_chars(info); /* the receive function */ - - if (status & DZ_TRDY) - transmit_chars (info); -} - -/* - * ------------------------------------------------------------------- - * Here ends the DZ interrupt routines. - * ------------------------------------------------------------------- - */ - -/* - * This routine is used to handle the "bottom half" processing for the - * serial driver, known also the "software interrupt" processing. - * This processing is done at the kernel interrupt level, after the - * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This - * is where time-consuming activities which can not be done in the - * interrupt driver proper are done; the interrupt driver schedules - * them using rs_sched_event(), and they get done here. - */ -static void do_serial_bh (void) -{ - run_task_queue (&tq_serial); -} - -static void do_softint (void *private_data) -{ - struct dz_serial *info = (struct dz_serial *) private_data; - struct tty_struct *tty = info->tty; - - if (!tty) - return; - - if (test_and_clear_bit(DZ_EVENT_WRITE_WAKEUP, &info->event)) { - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - (tty->ldisc.write_wakeup) (tty); - wake_up_interruptible (&tty->write_wait); - } -} - -/* - * ------------------------------------------------------------------- - * This routine is called from the scheduler tqueue when the interrupt - * routine has signalled that a hangup has occurred. The path of - * hangup processing is: - * - * serial interrupt routine -> (scheduler tqueue) -> - * do_serial_hangup() -> tty->hangup() -> rs_hangup() - * ------------------------------------------------------------------- - */ -static void do_serial_hangup (void *private_data) -{ - struct dz_serial *info = (struct dz_serial *) private_data; - struct tty_struct *tty = info->tty;; - - if (!tty) - return; - - tty_hangup(tty); -} - -/* - * ------------------------------------------------------------------- - * startup () - * - * various initialization tasks - * ------------------------------------------------------------------- - */ -static int startup (struct dz_serial *info) -{ - unsigned long page, flags; - unsigned short tmp; - - if (info->is_initialized) - return 0; - - save_and_cli(flags); - - if (!info->port) { - if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); - restore_flags(flags); - return -ENODEV; - } - - if (!info->xmit_buf) { - page = get_zeroed_page(GFP_KERNEL); - if (!page) { - restore_flags (flags); - return -ENOMEM; - } - info->xmit_buf = (unsigned char *)page; - } - - if (info->tty) - clear_bit(TTY_IO_ERROR, &info->tty->flags); - - /* enable the interrupt and the scanning */ - tmp = dz_in(info, DZ_CSR); - tmp |= (DZ_RIE | DZ_TIE | DZ_MSE); - dz_out(info, DZ_CSR, tmp); - - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - - change_speed(info); /* set up the speed */ - - /* - * Clear the line transmitter buffer I can't figure out why I need to - * do this - but its necessary - in order for the console portion and - * the interrupt portion to live happily side by side. - */ - - info->is_initialized = 1; - - restore_flags(flags); - - return 0; -} - -/* - * ------------------------------------------------------------------- - * shutdown () - * - * This routine will shutdown a serial port; interrupts are disabled, and - * DTR is dropped if the hangup on close termio flag is on. - * ------------------------------------------------------------------- - */ -static void shutdown (struct dz_serial *info) -{ - unsigned long flags; - unsigned short tmp; - - if (!info->is_initialized) - return; - - save_and_cli(flags); - - dz_stop (info->tty); - - info->cflags &= ~DZ_CREAD; /* turn off receive enable flag */ - dz_out(info, DZ_LPR, info->cflags); - - if (info->xmit_buf) { /* free Tx buffer */ - free_page((unsigned long)info->xmit_buf); - info->xmit_buf = 0; - } - - if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { - tmp = dz_in(info, DZ_TCR); - if (tmp & DZ_MODEM_DTR) { - tmp &= ~DZ_MODEM_DTR; - dz_out(info, DZ_TCR, tmp); - } - } - - if (info->tty) - set_bit (TTY_IO_ERROR, &info->tty->flags); - - info->is_initialized = 0; - - restore_flags (flags); -} - -/* - * ------------------------------------------------------------------- - * change_speed () - * - * set the baud rate. - * ------------------------------------------------------------------- - */ -static void change_speed (struct dz_serial *info) -{ - unsigned long flags; - unsigned cflag; - int baud; - - if (!info->tty || !info->tty->termios) - return; - - save_and_cli(flags); - - info->cflags = info->line; - - cflag = info->tty->termios->c_cflag; - - switch (cflag & CSIZE) { - case CS5: - info->cflags |= DZ_CS5; - break; - case CS6: - info->cflags |= DZ_CS6; - break; - case CS7: - info->cflags |= DZ_CS7; - break; - case CS8: - default: - info->cflags |= DZ_CS8; - } - - if (cflag & CSTOPB) - info->cflags |= DZ_CSTOPB; - if (cflag & PARENB) - info->cflags |= DZ_PARENB; - if (cflag & PARODD) - info->cflags |= DZ_PARODD; - - baud = tty_get_baud_rate(info->tty); - switch (baud) { - case 50: - info->cflags |= DZ_B50; - break; - case 75: - info->cflags |= DZ_B75; - break; - case 110: - info->cflags |= DZ_B110; - break; - case 134: - info->cflags |= DZ_B134; - break; - case 150: - info->cflags |= DZ_B150; - break; - case 300: - info->cflags |= DZ_B300; - break; - case 600: - info->cflags |= DZ_B600; - break; - case 1200: - info->cflags |= DZ_B1200; - break; - case 1800: - info->cflags |= DZ_B1800; - break; - case 2000: - info->cflags |= DZ_B2000; - break; - case 2400: - info->cflags |= DZ_B2400; - break; - case 3600: - info->cflags |= DZ_B3600; - break; - case 4800: - info->cflags |= DZ_B4800; - break; - case 7200: - info->cflags |= DZ_B7200; - break; - case 9600: - default: - info->cflags |= DZ_B9600; - } - - info->cflags |= DZ_RXENAB; - dz_out(info, DZ_LPR, info->cflags); - - /* setup accept flag */ - info->read_status_mask = DZ_OERR; - if (I_INPCK(info->tty)) - info->read_status_mask |= (DZ_FERR | DZ_PERR); - - /* characters to ignore */ - info->ignore_status_mask = 0; - if (I_IGNPAR(info->tty)) - info->ignore_status_mask |= (DZ_FERR | DZ_PERR); - - restore_flags(flags); -} - -/* - * ------------------------------------------------------------------- - * dz_flush_char () - * - * Flush the buffer. - * ------------------------------------------------------------------- - */ -static void dz_flush_chars (struct tty_struct *tty) -{ - struct dz_serial *info = (struct dz_serial *)tty->driver_data; - unsigned long flags; - - if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || - !info->xmit_buf) - return; - - save_and_cli(flags); - dz_start (info->tty); - restore_flags(flags); -} - - -/* - * ------------------------------------------------------------------- - * dz_write () - * - * main output routine. - * ------------------------------------------------------------------- - */ -static int dz_write (struct tty_struct *tty, int from_user, - const unsigned char *buf, int count) -{ - struct dz_serial *info = (struct dz_serial *)tty->driver_data; - unsigned long flags; - int c, ret = 0; - - if (!tty ) - return ret; - if (!info->xmit_buf) - return ret; - if (!tmp_buf) - tmp_buf = tmp_buffer; - - if (from_user) { - down (&tmp_buf_sem); - while (1) { - c = MIN(count, MIN(DZ_XMIT_SIZE - info->xmit_cnt - 1, - DZ_XMIT_SIZE - info->xmit_head)); - if (c <= 0) - break; - - c -= copy_from_user (tmp_buf, buf, c); - if (!c) { - if (!ret) - ret = -EFAULT; - break; - } - - save_and_cli(flags); - - c = MIN(c, MIN(DZ_XMIT_SIZE - info->xmit_cnt - 1, - DZ_XMIT_SIZE - info->xmit_head)); - memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); - info->xmit_head = ((info->xmit_head + c) & - (DZ_XMIT_SIZE - 1)); - info->xmit_cnt += c; - restore_flags(flags); - - buf += c; - count -= c; - ret += c; - } - up(&tmp_buf_sem); - } else { - while (1) { - save_and_cli(flags); - - c = MIN(count, MIN(DZ_XMIT_SIZE - info->xmit_cnt - 1, - DZ_XMIT_SIZE - info->xmit_head)); - if (c <= 0) { - restore_flags (flags); - break; - } - memcpy(info->xmit_buf + info->xmit_head, buf, c); - info->xmit_head = ((info->xmit_head + c) & - (DZ_XMIT_SIZE-1)); - info->xmit_cnt += c; - restore_flags(flags); - - buf += c; - count -= c; - ret += c; - } - } - - if (info->xmit_cnt) { - if (!tty->stopped) { - if (!tty->hw_stopped) { - dz_start (info->tty); - } - } - } - - return ret; -} - -/* - * ------------------------------------------------------------------- - * dz_write_room () - * - * compute the amount of space available for writing. - * ------------------------------------------------------------------- - */ -static int dz_write_room (struct tty_struct *tty) -{ - struct dz_serial *info = (struct dz_serial *)tty->driver_data; - int ret; - - ret = DZ_XMIT_SIZE - info->xmit_cnt - 1; - if (ret < 0) - ret = 0; - - return ret; -} - -/* - * ------------------------------------------------------------------- - * dz_chars_in_buffer () - * - * compute the amount of char left to be transmitted - * ------------------------------------------------------------------- - */ -static int dz_chars_in_buffer (struct tty_struct *tty) -{ - struct dz_serial *info = (struct dz_serial *)tty->driver_data; - - return info->xmit_cnt; -} - -/* - * ------------------------------------------------------------------- - * dz_flush_buffer () - * - * Empty the output buffer - * ------------------------------------------------------------------- - */ -static void dz_flush_buffer (struct tty_struct *tty) -{ - struct dz_serial *info = (struct dz_serial *)tty->driver_data; - - cli(); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - sti(); - - wake_up_interruptible (&tty->write_wait); - - if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - tty->ldisc.write_wakeup) - tty->ldisc.write_wakeup(tty); -} - -/* - * ------------------------------------------------------------ - * dz_throttle () and dz_unthrottle () - * - * This routine is called by the upper-layer tty layer to signal that - * incoming characters should be throttled (or not). - * ------------------------------------------------------------ - */ -static void dz_throttle (struct tty_struct *tty) -{ - struct dz_serial *info = (struct dz_serial *)tty->driver_data; - - if (I_IXOFF(tty)) - info->x_char = STOP_CHAR(tty); -} - -static void dz_unthrottle (struct tty_struct *tty) -{ - struct dz_serial *info = (struct dz_serial *)tty->driver_data; - - if (I_IXOFF(tty)) { - if (info->x_char) - info->x_char = 0; - else - info->x_char = START_CHAR(tty); - } -} - -static void dz_send_xchar (struct tty_struct *tty, char ch) -{ - struct dz_serial *info = (struct dz_serial *)tty->driver_data; - - info->x_char = ch; - - if (ch) - dz_start(info->tty); -} - -/* - * ------------------------------------------------------------ - * rs_ioctl () and friends - * ------------------------------------------------------------ - */ -static int get_serial_info(struct dz_serial *info, - struct serial_struct *retinfo) -{ - struct serial_struct tmp; - - if (!retinfo) - return -EFAULT; - - memset (&tmp, 0, sizeof(tmp)); - - tmp.type = info->type; - tmp.line = info->line; - tmp.port = info->port; - tmp.irq = SERIAL; - tmp.flags = info->flags; - tmp.baud_base = info->baud_base; - tmp.close_delay = info->close_delay; - tmp.closing_wait = info->closing_wait; - - return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0; -} - -static int set_serial_info (struct dz_serial *info, - struct serial_struct *new_info) -{ - struct serial_struct new_serial; - struct dz_serial old_info; - int retval = 0; - - if (!new_info) - return -EFAULT; - - if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) - return -EFAULT; - - old_info = *info; - - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - if (info->count > 1) - return -EBUSY; - - /* - * OK, past this point, all the error checking has been done. - * At this point, we start making changes..... - */ - - info->baud_base = new_serial.baud_base; - info->type = new_serial.type; - info->close_delay = new_serial.close_delay; - info->closing_wait = new_serial.closing_wait; - - retval = startup(info); - - return retval; -} - -/* - * get_lsr_info - get line status register info - * - * Purpose: Let user call ioctl() to get info when the UART physically - * is emptied. On bus types like RS485, the transmitter must - * release the bus after transmitting. This must be done when - * the transmit shift register is empty, not be done when the - * transmit holding register is empty. This functionality - * allows an RS485 driver to be written in user space. - */ -static int get_lsr_info (struct dz_serial *info, unsigned int *value) -{ - unsigned short status = dz_in (info, DZ_LPR); - - return put_user (status, value); -} - -/* - * This routine sends a break character out the serial port. - */ -static void send_break (struct dz_serial *info, int duration) -{ - unsigned long flags; - unsigned short tmp, mask; - - if (!info->port) - return; - - mask = 1 << info->line; - tmp = dz_in (info, DZ_TCR); - tmp |= mask; - - current->state = TASK_INTERRUPTIBLE; - - save_and_cli(flags); - dz_out(info, DZ_TCR, tmp); - schedule_timeout(duration); - tmp &= ~mask; - dz_out(info, DZ_TCR, tmp); - restore_flags(flags); -} - -static int dz_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int error; - struct dz_serial * info = (struct dz_serial *)tty->driver_data; - int retval; - - if (cmd != TIOCGSERIAL && cmd != TIOCSSERIAL && - cmd != TIOCSERCONFIG && cmd != TIOCSERGWILD && - cmd != TIOCSERSWILD && cmd != TIOCSERGSTRUCT) { - if (tty->flags & (1 << TTY_IO_ERROR)) - return -EIO; - } - - switch (cmd) { - case TCSBRK: /* SVID version: non-zero arg --> no break */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - if (!arg) - send_break(info, HZ/4); /* 1/4 second */ - return 0; - - case TCSBRKP: /* support for POSIX tcsendbreak() */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - send_break(info, arg ? arg*(HZ/10) : HZ/4); - return 0; - - case TIOCGSOFTCAR: - error = verify_area (VERIFY_WRITE, (void *)arg, sizeof(long)); - if (error) - return error; - put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *)arg); - return 0; - - case TIOCSSOFTCAR: - if (get_user (arg, (unsigned long *)arg)) - return -EFAULT; - - tty->termios->c_cflag = (tty->termios->c_cflag & ~CLOCAL) | - (arg ? CLOCAL : 0); - return 0; - - case TIOCGSERIAL: - error = verify_area(VERIFY_WRITE, (void *)arg, - sizeof(struct serial_struct)); - if (error) - return error; - return get_serial_info(info, (struct serial_struct *)arg); - - case TIOCSSERIAL: - return set_serial_info(info, (struct serial_struct *) arg); - - case TIOCSERGETLSR: /* Get line status register */ - return get_lsr_info (info, (unsigned int *)arg); - - case TIOCSERGSTRUCT: - return copy_to_user((struct dz_serial *)arg, info, - sizeof(struct dz_serial)) ? -EFAULT : 0; - - default: - return -ENOIOCTLCMD; - } - - return 0; -} - -static void dz_set_termios (struct tty_struct *tty, - struct termios *old_termios) -{ - struct dz_serial *info = (struct dz_serial *)tty->driver_data; - - if (tty->termios->c_cflag == old_termios->c_cflag) - return; - - change_speed (info); - - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; - dz_start(tty); - } -} - -/* - * ------------------------------------------------------------ - * dz_close() - * - * This routine is called when the serial port gets closed. First, we - * wait for the last remaining data to be sent. Then, we turn off - * the transmit enable and receive enable flags. - * ------------------------------------------------------------ - */ -static void dz_close(struct tty_struct *tty, struct file *filp) -{ - struct dz_serial * info = (struct dz_serial *)tty->driver_data; - unsigned long flags; - - if (!info) - return; - - save_and_cli(flags); - - if (tty_hung_up_p(filp)) { - restore_flags(flags); - return; - } - - if ((tty->count == 1) && (info->count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty structure - * will be freed. Info->count should always be one in these - * conditions. If it's greater than one, we've got real - * problems, since it means the serial port won't be shutdown. - */ - printk("dz_close: bad serial port count; tty->count is 1, " - "info->count is %d\n", info->count); - info->count = 1; - } - - if (--info->count < 0) { - printk("ds_close: bad serial port count for ttyS%02d: %d\n", - info->line, info->count); - info->count = 0; - } - - if (info->count) { - restore_flags(flags); - return; - } - info->flags |= DZ_CLOSING; - /* - * Save the termios structure, since this port may have - * separate termios for callout and dialin. - */ - if (info->flags & DZ_NORMAL_ACTIVE) - info->normal_termios = *tty->termios; - if (info->flags & DZ_CALLOUT_ACTIVE) - info->callout_termios = *tty->termios; - /* - * Now we wait for the transmit buffer to clear; and we notify the line - * discipline to only process XON/XOFF characters. - */ - tty->closing = 1; - - if (info->closing_wait != DZ_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->closing_wait); - - /* - * At this point we stop accepting input. To do this, we disable the - * receive line status interrupts. - */ - shutdown(info); - - if (tty->driver->flush_buffer) - tty->driver->flush_buffer (tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer (tty); - tty->closing = 0; - info->event = 0; - info->tty = 0; - - if (tty->ldisc.num != ldiscs[N_TTY].num) { - if (tty->ldisc.close) - tty->ldisc.close(tty); - tty->ldisc = ldiscs[N_TTY]; - tty->termios->c_line = N_TTY; - if (tty->ldisc.open) - tty->ldisc.open(tty); - } - if (info->blocked_open) { - if (info->close_delay) { - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(info->close_delay); - } - wake_up_interruptible(&info->open_wait); - } - - info->flags &= ~(DZ_NORMAL_ACTIVE | DZ_CALLOUT_ACTIVE | DZ_CLOSING); - wake_up_interruptible(&info->close_wait); - - restore_flags(flags); -} - -/* - * dz_hangup () --- called by tty_hangup() when a hangup is signaled. - */ -static void dz_hangup (struct tty_struct *tty) -{ - struct dz_serial *info = (struct dz_serial *) tty->driver_data; - - dz_flush_buffer(tty); - shutdown(info); - info->event = 0; - info->count = 0; - info->flags &= ~(DZ_NORMAL_ACTIVE | DZ_CALLOUT_ACTIVE); - info->tty = 0; - wake_up_interruptible(&info->open_wait); -} - -/* - * ------------------------------------------------------------ - * rs_open() and friends - * ------------------------------------------------------------ - */ -static int block_til_ready(struct tty_struct *tty, struct file *filp, - struct dz_serial *info) -{ - DECLARE_WAITQUEUE(wait, current); - int retval; - int do_clocal = 0; - - /* - * If the device is in the middle of being closed, then block - * until it's done, and then try again. - */ - if (info->flags & DZ_CLOSING) { - interruptible_sleep_on(&info->close_wait); - return -EAGAIN; - } - - /* - * If this is a callout device, then just make sure the normal - * device isn't being used. - */ - if (tty->driver->subtype == SERIAL_TYPE_CALLOUT) { - if (info->flags & DZ_NORMAL_ACTIVE) - return -EBUSY; - - if ((info->flags & DZ_CALLOUT_ACTIVE) && - (info->flags & DZ_SESSION_LOCKOUT) && - (info->session != current->session)) - return -EBUSY; - - if ((info->flags & DZ_CALLOUT_ACTIVE) && - (info->flags & DZ_PGRP_LOCKOUT) && - (info->pgrp != current->pgrp)) - return -EBUSY; - - info->flags |= DZ_CALLOUT_ACTIVE; - return 0; - } - - /* - * If non-blocking mode is set, or the port is not enabled, then make - * the check up front and then exit. - */ - if ((filp->f_flags & O_NONBLOCK) || - (tty->flags & (1 << TTY_IO_ERROR))) { - if (info->flags & DZ_CALLOUT_ACTIVE) - return -EBUSY; - info->flags |= DZ_NORMAL_ACTIVE; - - return 0; - } - - if (info->flags & DZ_CALLOUT_ACTIVE) { - if (info->normal_termios.c_cflag & CLOCAL) - do_clocal = 1; - } else { - if (tty->termios->c_cflag & CLOCAL) - do_clocal = 1; - } - - /* - * Block waiting for the carrier detect and the line to become free - * (i.e., not in use by the callout). While we are in this loop, - * info->count is dropped by one, so that dz_close() knows when to free - * things. We restore it upon exit, either normal or abnormal. - */ - retval = 0; - add_wait_queue(&info->open_wait, &wait); - - info->count--; - info->blocked_open++; - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p (filp) || !(info->is_initialized)) { - retval = -EAGAIN; - break; - } - if (!(info->flags & DZ_CALLOUT_ACTIVE) && - !(info->flags & DZ_CLOSING) && do_clocal) - break; - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - schedule(); - } - - current->state = TASK_RUNNING; - remove_wait_queue (&info->open_wait, &wait); - if (!tty_hung_up_p(filp)) - info->count++; - info->blocked_open--; - - if (retval) - return retval; - info->flags |= DZ_NORMAL_ACTIVE; - return 0; -} - -/* - * This routine is called whenever a serial port is opened. It - * enables interrupts for a serial port. It also performs the - * serial-specific initialization for the tty structure. - */ -static int dz_open (struct tty_struct *tty, struct file *filp) -{ - struct dz_serial *info; - int retval, line; - - line = tty->index; - - /* - * The dz lines for the mouse/keyboard must be opened using their - * respective drivers. - */ - if ((line < 0) || (line >= DZ_NB_PORT)) - return -ENODEV; - - if ((line == DZ_KEYBOARD) || (line == DZ_MOUSE)) - return -ENODEV; - - info = lines[line]; - info->count++; - - tty->driver_data = info; - info->tty = tty; - - /* - * Start up serial port - */ - retval = startup (info); - if (retval) - return retval; - - retval = block_til_ready (tty, filp, info); - if (retval) - return retval; - - if ((info->count == 1) && (info->flags & DZ_SPLIT_TERMIOS)) { - if (tty->driver->subtype == SERIAL_TYPE_NORMAL) - *tty->termios = info->normal_termios; - else - *tty->termios = info->callout_termios; - change_speed(info); - } - - info->session = current->session; - info->pgrp = current->pgrp; - - return 0; -} - -static void show_serial_version (void) -{ - printk("%s%s\n", dz_name, dz_version); -} - - -int __init dz_init(void) -{ - int i, flags; - struct dz_serial *info; - - /* Setup base handler, and timer table. */ - init_bh(SERIAL_BH, do_serial_bh); - - show_serial_version(); - - memset(&serial_driver, 0, sizeof(struct tty_driver)); - serial_driver.magic = TTY_DRIVER_MAGIC; - serial_driver.owner = THIS_MODULE; -#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) - serial_driver.name = "ttyS"; -#else - serial_driver.name = "tts/"; -#endif - serial_driver.major = TTY_MAJOR; - serial_driver.minor_start = 64; - serial_driver.num = DZ_NB_PORT; - serial_driver.type = TTY_DRIVER_TYPE_SERIAL; - serial_driver.subtype = SERIAL_TYPE_NORMAL; - serial_driver.init_termios = tty_std_termios; - - serial_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | - CLOCAL; - serial_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; - serial_driver.refcount = &serial_refcount; - serial_driver.table = serial_table; - serial_driver.termios = serial_termios; - serial_driver.termios_locked = serial_termios_locked; - - serial_driver.open = dz_open; - serial_driver.close = dz_close; - serial_driver.write = dz_write; - serial_driver.flush_chars = dz_flush_chars; - serial_driver.write_room = dz_write_room; - serial_driver.chars_in_buffer = dz_chars_in_buffer; - serial_driver.flush_buffer = dz_flush_buffer; - serial_driver.ioctl = dz_ioctl; - serial_driver.throttle = dz_throttle; - serial_driver.unthrottle = dz_unthrottle; - serial_driver.send_xchar = dz_send_xchar; - serial_driver.set_termios = dz_set_termios; - serial_driver.stop = dz_stop; - serial_driver.start = dz_start; - serial_driver.hangup = dz_hangup; - - /* - * The callout device is just like normal device except for major - * number and the subtype code. - */ - callout_driver = serial_driver; -#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) - callout_driver.name = "cua"; -#else - callout_driver.name = "cua/"; -#endif - callout_driver.major = TTYAUX_MAJOR; - callout_driver.subtype = SERIAL_TYPE_CALLOUT; - - if (tty_register_driver (&serial_driver)) - panic("Couldn't register serial driver\n"); - if (tty_register_driver (&callout_driver)) - panic("Couldn't register callout driver\n"); - - save_flags(flags); cli(); - for (i=0; i < DZ_NB_PORT; i++) { - info = &multi[i]; - lines[i] = info; - info->magic = SERIAL_MAGIC; - - if ((mips_machtype == MACH_DS23100) || - (mips_machtype == MACH_DS5100)) - info->port = (unsigned long) KN01_DZ11_BASE; - else - info->port = (unsigned long) KN02_DZ11_BASE; - - info->line = i; - info->tty = 0; - info->close_delay = 50; - info->closing_wait = 3000; - info->x_char = 0; - info->event = 0; - info->count = 0; - info->blocked_open = 0; - info->tqueue.routine = do_softint; - info->tqueue.data = info; - info->tqueue_hangup.routine = do_serial_hangup; - info->tqueue_hangup.data = info; - info->callout_termios = callout_driver.init_termios; - info->normal_termios = serial_driver.init_termios; - init_waitqueue_head(&info->open_wait); - init_waitqueue_head(&info->close_wait); - - /* - * If we are pointing to address zero then punt - not correctly - * set up in setup.c to handle this. - */ - if (! info->port) - return 0; - - printk("ttyS%02d at 0x%08x (irq = %d)\n", info->line, - info->port, SERIAL); - - tty_register_device(&serial_driver, info->line, NULL); - tty_register_device(&callout_driver, info->line, NULL); - } - - /* Reset the chip */ -#ifndef CONFIG_SERIAL_CONSOLE - { - int tmp; - dz_out(info, DZ_CSR, DZ_CLR); - while ((tmp = dz_in(info,DZ_CSR)) & DZ_CLR); - wbflush(); - - /* Enable scanning */ - dz_out(info, DZ_CSR, DZ_MSE); - } -#endif - - /* - * Order matters here... the trick is that flags is updated... in - * request_irq - to immediatedly obliterate it is unwise. - */ - restore_flags(flags); - - if (request_irq(SERIAL, dz_interrupt, SA_INTERRUPT, "DZ", lines[0])) - panic("Unable to register DZ interrupt\n"); - - return 0; -} - -#ifdef CONFIG_SERIAL_CONSOLE -static void dz_console_put_char (unsigned char ch) -{ - unsigned long flags; - int loops = 2500; - unsigned short tmp = ch; - /* - * this code sends stuff out to serial device - spinning its wheels and - * waiting. - */ - - /* force the issue - point it at lines[3]*/ - dz_console = &multi[CONSOLE_LINE]; - - save_and_cli(flags); - - /* spin our wheels */ - while (((dz_in(dz_console, DZ_CSR) & DZ_TRDY) != DZ_TRDY) && loops--) - ; - - /* Actually transmit the character. */ - dz_out(dz_console, DZ_TDR, tmp); - - restore_flags(flags); -} - -/* - * ------------------------------------------------------------------- - * dz_console_print () - * - * dz_console_print is registered for printk. - * The console must be locked when we get here. - * ------------------------------------------------------------------- - */ -static void dz_console_print (struct console *cons, - const char *str, - unsigned int count) -{ -#ifdef DEBUG_DZ - prom_printf((char *)str); -#endif - while (count--) { - if (*str == '\n') - dz_console_put_char('\r'); - dz_console_put_char(*str++); - } -} - -static struct tty_driver *dz_console_device(struct console *c, int *index) -{ - *index = c->index; - return &serial_driver; -} - -static int __init dz_console_setup(struct console *co, char *options) -{ - int baud = 9600; - int bits = 8; - int parity = 'n'; - int cflag = CREAD | HUPCL | CLOCAL; - char *s; - unsigned short mask,tmp; - - if (options) { - baud = simple_strtoul(options, NULL, 10); - s = options; - while (*s >= '0' && *s <= '9') - s++; - if (*s) - parity = *s++; - if (*s) - bits = *s - '0'; - } - - /* - * Now construct a cflag setting. - */ - switch (baud) { - case 1200: - cflag |= DZ_B1200; - break; - case 2400: - cflag |= DZ_B2400; - break; - case 4800: - cflag |= DZ_B4800; - break; - case 9600: - default: - cflag |= DZ_B9600; - break; - } - switch (bits) { - case 7: - cflag |= DZ_CS7; - break; - default: - case 8: - cflag |= DZ_CS8; - break; - } - switch (parity) { - case 'o': - case 'O': - cflag |= DZ_PARODD; - break; - case 'e': - case 'E': - cflag |= DZ_PARENB; - break; - } - co->cflag = cflag; - - /* TOFIX: force to console line */ - dz_console = &multi[CONSOLE_LINE]; - if ((mips_machtype == MACH_DS23100) || (mips_machtype == MACH_DS5100)) - dz_console->port = KN01_DZ11_BASE; - else - dz_console->port = KN02_DZ11_BASE; - dz_console->line = CONSOLE_LINE; - - dz_out(dz_console, DZ_CSR, DZ_CLR); - while ((tmp = dz_in(dz_console,DZ_CSR)) & DZ_CLR) - ; - - /* enable scanning */ - dz_out(dz_console, DZ_CSR, DZ_MSE); - - /* Set up flags... */ - dz_console->cflags = 0; - dz_console->cflags |= DZ_B9600; - dz_console->cflags |= DZ_CS8; - dz_console->cflags |= DZ_PARENB; - dz_out(dz_console, DZ_LPR, dz_console->cflags); - - mask = 1 << dz_console->line; - tmp = dz_in (dz_console, DZ_TCR); /* read the TX flag */ - if (!(tmp & mask)) { - tmp |= mask; /* set the TX flag */ - dz_out (dz_console, DZ_TCR, tmp); - } - - return 0; -} - -static struct console dz_sercons = { - .name = "ttyS", - .write = dz_console_print, - .device = dz_console_device, - .setup = dz_console_setup, - .flags = CON_CONSDEV | CON_PRINTBUFFER, - .index = CONSOLE_LINE, -}; - -void __init dz_serial_console_init(void) -{ - register_console(&dz_sercons); -} - -#endif /* ifdef CONFIG_SERIAL_CONSOLE */ - -MODULE_LICENSE("GPL"); diff -urN orig/drivers/char/dz.h linux/drivers/char/dz.h --- orig/drivers/char/dz.h Sun Feb 27 09:44:30 2000 +++ linux/drivers/char/dz.h Thu Jan 1 01:00:00 1970 @@ -1,242 +0,0 @@ -/* - * dz.h: Serial port driver for DECStations equiped - * with the DZ chipset. - * - * Copyright (C) 1998 Olivier A. D. Lebaillif - * - * Email: olivier.lebaillif@ifrsys.com - * - */ -#ifndef DZ_SERIAL_H -#define DZ_SERIAL_H - -/* - * Definitions for the Control and Status Received. - */ -#define DZ_TRDY 0x8000 /* Transmitter empty */ -#define DZ_TIE 0x4000 /* Transmitter Interrupt Enable */ -#define DZ_RDONE 0x0080 /* Receiver data ready */ -#define DZ_RIE 0x0040 /* Receive Interrupt Enable */ -#define DZ_MSE 0x0020 /* Master Scan Enable */ -#define DZ_CLR 0x0010 /* Master reset */ -#define DZ_MAINT 0x0008 /* Loop Back Mode */ - -/* - * Definitions for the Received buffer. - */ -#define DZ_RBUF_MASK 0x00FF /* Data Mask in the Receive Buffer */ -#define DZ_LINE_MASK 0x0300 /* Line Mask in the Receive Buffer */ -#define DZ_DVAL 0x8000 /* Valid Data indicator */ -#define DZ_OERR 0x4000 /* Overrun error indicator */ -#define DZ_FERR 0x2000 /* Frame error indicator */ -#define DZ_PERR 0x1000 /* Parity error indicator */ - -#define LINE(x) (x & DZ_LINE_MASK) >> 8 /* Get the line number from the input buffer */ -#define UCHAR(x) (unsigned char)(x & DZ_RBUF_MASK) - -/* - * Definitions for the Transmit Register. - */ -#define DZ_LINE_KEYBOARD 0x0001 -#define DZ_LINE_MOUSE 0x0002 -#define DZ_LINE_MODEM 0x0004 -#define DZ_LINE_PRINTER 0x0008 - -#define DZ_MODEM_DTR 0x0400 /* DTR for the modem line (2) */ - -/* - * Definitions for the Modem Status Register. - */ -#define DZ_MODEM_DSR 0x0200 /* DSR for the modem line (2) */ - -/* - * Definitions for the Transmit Data Register. - */ -#define DZ_BRK0 0x0100 /* Break assertion for line 0 */ -#define DZ_BRK1 0x0200 /* Break assertion for line 1 */ -#define DZ_BRK2 0x0400 /* Break assertion for line 2 */ -#define DZ_BRK3 0x0800 /* Break assertion for line 3 */ - -/* - * Definitions for the Line Parameter Register. - */ -#define DZ_KEYBOARD 0x0000 /* line 0 = keyboard */ -#define DZ_MOUSE 0x0001 /* line 1 = mouse */ -#define DZ_MODEM 0x0002 /* line 2 = modem */ -#define DZ_PRINTER 0x0003 /* line 3 = printer */ - -#define DZ_CSIZE 0x0018 /* Number of bits per byte (mask) */ -#define DZ_CS5 0x0000 /* 5 bits per byte */ -#define DZ_CS6 0x0008 /* 6 bits per byte */ -#define DZ_CS7 0x0010 /* 7 bits per byte */ -#define DZ_CS8 0x0018 /* 8 bits per byte */ - -#define DZ_CSTOPB 0x0020 /* 2 stop bits instead of one */ - -#define DZ_PARENB 0x0040 /* Parity enable */ -#define DZ_PARODD 0x0080 /* Odd parity instead of even */ - -#define DZ_CBAUD 0x0E00 /* Baud Rate (mask) */ -#define DZ_B50 0x0000 -#define DZ_B75 0x0100 -#define DZ_B110 0x0200 -#define DZ_B134 0x0300 -#define DZ_B150 0x0400 -#define DZ_B300 0x0500 -#define DZ_B600 0x0600 -#define DZ_B1200 0x0700 -#define DZ_B1800 0x0800 -#define DZ_B2000 0x0900 -#define DZ_B2400 0x0A00 -#define DZ_B3600 0x0B00 -#define DZ_B4800 0x0C00 -#define DZ_B7200 0x0D00 -#define DZ_B9600 0x0E00 - -#define DZ_CREAD 0x1000 /* Enable receiver */ -#define DZ_RXENAB 0x1000 /* enable receive char */ -/* - * Addresses for the DZ registers - */ -#define DZ_CSR 0x00 /* Control and Status Register */ -#define DZ_RBUF 0x08 /* Receive Buffer */ -#define DZ_LPR 0x08 /* Line Parameters Register */ -#define DZ_TCR 0x10 /* Transmitter Control Register */ -#define DZ_MSR 0x18 /* Modem Status Register */ -#define DZ_TDR 0x18 /* Transmit Data Register */ - - -#define DZ_NB_PORT 4 - -#define DZ_XMIT_SIZE 4096 /* buffer size */ -#define WAKEUP_CHARS DZ_XMIT_SIZE/4 - -#define DZ_EVENT_WRITE_WAKEUP 0 - -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) - -#define DZ_INITIALIZED 0x80000000 /* Serial port was initialized */ -#define DZ_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */ -#define DZ_NORMAL_ACTIVE 0x20000000 /* Normal device is active */ -#define DZ_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */ -#define DZ_CLOSING 0x08000000 /* Serial port is closing */ -#define DZ_CTS_FLOW 0x04000000 /* Do CTS flow control */ -#define DZ_CHECK_CD 0x02000000 /* i.e., CLOCAL */ - -#define DZ_CLOSING_WAIT_INF 0 -#define DZ_CLOSING_WAIT_NONE 65535 - -#define DZ_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */ -#define DZ_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */ -#define DZ_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */ - -struct dz_serial { - unsigned port; /* base address for the port */ - int type; - int flags; - int baud_base; - int blocked_open; - unsigned short close_delay; - unsigned short closing_wait; - unsigned short line; /* port/line number */ - unsigned short cflags; /* line configuration flag */ - unsigned short x_char; /* xon/xoff character */ - unsigned short read_status_mask; /* mask for read condition */ - unsigned short ignore_status_mask; /* mask for ignore condition */ - unsigned long event; /* mask used in BH */ - unsigned char *xmit_buf; /* Transmit buffer */ - int xmit_head; /* Position of the head */ - int xmit_tail; /* Position of the tail */ - int xmit_cnt; /* Count of the chars in the buffer */ - int count; /* indicates how many times it has been opened */ - int magic; - - struct async_icount icount; /* keep track of things ... */ - struct tty_struct *tty; /* tty associated */ - struct tq_struct tqueue; /* Queue for BH */ - struct tq_struct tqueue_hangup; - struct termios normal_termios; - struct termios callout_termios; - wait_queue_head_t open_wait; - wait_queue_head_t close_wait; - - long session; /* Session of opening process */ - long pgrp; /* pgrp of opening process */ - - unsigned char is_console; /* flag indicating a serial console */ - unsigned char is_initialized; -}; - -static struct dz_serial multi[DZ_NB_PORT]; /* Four serial lines in the DZ chip */ -static struct dz_serial *dz_console; -static struct tty_driver serial_driver, callout_driver; - -static struct tty_struct *serial_table[DZ_NB_PORT]; -static struct termios *serial_termios[DZ_NB_PORT]; -static struct termios *serial_termios_locked[DZ_NB_PORT]; - -static int serial_refcount; - -/* - * tmp_buf is used as a temporary buffer by serial_write. We need to - * lock it in case the copy_from_user blocks while swapping in a page, - * and some other program tries to do a serial write at the same time. - * Since the lock will only come under contention when the system is - * swapping and available memory is low, it makes sense to share one - * buffer across all the serial ports, since it significantly saves - * memory if large numbers of serial ports are open. - */ -static unsigned char *tmp_buf; -static DECLARE_MUTEX(tmp_buf_sem); - -static char *dz_name = "DECstation DZ serial driver version "; -static char *dz_version = "1.02"; - -static inline unsigned short dz_in (struct dz_serial *, unsigned); -static inline void dz_out (struct dz_serial *, unsigned, unsigned short); - -static inline void dz_sched_event (struct dz_serial *, int); -static inline void receive_chars (struct dz_serial *); -static inline void transmit_chars (struct dz_serial *); -static inline void check_modem_status (struct dz_serial *); - -static void dz_stop (struct tty_struct *); -static void dz_start (struct tty_struct *); -static void dz_interrupt (int, void *, struct pt_regs *); -static void do_serial_bh (void); -static void do_softint (void *); -static void do_serial_hangup (void *); -static void change_speed (struct dz_serial *); -static void dz_flush_chars (struct tty_struct *); -static void dz_console_print (struct console *, const char *, unsigned int); -static void dz_flush_buffer (struct tty_struct *); -static void dz_throttle (struct tty_struct *); -static void dz_unthrottle (struct tty_struct *); -static void dz_send_xchar (struct tty_struct *, char); -static void shutdown (struct dz_serial *); -static void send_break (struct dz_serial *, int); -static void dz_set_termios (struct tty_struct *, struct termios *); -static void dz_close (struct tty_struct *, struct file *); -static void dz_hangup (struct tty_struct *); -static void show_serial_version (void); - -static int dz_write (struct tty_struct *, int, const unsigned char *, int); -static int dz_write_room (struct tty_struct *); -static int dz_chars_in_buffer (struct tty_struct *); -static int startup (struct dz_serial *); -static int get_serial_info (struct dz_serial *, struct serial_struct *); -static int set_serial_info (struct dz_serial *, struct serial_struct *); -static int get_lsr_info (struct dz_serial *, unsigned int *); -static int dz_ioctl (struct tty_struct *, struct file *, unsigned int, unsigned long); -static int block_til_ready (struct tty_struct *, struct file *, struct dz_serial *); -static int dz_open (struct tty_struct *, struct file *); - -#ifdef MODULE -int init_module (void) -void cleanup_module (void) -#endif - -#endif - -#endif /* DZ_SERIAL_H */ diff -urN orig/drivers/char/gc_kbmap.h linux/drivers/char/gc_kbmap.h --- orig/drivers/char/gc_kbmap.h Thu Jan 1 01:00:00 1970 +++ linux/drivers/char/gc_kbmap.h Tue Aug 28 20:26:52 2001 @@ -0,0 +1,162 @@ + + +#define KK_NONE 0x7f +#define KK_ESC 0x00 +#define KK_F1 0x01 +#define KK_F2 0x02 +#define KK_F3 0x03 +#define KK_F4 0x04 +#define KK_F5 0x05 +#define KK_F6 0x06 +#define KK_F7 0x07 +#define KK_F8 0x08 +#define KK_F9 0x09 +#define KK_F10 0x0a +#define KK_F11 0x0b +#define KK_F12 0x0c +#define KK_PRNT 0x0d +#define KK_SCRL 0x0e +#define KK_BRK 0x0f +#define KK_AGR 0x10 +#define KK_1 0x11 +#define KK_2 0x12 +#define KK_3 0x13 +#define KK_4 0x14 +#define KK_5 0x15 +#define KK_6 0x16 +#define KK_7 0x17 +#define KK_8 0x18 +#define KK_9 0x19 +#define KK_0 0x1a +#define KK_MINS 0x1b +#define KK_EQLS 0x1c +#define KK_BKSP 0x1e +#define KK_INS 0x1f +#define KK_HOME 0x20 +#define KK_PGUP 0x21 +#define KK_NUML 0x22 +#define KP_SLH 0x23 +#define KP_STR 0x24 +#define KP_MNS 0x3a +#define KK_TAB 0x26 +#define KK_Q 0x27 +#define KK_W 0x28 +#define KK_E 0x29 +#define KK_R 0x2a +#define KK_T 0x2b +#define KK_Y 0x2c +#define KK_U 0x2d +#define KK_I 0x2e +#define KK_O 0x2f +#define KK_P 0x30 +#define KK_LSBK 0x31 +#define KK_RSBK 0x32 +#define KK_ENTR 0x47 +#define KK_DEL 0x34 +#define KK_END 0x35 +#define KK_PGDN 0x36 +#define KP_7 0x37 +#define KP_8 0x38 +#define KP_9 0x39 +#define KP_PLS 0x4b +#define KK_CAPS 0x5d +#define KK_A 0x3c +#define KK_S 0x3d +#define KK_D 0x3e +#define KK_F 0x3f +#define KK_G 0x40 +#define KK_H 0x41 +#define KK_J 0x42 +#define KK_K 0x43 +#define KK_L 0x44 +#define KK_SEMI 0x45 +#define KK_SQOT 0x46 +#define KK_HASH 0x1d +#define KP_4 0x48 +#define KP_5 0x49 +#define KP_6 0x4a +#define KK_LSFT 0x4c +#define KK_BSLH 0x33 +#define KK_Z 0x4e +#define KK_X 0x4f +#define KK_C 0x50 +#define KK_V 0x51 +#define KK_B 0x52 +#define KK_N 0x53 +#define KK_M 0x54 +#define KK_COMA 0x55 +#define KK_DOT 0x56 +#define KK_FSLH 0x57 +#define KK_RSFT 0x58 +#define KK_UP 0x59 +#define KP_1 0x5a +#define KP_2 0x5b +#define KP_3 0x5c +#define KP_ENT 0x67 +#define KK_LCTL 0x3b +#define KK_LALT 0x5e +#define KK_SPCE 0x5f +#define KK_RALT 0x60 +#define KK_RCTL 0x61 +#define KK_LEFT 0x62 +#define KK_DOWN 0x63 +#define KK_RGHT 0x64 +#define KP_0 0x65 +#define KP_DOT 0x66 + +static char kbmap[128] = { +KK_NONE, KK_LALT, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, KK_AGR, KK_BSLH, KK_TAB, KK_Z, KK_A, KK_X, KK_NONE, +KK_NONE, KK_NONE, KK_LSFT, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, KK_LCTL, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, 0x21, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, KK_ESC, KK_DEL, KK_Q, KK_CAPS, KK_S, KK_C, KK_3, +KK_NONE, KK_1, KK_NONE, KK_W, KK_NONE, KK_D, KK_V, KK_4, +KK_NONE, KK_2, KK_T, KK_E, KK_NONE, KK_F, KK_B, KK_5, +KK_NONE, KK_9, KK_Y, KK_R, KK_K, KK_G, KK_N, KK_6, +KK_NONE, KK_0, KK_U, KK_O, KK_L, KK_H, KK_M, KK_7, +KK_NONE, KK_MINS, KK_I, KK_P, KK_SEMI, KK_J, KK_COMA, KK_8, +KK_NONE, KK_EQLS, KK_ENTR, KK_LSBK, KK_BSLH, KK_FSLH, KK_DOT, KK_NONE, +KK_NONE, KK_NONE, KK_RSFT, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, KK_BKSP, KK_DOWN, KK_RSBK, KK_UP, KK_LEFT, KK_SPCE, KK_RGHT, +KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE}; + +static char kbmapFN[128] = { +KK_NONE, KK_LALT, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, KK_NONE, KK_LSFT, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, KK_LCTL, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, 0x21, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_F3, +KK_NONE, KK_F1, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_F4, +KK_NONE, KK_F2, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_F5, +KK_NONE, KK_F9, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_F6, +KK_NONE, KK_F10, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_F7, +KK_NONE, KK_NUML, KK_NONE, KK_INS, KK_PRNT, KK_NONE, KK_NONE, KK_F8, +KK_NONE, KK_BRK, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, KK_NONE, KK_RSFT, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, KK_NONE, KK_PGDN, KK_SCRL, KK_PGUP, KK_HOME, KK_NONE, KK_END, +KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE}; + +static char kbmapNL[128] = { +KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, KP_9, KK_NONE, KK_NONE, KP_2, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, KP_STR, KP_4, KP_6, KP_3, KK_NONE, KP_0, KP_7, +KK_NONE, KK_NONE, KP_5, KP_MNS, KP_PLS, KP_1, KK_NONE, KP_8, +KK_NONE, KK_NONE, KP_ENT, KK_NONE, KK_NONE, KP_SLH, KP_DOT, KK_NONE, +KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, +KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE, KK_NONE}; + + + diff -urN orig/drivers/char/gc_keyb.c linux/drivers/char/gc_keyb.c --- orig/drivers/char/gc_keyb.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/char/gc_keyb.c Sun Feb 17 11:46:23 2002 @@ -0,0 +1,1145 @@ +/* + * linux/arch/arm/drivers/char/gc_keyb.c + * + * Copyright 2000 Applied Data Systems + * + * Keyboard & Smartio driver for GraphicsClient ARM Linux. + * Graphics Client is SA1110 based single board computer by + * Applied Data Systems (http://www.applieddata.net) + * + * Change log: + * 7-10/6/01 Thomas Thaele + * - Added Keyboard Sniffer on /dev/sio12 + * - First implementation of PC- compatible Scancodes (thanks to pc_keyb.c) + * 3/23/01 Woojung Huh + * Power Management added + * 12/01/00 Woojung Huh + * Bug fixed + * 11/16/00 Woojung Huh [whuh@applieddata.net] + * Added smartio device driver on it + */ + +/* + * Introduced setkeycode, ketkeycode for the GC+ by Thomas Thaele + * GC+ now performs like a real PC on the keyboard. + * Warning: this code is still beta! PrntScrn and Pause keys are not + * completely tested and implemented!!! Keyboard driver can be confused + * by hacking like crazy on the keyboard. (hardware problem on serial line?) + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define ADS_AVR_IRQ 63 + +#define SMARTIO_IOCTL_BASES 's' +#define SMARTIO_KPD_TIMEOUT _IOW(SMARTIO_IOCTL_BASES, 0, int) +#define SMARTIO_KPD_SETUP _IOW(SMARTIO_IOCTL_BASES, 1, short) +#define SMARTIO_BL_CONTROL _IOW(SMARTIO_IOCTL_BASES, 2, char) +#define SMARTIO_BL_CONTRAST _IOW(SMARTIO_IOCTL_BASES, 3, char) +#define SMARTIO_PORT_CONFIG _IOW(SMARTIO_IOCTL_BASES, 4, char) +#define SMARTIO_SNIFFER_TIMEOUT _IOW(SMARTIO_IOCTL_BASES, 5, long) + + +/* Simple translation table for the SysRq keys */ + +#ifdef CONFIG_MAGIC_SYSRQ +unsigned char pckbd_sysrq_xlate[128] = + "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ + "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ + "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ + "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ + "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ + "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ + "\r\000/"; /* 0x60 - 0x6f */ +#endif + +/* + * Translation of escaped scancodes to keycodes. + * This is now user-settable. + * The keycodes 1-88,96-111,119 are fairly standard, and + * should probably not be changed - changing might confuse X. + * X also interprets scancode 0x5d (KEY_Begin). + * + * For 1-88 keycode equals scancode. + */ + +#define E0_KPENTER 96 +#define E0_RCTRL 97 +#define E0_KPSLASH 98 +#define E0_PRSCR 99 +#define E0_RALT 100 +#define E0_BREAK 101 /* (control-pause) */ +#define E0_HOME 102 +#define E0_UP 103 +#define E0_PGUP 104 +#define E0_LEFT 105 +#define E0_RIGHT 106 +#define E0_END 107 +#define E0_DOWN 108 +#define E0_PGDN 109 +#define E0_INS 110 +#define E0_DEL 111 + +#define E1_PAUSE 119 + +/* + * The keycodes below are randomly located in 89-95,112-118,120-127. + * They could be thrown away (and all occurrences below replaced by 0), + * but that would force many users to use the `setkeycodes' utility, where + * they needed not before. It does not matter that there are duplicates, as + * long as no duplication occurs for any single keyboard. + */ +#define SC_LIM 89 + +#define FOCUS_PF1 85 /* actual code! */ +#define FOCUS_PF2 89 +#define FOCUS_PF3 90 +#define FOCUS_PF4 91 +#define FOCUS_PF5 92 +#define FOCUS_PF6 93 +#define FOCUS_PF7 94 +#define FOCUS_PF8 95 +#define FOCUS_PF9 120 +#define FOCUS_PF10 121 +#define FOCUS_PF11 122 +#define FOCUS_PF12 123 + +#define JAP_86 124 +/* tfj@olivia.ping.dk: + * The four keys are located over the numeric keypad, and are + * labelled A1-A4. It's an rc930 keyboard, from + * Regnecentralen/RC International, Now ICL. + * Scancodes: 59, 5a, 5b, 5c. + */ +#define RGN1 124 +#define RGN2 125 +#define RGN3 126 +#define RGN4 127 + +static unsigned char high_keys[128 - SC_LIM] = { + RGN1, RGN2, RGN3, RGN4, 0, 0, 0, /* 0x59-0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ + 0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12, /* 0x68-0x6f */ + 0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3, /* 0x70-0x77 */ + FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7, /* 0x78-0x7b */ + FOCUS_PF8, JAP_86, FOCUS_PF10, 0 /* 0x7c-0x7f */ +}; + +/* BTC */ +#define E0_MACRO 112 +/* LK450 */ +#define E0_F13 113 +#define E0_F14 114 +#define E0_HELP 115 +#define E0_DO 116 +#define E0_F17 117 +#define E0_KPMINPLUS 118 +/* + * My OmniKey generates e0 4c for the "OMNI" key and the + * right alt key does nada. [kkoller@nyx10.cs.du.edu] + */ +#define E0_OK 124 +/* + * New microsoft keyboard is rumoured to have + * e0 5b (left window button), e0 5c (right window button), + * e0 5d (menu button). [or: LBANNER, RBANNER, RMENU] + * [or: Windows_L, Windows_R, TaskMan] + */ +#define E0_MSLW 125 +#define E0_MSRW 126 +#define E0_MSTM 127 + +static unsigned char e0_keys[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */ + 0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */ + 0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */ + E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */ + E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */ + E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */ + E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */ + 0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0, /* 0x58-0x5f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */ + 0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */ + 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */ +}; + +int gc_kbd_setkeycode(unsigned int scancode, unsigned int keycode) +{ + if (scancode < SC_LIM || scancode > 255 || keycode > 127) + return -EINVAL; + if (scancode < 128) + high_keys[scancode - SC_LIM] = keycode; + else + e0_keys[scancode - 128] = keycode; + return 0; +} + +int gc_kbd_getkeycode(unsigned int scancode) +{ + return + (scancode < SC_LIM || scancode > 255) ? -EINVAL : + (scancode < 128) ? high_keys[scancode - SC_LIM] : + e0_keys[scancode - 128]; +} + +int gc_kbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode) +{ + static int prev_scancode; + + /* special prefix scancodes.. */ + if (scancode == 0xe0 || scancode == 0xe1) { + prev_scancode = scancode; + return 0; + } + + /* 0xFF is sent by a few keyboards, ignore it. 0x00 is error */ + if (scancode == 0x00 || scancode == 0xff) { + prev_scancode = 0; + return 0; + } + + scancode &= 0x7f; + + if (prev_scancode) { + /* + * usually it will be 0xe0, but a Pause key generates + * e1 1d 45 e1 9d c5 when pressed, and nothing when released + */ + if (prev_scancode != 0xe0) { + if (prev_scancode == 0xe1 && scancode == 0x1d) { + prev_scancode = 0x100; + return 0; + } else if (prev_scancode == 0x100 && scancode == 0x45) { + *keycode = E1_PAUSE; + prev_scancode = 0; + } else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown e1 escape sequence\n"); +#endif + prev_scancode = 0; + return 0; + } + } else { + prev_scancode = 0; + /* + * The keyboard maintains its own internal caps lock and + * num lock statuses. In caps lock mode E0 AA precedes make + * code and E0 2A follows break code. In num lock mode, + * E0 2A precedes make code and E0 AA follows break code. + * We do our own book-keeping, so we will just ignore these. + */ + /* + * For my keyboard there is no caps lock mode, but there are + * both Shift-L and Shift-R modes. The former mode generates + * E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs. + * So, we should also ignore the latter. - aeb@cwi.nl + */ + if (scancode == 0x2a || scancode == 0x36) + return 0; + + if (e0_keys[scancode]) + *keycode = e0_keys[scancode]; + else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown scancode e0 %02x\n", + scancode); +#endif + return 0; + } + } + } else if (scancode >= SC_LIM) { + /* This happens with the FOCUS 9000 keyboard + Its keys PF1..PF12 are reported to generate + 55 73 77 78 79 7a 7b 7c 74 7e 6d 6f + Moreover, unless repeated, they do not generate + key-down events, so we have to zero up_flag below */ + /* Also, Japanese 86/106 keyboards are reported to + generate 0x73 and 0x7d for \ - and \ | respectively. */ + /* Also, some Brazilian keyboard is reported to produce + 0x73 and 0x7e for \ ? and KP-dot, respectively. */ + + *keycode = high_keys[scancode - SC_LIM]; + + if (!*keycode) { + if (!raw_mode) { +#ifdef KBD_REPORT_UNKN + printk(KERN_INFO "keyboard: unrecognized scancode (%02x)" + " - ignored\n", scancode); +#endif + } + return 0; + } + } else + *keycode = scancode; + return 1; +} + +// this table converts the hardware dependent codes of a MF-2 Keyboard to +// the codes normally comming out of a i8042. This table is 128 Bytes too +// big, but for stability reasons it should be kept like it is! +// There is no range checking in the code! +static int mf_two_kbdmap[256] = { + 00, 67, 65, 63, 61, 59, 60, 88, 00, 68, 66, 64, 62, 15, 41, 00, + 00, 56, 42, 00, 29, 16, 02, 00, 00, 00, 44, 31, 30, 17, 03, 00, + 00, 46, 45, 32, 18, 05, 04, 00, 00, 57, 47, 33, 20, 19, 06, 00, + 00, 49, 48, 35, 34, 21, 7, 00, 00, 00, 50, 36, 22, 8, 9, 00, + 00, 51, 37, 23, 24, 11, 10, 00, 00, 52, 53, 38, 39, 25, 12, 00, + 00, 00, 40, 00, 26, 13, 00, 00, 58, 54, 28, 27, 00, 43, 00, 00, + 00, 86, 00, 00, 00, 00, 14, 00, 00, 79, 00, 75, 71, 00, 00, 00, + 82, 83, 80, 76, 77, 72, 01, 69, 87, 78, 81, 74, 55, 73, 70, 00, + 00, 00, 00, 65, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, + 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 }; + + +// some texts displayed by the proc_file_system +static char *kbd_sniff[2] = { "off", "on" }; +static char *kbd_sniff_mode[2] = { "passive", "active" }; + +#define PASSIVE 0 +#define ACTIVE 1 + +// is the sniffer active (1) or inactive (0) +static int SNIFFER = 0; +// do we get a copy (SNIFFMODE = PASSIVE) or do we get the original data (SNIFFMODE = ACTIVE) +// and have to reinsert the data +static int SNIFFMODE = PASSIVE; + +// we allow only one process to sniff +static int sniffer_in_use = 0; + +// timeout for the keyboard sniffer -1 = blocking, otherwise timeout in msecs +static long sniffer_timeout = -1; + +// the value we sniffed from the keyboard +static int sniffed_value; + +static char *smartio_version = "1.02 MF-II compatibility patch "; +static char *smartio_date = "Aug-27-2001"; + +static int sio_reset_flag; +static int kbd_press_flag; + +static void send_SSP_msg(unchar *pBuf, int num) +{ + ushort tmp; + int i; + + for (i=0;i 7)) + return 0xFFFF; + + CONV_ADC_CMD.Opt[0] = (unchar) channel; + + lock_smartio(&flags); + send_SSP_msg((unchar *) &CONV_ADC_CMD, 3); + unlock_smartio(&flags); + + interruptible_sleep_on(&smartio_adc_queue); + + return adc_value & 0x3FF; +} + +static ushort read_sio_port(int port) +{ + unsigned long flags; + ushort ret; + + if ((port < SMARTIO_PORT_B) || (port > SMARTIO_PORT_D)) + return 0xFFFF; + + READ_PORT_CMD.Code = (unchar) port; + + lock_smartio(&flags); + send_SSP_msg((unchar *) &READ_PORT_CMD, 2); + ret = read_SSP_response(1); + unlock_smartio(&flags); + + return ret; +} + +static ushort read_sio_kpd(void) +{ + long timeout; + + // kpd_timeout is mSec order + // interrupt_sleep_on_timeout is based on 10msec timer tick + if (kpd_timeout == -1) { + interruptible_sleep_on(&smartio_kpd_queue); + } + else { + timeout = interruptible_sleep_on_timeout(&smartio_kpd_queue, + kpd_timeout/10); + if (timeout == 0) { + // timeout without keypad input + return 0xFFFF; + } + } + return kpd_value; +} + +static ushort read_sio_sniff(void) +{ + long timeout; + + // kpd_timeout is mSec order + // interrupt_sleep_on_timeout is based on 10msec timer tick + if (sniffer_timeout == -1) { + interruptible_sleep_on(&sniffer_queue); + } + else { + timeout = interruptible_sleep_on_timeout(&sniffer_queue, + sniffer_timeout/10); + if (timeout == 0) { + // timeout without keypad input + return -1; + } + } + return (ushort)sniffed_value; +} + +static struct sio_ver { + uint DevVer; + uint DevType; + uint FwLevel; +}; + +static ushort read_sio_version(struct sio_ver *ptr) +{ + unsigned long flags; + ushort ret; + + // Read Device Version + lock_smartio(&flags); + send_SSP_msg((unchar *) &READ_DEVVER_CMD, 2); + ret = read_SSP_response(1); + unlock_smartio(&flags); + ptr->DevVer = (uint)ret; + // Read Device Type + lock_smartio(&flags); + send_SSP_msg((unchar *) &READ_DEVTYPE_CMD, 2); + ret = read_SSP_response(2); + unlock_smartio(&flags); + // swap MSB & LSB + ret = ((ret & 0xFF) << 8) | ((ret & 0xFF00) >> 8); + ptr->DevType = (uint)ret; + // Read Firmware Level + lock_smartio(&flags); + send_SSP_msg((unchar *) &READ_FWLEVEL_CMD, 2); + ret = read_SSP_response(2); + unlock_smartio(&flags); + // swap MSB & LSB + ret = ((ret & 0xFF) << 8) | ((ret & 0xFF00) >> 8); + ptr->FwLevel = (uint)ret; + + return 0; +} + +static ssize_t sio_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + struct inode *inode = file->f_dentry->d_inode; + unsigned int minor = MINOR(inode->i_rdev); + ushort *ret = (ushort *)buf; + + switch (minor) { + case SMARTIO_ADC: + if ((*ret = read_sio_adc(buf[0])) != 0xFFFF) + return sizeof(ushort); // 2 bytes + case SMARTIO_PORT_B: + case SMARTIO_PORT_C: + case SMARTIO_PORT_D: + if ((*ret = read_sio_port(minor)) != 0xFFFF) + return sizeof(ushort); + case SMARTIO_VERSION: + if ((read_sio_version((struct sio_ver *)buf)) != 0xFFFF) + return sizeof(struct sio_ver); + case SMARTIO_KEYPAD: + if ((*ret = read_sio_kpd()) != 0xFFFF) + return sizeof(ushort); + case SMARTIO_KBD_SNIFFER: + if ((*ret = read_sio_sniff()) != (ushort)-1) + return 1; + default : + return -ENXIO; + } +} + +static SMARTIO_CMD WRITE_PORT_CMD = { 0x81, 0x00, { 0x00, 0x00 } }; +static SMARTIO_CMD SELECT_OPT_CMD = { 0x80, 0x00, { 0x00, 0x00 } }; +static SMARTIO_CMD CONTROL_BL_CMD = { 0x80, 0x00, { 0x00, 0x00 } }; +static SMARTIO_CMD CONTRAST_BL_CMD = { 0x80, 0x21, { 0x00, 0x00 } }; +static SMARTIO_CMD CONTROL_KPD_CMD = { 0x80, 0x27, { 0x00, 0x00 } }; +static SMARTIO_CMD CONTROL_VEE_CMD = { 0x80, 0x22, { 0x00, 0x00 } }; + +static ushort write_sio_port(int port, unchar value) +{ + unsigned long flags; + + if ((port < SMARTIO_PORT_B) || (port > SMARTIO_PORT_D)) + return 0xFFFF; + + WRITE_PORT_CMD.Code = (unchar) port; + WRITE_PORT_CMD.Opt[0] = (unchar) value; + + lock_smartio(&flags); + send_SSP_msg((unchar *) &WRITE_PORT_CMD, 3); + unlock_smartio(&flags); + + return 0; +} + +static ushort write_sio_select(unchar select) +{ + unsigned long flags; + + if ((select < 1) || (select > 2)) + return 0xFFFF; + + SELECT_OPT_CMD.Code = (unchar) (select + 0x28); + + lock_smartio(&flags); + send_SSP_msg((unchar *) &SELECT_OPT_CMD, 2); + unlock_smartio(&flags); + + return 0; +} + +static ushort control_sio_backlite(int cmd, int value) +{ + unsigned long flags; + + if (cmd == SMARTIO_BL_CONTRAST) { + value &= 0xFF; + CONTRAST_BL_CMD.Opt[0] = (unchar) value; + + lock_smartio(&flags); + send_SSP_msg((unchar *) &CONTRAST_BL_CMD, 3); + unlock_smartio(&flags); + } + else if (cmd == SMARTIO_BL_CONTROL) { + if (value == 0x00) { + // Backlite OFF + CONTROL_BL_CMD.Code = 0x24; + } + else { + // Backlite ON + CONTROL_BL_CMD.Code = 0x23; + } + lock_smartio(&flags); + send_SSP_msg((unchar *) &CONTROL_BL_CMD, 2); + unlock_smartio(&flags); + } + else + return 0xFFFF; + + return 0; +} + +static ushort control_sio_keypad(int x, int y) +{ + unsigned long flags; + + if ( (x<1) || (x>8) || (y<1) || (y>8)) { + return 0xFFFF; + } + + CONTROL_KPD_CMD.Opt[0] = (unchar) x; + CONTROL_KPD_CMD.Opt[1] = (unchar) y; + + lock_smartio(&flags); + send_SSP_msg((unchar *) &CONTROL_KPD_CMD, 4); + unlock_smartio(&flags); + + return 0; +} + +static ushort control_sio_vee(int value) +{ + unsigned long flags; + + value &= 0xFF; + CONTROL_VEE_CMD.Opt[0] = (unchar) value; + + lock_smartio(&flags); + send_SSP_msg((unchar *) &CONTROL_VEE_CMD, 3); + unlock_smartio(&flags); + + return 0; +} + +static ssize_t sio_write(struct file *file, const char *buf, size_t cont, loff_t *ppos) +{ + struct inode *inode = file->f_dentry->d_inode; + unsigned int minor = MINOR(inode->i_rdev); + + switch (minor) { + case SMARTIO_PORT_B: + case SMARTIO_PORT_C: + case SMARTIO_PORT_D: + if (write_sio_port(minor, buf[0]) != 0xFFFF) + return 1; + case SMARTIO_SELECT_OPTION: + if (write_sio_select(buf[0]) != 0xFFFF) + return 1; + case SMARTIO_BACKLITE: + if (control_sio_backlite(SMARTIO_BL_CONTROL, buf[0]) != 0xFFFF) + return 1; + case SMARTIO_KEYPAD: + if (control_sio_keypad(buf[0], buf[1]) != 0xFFFF) + return 2; + case SMARTIO_VEE_PWM: + if (control_sio_vee(buf[0]) != 0xFFFF) + return 1; + case SMARTIO_KBD_SNIFFER: + // here are the scancodes injected + handle_scancode((unchar)buf[0], (buf[0] & 0x80) ? 0 : 1); + wake_up_interruptible(&keyboard_done_queue); + // give some time to process! File IO is a bit faster than manual typing ;-) + udelay(10000); + return 1; + default: + return -ENXIO; + } +} + +static unsigned int sio_poll(struct file *file, struct poll_table_struct *wait) +{ + return 0; +} + +static SMARTIO_CMD IOCTL_PORT_CMD = { 0x81, 0x00, { 0x00, 0x00 } }; + +static ushort ioctl_sio_port(int port, unchar value) +{ + unsigned long flags; + + if ((port < SMARTIO_PORT_B) || (port > SMARTIO_PORT_D)) + return 0xFFFF; + + IOCTL_PORT_CMD.Code = (unchar) port + 0x04; // 0x05 ~ 0x08 + if (port == SMARTIO_PORT_B) { + // Port B has 4 bits only + IOCTL_PORT_CMD.Opt[0] = (unchar) value & 0x0F; + } + else + IOCTL_PORT_CMD.Opt[0] = (unchar) value; + + lock_smartio(&flags); + send_SSP_msg((unchar *) &IOCTL_PORT_CMD, 3); + unlock_smartio(&flags); + + return 0; +} + +static int sio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + unsigned int minor = MINOR(inode->i_rdev); + unchar *buf = (unchar *)arg; + + switch (minor) { + case SMARTIO_PORT_B: + case SMARTIO_PORT_C: + case SMARTIO_PORT_D: + if (cmd == SMARTIO_PORT_CONFIG) { + if (ioctl_sio_port(minor, buf[0]) != 0xFFFF) + return 0; + } + return -EINVAL; + case SMARTIO_SELECT_OPTION: + if (write_sio_select(buf[0]) != 0xFFFF) return 0; + return -EINVAL; + case SMARTIO_BACKLITE: + if (cmd == SMARTIO_BL_CONTROL) { + if (control_sio_backlite(SMARTIO_BL_CONTROL, buf[0]) != 0xFFFF) return 0; + } + else if (cmd == SMARTIO_BL_CONTRAST) { + if (control_sio_backlite(SMARTIO_BL_CONTRAST, buf[0]) != 0xFFFF) return 0; + } + else return -EINVAL; + case SMARTIO_KEYPAD: + if (cmd == SMARTIO_KPD_TIMEOUT) { + kpd_timeout = *(long*)buf; + return 0; + } + else if (cmd == SMARTIO_KPD_SETUP) { + if (control_sio_keypad(buf[0], buf[1]) != 0xFFFF) return 0; + } + return -EINVAL; + case SMARTIO_VEE_PWM: + if (control_sio_vee(buf[0]) != 0xFFFF) return 0; + return -EINVAL; + case SMARTIO_KBD_SNIFFER: + if (cmd == SMARTIO_SNIFFER_TIMEOUT) { + sniffer_timeout = *(long*)buf; + if (sniffer_timeout < 0) sniffer_timeout = -1; + // the value will be devided by 10 later on + if (!sniffer_timeout) sniffer_timeout = 10; + return 0; + } + return -EINVAL; + default: + return -ENXIO; + } +} + +static int sio_open(struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR(inode->i_rdev); + + // we open all by default. we only have a special handler for the kbd sniffer + switch (minor) { + case SMARTIO_KBD_SNIFFER: + if (sniffer_in_use) return -EBUSY; + sniffer_in_use = 1; + SNIFFER = 1; + // sniff in active or passive mode + if ((file->f_flags & O_RDWR) == O_RDWR) SNIFFMODE = 1; else SNIFFMODE = 0; + // do we have a blocking or non blocking sniffer? + if ((file->f_flags & O_NONBLOCK) == O_NONBLOCK) sniffer_timeout = 100; else sniffer_timeout = -1; + break; + default: + break; + } + return 0; +} + +static int sio_close(struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR(inode->i_rdev); + + switch (minor) { + case SMARTIO_KBD_SNIFFER: + SNIFFER = 0; + SNIFFMODE = 0; + sniffer_in_use = 0; + break; + default: + break; + } + return 0; +} + +static struct file_operations sio_fops = { + read: sio_read, + write: sio_write, + poll: sio_poll, + ioctl: sio_ioctl, + open: sio_open, + release: sio_close, +}; + +static struct proc_dir_entry *sio_dir, *parent_dir = NULL; + +#define SMARTIO_MAJOR 58 +#define MAJOR_NR SMARTIO_MAJOR + +#define PROC_NAME "sio" + +static int sio_read_proc(char *buf, char **start, off_t pos, int count, int *eof, void *data) +{ + char *p = buf; + + p += sprintf(p, "ADS SMARTIO Status: \n"); + p += sprintf(p, "\t Keyboard Interrupt : %lu\n", kbd_int); + p += sprintf(p, "\t Keypad Interrupt : %lu\n", kpd_int); + p += sprintf(p, "\t ADC Interrupt : %lu\n", adc_int); + p += sprintf(p, "\t Keyboard Sniffer : %s mode : %s\n", kbd_sniff[ SNIFFER ], kbd_sniff_mode [ SNIFFMODE ]); + + return (p-buf); +} + +#ifdef CONFIG_PM +static int pm_smartio_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + switch (rqst) { + case PM_RESUME: + gc_sio_init(); + break; + case PM_SUSPEND: + // 4/5/01 Woojung + // It checks Keybard received pair of press/release code. + // System can sleep before receiving release code + if (kbd_press_flag) { + interruptible_sleep_on(&keyboard_done_queue); + } + break; + } + + return 0; +} +#endif + +void __init sio_init(void) +{ + if (register_chrdev(MAJOR_NR, "sio", &sio_fops)) { + printk("smartio : unable to get major %d\n", MAJOR_NR); + return; + } + else { + printk("smartio driver initialized. version %s, date:%s\n", + smartio_version, smartio_date); + + if (sio_reset_flag != 1) { + gc_sio_init(); + if (request_irq(ADS_AVR_IRQ, gc_sio_interrupt,0,"sio",NULL) != 0){ + printk("smartio : Could not allocate IRQ!\n"); + return; + } + } + + if ((sio_dir = create_proc_entry(PROC_NAME, 0, parent_dir)) == NULL) { + printk("smartio : Unable to create /proc entry\n"); + return; + } + else { + sio_dir->read_proc = sio_read_proc; +#ifdef CONFIG_PM + pm_register(PM_SYS_DEV, PM_SYS_KBC, pm_smartio_callback); +#endif + } + } +} diff -urN orig/drivers/char/gckeymap.c linux/drivers/char/gckeymap.c --- orig/drivers/char/gckeymap.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/char/gckeymap.c Tue Aug 28 20:26:52 2001 @@ -0,0 +1,262 @@ +/* Do not edit this file! It was automatically generated by */ +/* loadkeys --mktable defkeymap.map > defkeymap.c */ + +#include +#include +#include + +u_short plain_map[NR_KEYS] = { + 0xf200, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, + 0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009, + 0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, + 0xfb6f, 0xfb70, 0xf05b, 0xf05d, 0xf201, 0xf702, 0xfb61, 0xfb73, + 0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03b, + 0xf027, 0xf060, 0xf700, 0xf05c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, + 0xfb62, 0xfb6e, 0xfb6d, 0xf02c, 0xf02e, 0xf02f, 0xf700, 0xf30c, + 0xf703, 0xf020, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, + 0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, + 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, + 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03c, 0xf10a, + 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short shift_map[NR_KEYS] = { + 0xf200, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, + 0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009, + 0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, + 0xfb4f, 0xfb50, 0xf07b, 0xf07d, 0xf201, 0xf702, 0xfb41, 0xfb53, + 0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03a, + 0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, + 0xfb42, 0xfb4e, 0xfb4d, 0xf03c, 0xf03e, 0xf03f, 0xf700, 0xf30c, + 0xf703, 0xf020, 0xf207, 0xf10a, 0xf10b, 0xf10c, 0xf10d, 0xf10e, + 0xf10f, 0xf110, 0xf111, 0xf112, 0xf113, 0xf213, 0xf203, 0xf307, + 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, + 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03e, 0xf10a, + 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf20b, 0xf601, 0xf602, 0xf117, 0xf600, 0xf20a, 0xf115, 0xf116, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short altgr_map[NR_KEYS] = { + 0xf200, 0xf200, 0xf200, 0xf040, 0xf200, 0xf024, 0xf200, 0xf200, + 0xf07b, 0xf05b, 0xf05d, 0xf07d, 0xf05c, 0xf200, 0xf200, 0xf200, + 0xfb71, 0xfb77, 0xf918, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, + 0xfb6f, 0xfb70, 0xf200, 0xf07e, 0xf201, 0xf702, 0xf914, 0xfb73, + 0xf917, 0xf919, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf200, + 0xf200, 0xf200, 0xf700, 0xf200, 0xfb7a, 0xfb78, 0xf916, 0xfb76, + 0xf915, 0xfb6e, 0xfb6d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c, + 0xf703, 0xf200, 0xf207, 0xf50c, 0xf50d, 0xf50e, 0xf50f, 0xf510, + 0xf511, 0xf512, 0xf513, 0xf514, 0xf515, 0xf208, 0xf202, 0xf911, + 0xf912, 0xf913, 0xf30b, 0xf90e, 0xf90f, 0xf910, 0xf30a, 0xf90b, + 0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf07c, 0xf516, + 0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short ctrl_map[NR_KEYS] = { + 0xf200, 0xf200, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e, + 0xf01f, 0xf07f, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf008, 0xf200, + 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, + 0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf201, 0xf702, 0xf001, 0xf013, + 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, + 0xf007, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, + 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf20e, 0xf07f, 0xf700, 0xf30c, + 0xf703, 0xf000, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, + 0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf204, 0xf307, + 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, + 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf200, 0xf10a, + 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short shift_ctrl_map[NR_KEYS] = { + 0xf200, 0xf200, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf200, 0xf200, + 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, + 0xf00f, 0xf010, 0xf200, 0xf200, 0xf201, 0xf702, 0xf001, 0xf013, + 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, + 0xf200, 0xf200, 0xf700, 0xf200, 0xf01a, 0xf018, 0xf003, 0xf016, + 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c, + 0xf703, 0xf200, 0xf207, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf208, 0xf200, 0xf307, + 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, + 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short alt_map[NR_KEYS] = { + 0xf200, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, + 0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809, + 0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, + 0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf702, 0xf861, 0xf873, + 0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b, + 0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876, + 0xf862, 0xf86e, 0xf86d, 0xf82c, 0xf82e, 0xf82f, 0xf700, 0xf30c, + 0xf703, 0xf820, 0xf207, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, + 0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf209, 0xf907, + 0xf908, 0xf909, 0xf30b, 0xf904, 0xf905, 0xf906, 0xf30a, 0xf901, + 0xf902, 0xf903, 0xf900, 0xf310, 0xf206, 0xf200, 0xf83c, 0xf50a, + 0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf118, 0xf210, 0xf211, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short ctrl_alt_map[NR_KEYS] = { + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, + 0xf80f, 0xf810, 0xf200, 0xf200, 0xf201, 0xf702, 0xf801, 0xf813, + 0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, + 0xf200, 0xf200, 0xf700, 0xf200, 0xf81a, 0xf818, 0xf803, 0xf816, + 0xf802, 0xf80e, 0xf80d, 0xf200, 0xf200, 0xf200, 0xf700, 0xf30c, + 0xf703, 0xf200, 0xf207, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, + 0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf200, 0xf307, + 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, + 0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf200, 0xf50a, + 0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf200, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +ushort *key_maps[MAX_NR_KEYMAPS] = { + plain_map, shift_map, altgr_map, 0, + ctrl_map, shift_ctrl_map, 0, 0, + alt_map, 0, 0, 0, + ctrl_alt_map, 0 +}; + +unsigned int keymap_count = 7; + +/* + * Philosophy: most people do not define more strings, but they who do + * often want quite a lot of string space. So, we statically allocate + * the default and allocate dynamically in chunks of 512 bytes. + */ + +char func_buf[] = { + '\033', '[', '[', 'A', 0, + '\033', '[', '[', 'B', 0, + '\033', '[', '[', 'C', 0, + '\033', '[', '[', 'D', 0, + '\033', '[', '[', 'E', 0, + '\033', '[', '1', '7', '~', 0, + '\033', '[', '1', '8', '~', 0, + '\033', '[', '1', '9', '~', 0, + '\033', '[', '2', '0', '~', 0, + '\033', '[', '2', '1', '~', 0, + '\033', '[', '2', '3', '~', 0, + '\033', '[', '2', '4', '~', 0, + '\033', '[', '2', '5', '~', 0, + '\033', '[', '2', '6', '~', 0, + '\033', '[', '2', '8', '~', 0, + '\033', '[', '2', '9', '~', 0, + '\033', '[', '3', '1', '~', 0, + '\033', '[', '3', '2', '~', 0, + '\033', '[', '3', '3', '~', 0, + '\033', '[', '3', '4', '~', 0, + '\033', '[', '1', '~', 0, + '\033', '[', '2', '~', 0, + '\033', '[', '3', '~', 0, + '\033', '[', '4', '~', 0, + '\033', '[', '5', '~', 0, + '\033', '[', '6', '~', 0, + '\033', '[', 'M', 0, + '\033', '[', 'P', 0, +}; + +char *funcbufptr = func_buf; +int funcbufsize = sizeof(func_buf); +int funcbufleft = 0; /* space left */ + +char *func_table[MAX_NR_FUNC] = { + func_buf + 0, + func_buf + 5, + func_buf + 10, + func_buf + 15, + func_buf + 20, + func_buf + 25, + func_buf + 31, + func_buf + 37, + func_buf + 43, + func_buf + 49, + func_buf + 55, + func_buf + 61, + func_buf + 67, + func_buf + 73, + func_buf + 79, + func_buf + 85, + func_buf + 91, + func_buf + 97, + func_buf + 103, + func_buf + 109, + func_buf + 115, + func_buf + 120, + func_buf + 125, + func_buf + 130, + func_buf + 135, + func_buf + 140, + func_buf + 145, + 0, + 0, + func_buf + 149, + 0, +}; + +struct kbdiacr accent_table[MAX_DIACR] = { + {'`', 'A', '\300'}, {'`', 'a', '\340'}, + {'\'', 'A', '\301'}, {'\'', 'a', '\341'}, + {'^', 'A', '\302'}, {'^', 'a', '\342'}, + {'~', 'A', '\303'}, {'~', 'a', '\343'}, + {'"', 'A', '\304'}, {'"', 'a', '\344'}, + {'O', 'A', '\305'}, {'o', 'a', '\345'}, + {'0', 'A', '\305'}, {'0', 'a', '\345'}, + {'A', 'A', '\305'}, {'a', 'a', '\345'}, + {'A', 'E', '\306'}, {'a', 'e', '\346'}, + {',', 'C', '\307'}, {',', 'c', '\347'}, + {'`', 'E', '\310'}, {'`', 'e', '\350'}, + {'\'', 'E', '\311'}, {'\'', 'e', '\351'}, + {'^', 'E', '\312'}, {'^', 'e', '\352'}, + {'"', 'E', '\313'}, {'"', 'e', '\353'}, + {'`', 'I', '\314'}, {'`', 'i', '\354'}, + {'\'', 'I', '\315'}, {'\'', 'i', '\355'}, + {'^', 'I', '\316'}, {'^', 'i', '\356'}, + {'"', 'I', '\317'}, {'"', 'i', '\357'}, + {'-', 'D', '\320'}, {'-', 'd', '\360'}, + {'~', 'N', '\321'}, {'~', 'n', '\361'}, + {'`', 'O', '\322'}, {'`', 'o', '\362'}, + {'\'', 'O', '\323'}, {'\'', 'o', '\363'}, + {'^', 'O', '\324'}, {'^', 'o', '\364'}, + {'~', 'O', '\325'}, {'~', 'o', '\365'}, + {'"', 'O', '\326'}, {'"', 'o', '\366'}, + {'/', 'O', '\330'}, {'/', 'o', '\370'}, + {'`', 'U', '\331'}, {'`', 'u', '\371'}, + {'\'', 'U', '\332'}, {'\'', 'u', '\372'}, + {'^', 'U', '\333'}, {'^', 'u', '\373'}, + {'"', 'U', '\334'}, {'"', 'u', '\374'}, + {'\'', 'Y', '\335'}, {'\'', 'y', '\375'}, + {'T', 'H', '\336'}, {'t', 'h', '\376'}, + {'s', 's', '\337'}, {'"', 'y', '\377'}, + {'s', 'z', '\337'}, {'i', 'j', '\377'}, +}; + +unsigned int accent_table_size = 68; diff -urN orig/drivers/char/gckeymap.map linux/drivers/char/gckeymap.map --- orig/drivers/char/gckeymap.map Thu Jan 1 01:00:00 1970 +++ linux/drivers/char/gckeymap.map Tue Aug 28 20:26:52 2001 @@ -0,0 +1,357 @@ +# Default kernel keymap. This uses 7 modifier combinations. +keymaps 0-2,4-5,8,12 +# Change the above line into +# keymaps 0-2,4-6,8,12 +# in case you want the entries +# altgr control keycode 83 = Boot +# altgr control keycode 111 = Boot +# below. +# +# In fact AltGr is used very little, and one more keymap can +# be saved by mapping AltGr to Alt (and adapting a few entries): +# keycode 100 = Alt +# +keycode 1 = Escape Escape + alt keycode 1 = Meta_Escape +keycode 2 = one exclam + alt keycode 2 = Meta_one +keycode 3 = two at at + control keycode 3 = nul + shift control keycode 3 = nul + alt keycode 3 = Meta_two +keycode 4 = three numbersign + control keycode 4 = Escape + alt keycode 4 = Meta_three +keycode 5 = four dollar dollar + control keycode 5 = Control_backslash + alt keycode 5 = Meta_four +keycode 6 = five percent + control keycode 6 = Control_bracketright + alt keycode 6 = Meta_five +keycode 7 = six asciicircum + control keycode 7 = Control_asciicircum + alt keycode 7 = Meta_six +keycode 8 = seven ampersand braceleft + control keycode 8 = Control_underscore + alt keycode 8 = Meta_seven +keycode 9 = eight asterisk bracketleft + control keycode 9 = Delete + alt keycode 9 = Meta_eight +keycode 10 = nine parenleft bracketright + alt keycode 10 = Meta_nine +keycode 11 = zero parenright braceright + alt keycode 11 = Meta_zero +keycode 12 = minus underscore backslash + control keycode 12 = Control_underscore + shift control keycode 12 = Control_underscore + alt keycode 12 = Meta_minus +keycode 13 = equal plus + alt keycode 13 = Meta_equal +keycode 14 = Delete Delete + control keycode 14 = BackSpace + alt keycode 14 = Meta_Delete +keycode 15 = Tab Tab + alt keycode 15 = Meta_Tab +keycode 16 = q +keycode 17 = w +keycode 18 = e + altgr keycode 18 = Hex_E +keycode 19 = r +keycode 20 = t +keycode 21 = y +keycode 22 = u +keycode 23 = i +keycode 24 = o +keycode 25 = p +keycode 26 = bracketleft braceleft + control keycode 26 = Escape + alt keycode 26 = Meta_bracketleft +keycode 27 = bracketright braceright asciitilde + control keycode 27 = Control_bracketright + alt keycode 27 = Meta_bracketright +keycode 28 = Return + alt keycode 28 = Meta_Control_m +keycode 29 = Control +keycode 30 = a + altgr keycode 30 = Hex_A +keycode 31 = s +keycode 32 = d + altgr keycode 32 = Hex_D +keycode 33 = f + altgr keycode 33 = Hex_F +keycode 34 = g +keycode 35 = h +keycode 36 = j +keycode 37 = k +keycode 38 = l +keycode 39 = semicolon colon + alt keycode 39 = Meta_semicolon +keycode 40 = apostrophe quotedbl + control keycode 40 = Control_g + alt keycode 40 = Meta_apostrophe +keycode 41 = grave asciitilde + control keycode 41 = nul + alt keycode 41 = Meta_grave +keycode 42 = Shift +keycode 43 = backslash bar + control keycode 43 = Control_backslash + alt keycode 43 = Meta_backslash +keycode 44 = z +keycode 45 = x +keycode 46 = c + altgr keycode 46 = Hex_C +keycode 47 = v +keycode 48 = b + altgr keycode 48 = Hex_B +keycode 49 = n +keycode 50 = m +keycode 51 = comma less + alt keycode 51 = Meta_comma +keycode 52 = period greater + control keycode 52 = Compose + alt keycode 52 = Meta_period +keycode 53 = slash question + control keycode 53 = Delete + alt keycode 53 = Meta_slash +keycode 54 = Shift +keycode 55 = KP_Multiply +keycode 56 = Alt +keycode 57 = space space + control keycode 57 = nul + alt keycode 57 = Meta_space +keycode 58 = Caps_Lock +keycode 59 = F1 F11 Console_13 + control keycode 59 = F1 + alt keycode 59 = Console_1 + control alt keycode 59 = Console_1 +keycode 60 = F2 F12 Console_14 + control keycode 60 = F2 + alt keycode 60 = Console_2 + control alt keycode 60 = Console_2 +keycode 61 = F3 F13 Console_15 + control keycode 61 = F3 + alt keycode 61 = Console_3 + control alt keycode 61 = Console_3 +keycode 62 = F4 F14 Console_16 + control keycode 62 = F4 + alt keycode 62 = Console_4 + control alt keycode 62 = Console_4 +keycode 63 = F5 F15 Console_17 + control keycode 63 = F5 + alt keycode 63 = Console_5 + control alt keycode 63 = Console_5 +keycode 64 = F6 F16 Console_18 + control keycode 64 = F6 + alt keycode 64 = Console_6 + control alt keycode 64 = Console_6 +keycode 65 = F7 F17 Console_19 + control keycode 65 = F7 + alt keycode 65 = Console_7 + control alt keycode 65 = Console_7 +keycode 66 = F8 F18 Console_20 + control keycode 66 = F8 + alt keycode 66 = Console_8 + control alt keycode 66 = Console_8 +keycode 67 = F9 F19 Console_21 + control keycode 67 = F9 + alt keycode 67 = Console_9 + control alt keycode 67 = Console_9 +keycode 68 = F10 F20 Console_22 + control keycode 68 = F10 + alt keycode 68 = Console_10 + control alt keycode 68 = Console_10 +keycode 69 = Num_Lock + shift keycode 69 = Bare_Num_Lock +keycode 70 = Scroll_Lock Show_Memory Show_Registers + control keycode 70 = Show_State + alt keycode 70 = Scroll_Lock +keycode 71 = KP_7 + alt keycode 71 = Ascii_7 + altgr keycode 71 = Hex_7 +keycode 72 = KP_8 + alt keycode 72 = Ascii_8 + altgr keycode 72 = Hex_8 +keycode 73 = KP_9 + alt keycode 73 = Ascii_9 + altgr keycode 73 = Hex_9 +keycode 74 = KP_Subtract +keycode 75 = KP_4 + alt keycode 75 = Ascii_4 + altgr keycode 75 = Hex_4 +keycode 76 = KP_5 + alt keycode 76 = Ascii_5 + altgr keycode 76 = Hex_5 +keycode 77 = KP_6 + alt keycode 77 = Ascii_6 + altgr keycode 77 = Hex_6 +keycode 78 = KP_Add +keycode 79 = KP_1 + alt keycode 79 = Ascii_1 + altgr keycode 79 = Hex_1 +keycode 80 = KP_2 + alt keycode 80 = Ascii_2 + altgr keycode 80 = Hex_2 +keycode 81 = KP_3 + alt keycode 81 = Ascii_3 + altgr keycode 81 = Hex_3 +keycode 82 = KP_0 + alt keycode 82 = Ascii_0 + altgr keycode 82 = Hex_0 +keycode 83 = KP_Period +# altgr control keycode 83 = Boot + control alt keycode 83 = Boot +keycode 84 = Last_Console +keycode 85 = +keycode 86 = less greater bar + alt keycode 86 = Meta_less +keycode 87 = F11 F11 Console_23 + control keycode 87 = F11 + alt keycode 87 = Console_11 + control alt keycode 87 = Console_11 +keycode 88 = F12 F12 Console_24 + control keycode 88 = F12 + alt keycode 88 = Console_12 + control alt keycode 88 = Console_12 +keycode 89 = +keycode 90 = +keycode 91 = +keycode 92 = +keycode 93 = +keycode 94 = +keycode 95 = +keycode 96 = KP_Enter +keycode 97 = Control +keycode 98 = KP_Divide +keycode 99 = Control_backslash + control keycode 99 = Control_backslash + alt keycode 99 = Control_backslash +keycode 100 = AltGr +keycode 101 = Break +keycode 102 = Find +keycode 103 = Up +keycode 104 = Prior + shift keycode 104 = Scroll_Backward +keycode 105 = Left + alt keycode 105 = Decr_Console +keycode 106 = Right + alt keycode 106 = Incr_Console +keycode 107 = Select +keycode 108 = Down +keycode 109 = Next + shift keycode 109 = Scroll_Forward +keycode 110 = Insert +keycode 111 = Remove +# altgr control keycode 111 = Boot + control alt keycode 111 = Boot +keycode 112 = Macro +keycode 113 = F13 +keycode 114 = F14 +keycode 115 = Help +keycode 116 = Do +keycode 117 = F17 +keycode 118 = KP_MinPlus +keycode 119 = Pause +keycode 120 = +keycode 121 = +keycode 122 = +keycode 123 = +keycode 124 = +keycode 125 = +keycode 126 = +keycode 127 = +string F1 = "\033[[A" +string F2 = "\033[[B" +string F3 = "\033[[C" +string F4 = "\033[[D" +string F5 = "\033[[E" +string F6 = "\033[17~" +string F7 = "\033[18~" +string F8 = "\033[19~" +string F9 = "\033[20~" +string F10 = "\033[21~" +string F11 = "\033[23~" +string F12 = "\033[24~" +string F13 = "\033[25~" +string F14 = "\033[26~" +string F15 = "\033[28~" +string F16 = "\033[29~" +string F17 = "\033[31~" +string F18 = "\033[32~" +string F19 = "\033[33~" +string F20 = "\033[34~" +string Find = "\033[1~" +string Insert = "\033[2~" +string Remove = "\033[3~" +string Select = "\033[4~" +string Prior = "\033[5~" +string Next = "\033[6~" +string Macro = "\033[M" +string Pause = "\033[P" +compose '`' 'A' to 'À' +compose '`' 'a' to 'à' +compose '\'' 'A' to 'Á' +compose '\'' 'a' to 'á' +compose '^' 'A' to 'Â' +compose '^' 'a' to 'â' +compose '~' 'A' to 'Ã' +compose '~' 'a' to 'ã' +compose '"' 'A' to 'Ä' +compose '"' 'a' to 'ä' +compose 'O' 'A' to 'Å' +compose 'o' 'a' to 'å' +compose '0' 'A' to 'Å' +compose '0' 'a' to 'å' +compose 'A' 'A' to 'Å' +compose 'a' 'a' to 'å' +compose 'A' 'E' to 'Æ' +compose 'a' 'e' to 'æ' +compose ',' 'C' to 'Ç' +compose ',' 'c' to 'ç' +compose '`' 'E' to 'È' +compose '`' 'e' to 'è' +compose '\'' 'E' to 'É' +compose '\'' 'e' to 'é' +compose '^' 'E' to 'Ê' +compose '^' 'e' to 'ê' +compose '"' 'E' to 'Ë' +compose '"' 'e' to 'ë' +compose '`' 'I' to 'Ì' +compose '`' 'i' to 'ì' +compose '\'' 'I' to 'Í' +compose '\'' 'i' to 'í' +compose '^' 'I' to 'Î' +compose '^' 'i' to 'î' +compose '"' 'I' to 'Ï' +compose '"' 'i' to 'ï' +compose '-' 'D' to 'Ð' +compose '-' 'd' to 'ð' +compose '~' 'N' to 'Ñ' +compose '~' 'n' to 'ñ' +compose '`' 'O' to 'Ò' +compose '`' 'o' to 'ò' +compose '\'' 'O' to 'Ó' +compose '\'' 'o' to 'ó' +compose '^' 'O' to 'Ô' +compose '^' 'o' to 'ô' +compose '~' 'O' to 'Õ' +compose '~' 'o' to 'õ' +compose '"' 'O' to 'Ö' +compose '"' 'o' to 'ö' +compose '/' 'O' to 'Ø' +compose '/' 'o' to 'ø' +compose '`' 'U' to 'Ù' +compose '`' 'u' to 'ù' +compose '\'' 'U' to 'Ú' +compose '\'' 'u' to 'ú' +compose '^' 'U' to 'Û' +compose '^' 'u' to 'û' +compose '"' 'U' to 'Ü' +compose '"' 'u' to 'ü' +compose '\'' 'Y' to 'Ý' +compose '\'' 'y' to 'ý' +compose 'T' 'H' to 'Þ' +compose 't' 'h' to 'þ' +compose 's' 's' to 'ß' +compose '"' 'y' to 'ÿ' +compose 's' 'z' to 'ß' +compose 'i' 'j' to 'ÿ' diff -urN orig/drivers/char/pcmcia/synclink_cs.c linux/drivers/char/pcmcia/synclink_cs.c --- orig/drivers/char/pcmcia/synclink_cs.c Tue May 27 10:04:32 2003 +++ linux/drivers/char/pcmcia/synclink_cs.c Tue May 27 10:54:13 2003 @@ -3170,9 +3170,18 @@ } } +static struct pcmcia_driver mgslpc_driver = { + .owner = THIS_MODULE, + .drv = { + .name = "synclink_cs", + }, + .attach = mgslpc_attach, + .detach = mgslpc_detach, +}; + static int __init synclink_cs_init(void) { - servinfo_t serv; + int error; if (break_on_load) { mgslpc_get_text_ptr(); @@ -3181,13 +3190,9 @@ printk("%s %s\n", driver_name, driver_version); - CardServices(GetCardServicesInfo, &serv); - if (serv.Revision != CS_RELEASE_CODE) { - printk(KERN_NOTICE "synclink_cs: Card Services release " - "does not match!\n"); - return -1; - } - register_pccard_driver(&dev_info, &mgslpc_attach, &mgslpc_detach); + error = pcmcia_register_driver(&mgslpc_driver); + if (error) + return error; /* Initialize the tty_driver structure */ @@ -3274,7 +3279,9 @@ printk("%s(%d) failed to unregister callout driver err=%d\n", __FILE__,__LINE__,rc); - unregister_pccard_driver(&dev_info); + pcmcia_unregister_driver(&mgslpc_driver); + + /* XXX: this really needs to move into generic code.. */ while (dev_list != NULL) { if (dev_list->state & DEV_CONFIG) mgslpc_release((u_long)dev_list); diff -urN orig/drivers/char/sa1100-rtc.c linux/drivers/char/sa1100-rtc.c --- orig/drivers/char/sa1100-rtc.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/char/sa1100-rtc.c Wed Feb 26 09:50:41 2003 @@ -0,0 +1,476 @@ +/* + * Real Time Clock interface for Linux on StrongARM SA1100 + * + * Copyright (c) 2000 Nils Faerber + * + * Based on rtc.c by Paul Gortmaker + * Date/time conversion routines taken from arch/arm/kernel/time.c + * by Linus Torvalds and Russel King + * and the GNU C Library + * ( ... I love the GPL ... just take what you need! ;) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * 1.00 2001-06-08 Nicolas Pitre + * - added periodic timer capability using OSMR1 + * - flag compatibility with other RTC chips + * - permission checks for ioctls + * - major cleanup, partial rewrite + * + * 0.03 2001-03-07 CIH + * - Modify the bug setups RTC clock. + * + * 0.02 2001-02-27 Nils Faerber + * - removed mktime(), added alarm irq clear + * + * 0.01 2000-10-01 Nils Faerber + * - initial release + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define DRIVER_VERSION "1.00" + +#define TIMER_FREQ 3686400 + +#define RTC_DEF_DIVIDER 32768 - 1 +#define RTC_DEF_TRIM 0 + +/* Those are the bits from a classic RTC we want to mimic */ +#define RTC_IRQF 0x80 /* any of the following 3 is active */ +#define RTC_PF 0x40 +#define RTC_AF 0x20 +#define RTC_UF 0x10 + +static unsigned long rtc_status; +static unsigned long rtc_irq_data; +static unsigned long rtc_freq = 1024; + +static struct fasync_struct *rtc_async_queue; +static DECLARE_WAIT_QUEUE_HEAD(rtc_wait); + +extern spinlock_t rtc_lock; + +static const unsigned char days_in_mo[] = + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +#define is_leap(year) \ + ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) + +/* + * Converts seconds since 1970-01-01 00:00:00 to Gregorian date. + */ + +static void decodetime (unsigned long t, struct rtc_time *tval) +{ + long days, month, year, rem; + + days = t / 86400; + rem = t % 86400; + tval->tm_hour = rem / 3600; + rem %= 3600; + tval->tm_min = rem / 60; + tval->tm_sec = rem % 60; + tval->tm_wday = (4 + days) % 7; + +#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400) + + year = 1970 + days / 365; + days -= ((year - 1970) * 365 + + LEAPS_THRU_END_OF (year - 1) + - LEAPS_THRU_END_OF (1970 - 1)); + if (days < 0) { + year -= 1; + days += 365 + is_leap(year); + } + tval->tm_year = year - 1900; + tval->tm_yday = days + 1; + + month = 0; + if (days >= 31) { + days -= 31; + month++; + if (days >= (28 + is_leap(year))) { + days -= (28 + is_leap(year)); + month++; + while (days >= days_in_mo[month]) { + days -= days_in_mo[month]; + month++; + } + } + } + tval->tm_mon = month; + tval->tm_mday = days + 1; +} + +static void rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned int rtsr = RTSR; + + /* clear interrupt sources */ + RTSR = 0; + RTSR = (RTSR_AL|RTSR_HZ); + + /* clear alarm interrupt if it has occurred */ + if (rtsr & RTSR_AL) + rtsr &= ~RTSR_ALE; + RTSR = rtsr & (RTSR_ALE|RTSR_HZE); + + /* update irq data & counter */ + if (rtsr & RTSR_AL) + rtc_irq_data |= (RTC_AF|RTC_IRQF); + if (rtsr & RTSR_HZ) + rtc_irq_data |= (RTC_UF|RTC_IRQF); + rtc_irq_data += 0x100; + + /* wake up waiting process */ + wake_up_interruptible(&rtc_wait); + kill_fasync (&rtc_async_queue, SIGIO, POLL_IN); +} + +static void timer1_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + /* + * If we match for the first time, the periodic interrupt flag won't + * be set. If it is, then we did wrap around (very unlikely but + * still possible) and compute the amount of missed periods. + * The match reg is updated only when the data is actually retrieved + * to avoid unnecessary interrupts. + */ + OSSR = OSSR_M1; /* clear match on timer1 */ + if (rtc_irq_data & RTC_PF) { + rtc_irq_data += (rtc_freq * ((1<<30)/(TIMER_FREQ>>2))) << 8; + } else { + rtc_irq_data += (0x100|RTC_PF|RTC_IRQF); + } + + wake_up_interruptible(&rtc_wait); + kill_fasync (&rtc_async_queue, SIGIO, POLL_IN); +} + +static int rtc_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit (1, &rtc_status)) + return -EBUSY; + rtc_irq_data = 0; + return 0; +} + +static int rtc_release(struct inode *inode, struct file *file) +{ + spin_lock_irq (&rtc_lock); + RTSR = 0; + RTSR = (RTSR_AL|RTSR_HZ); + OIER &= ~OIER_E1; + OSSR = OSSR_M1; + spin_unlock_irq (&rtc_lock); + rtc_status = 0; + return 0; +} + +static int rtc_fasync (int fd, struct file *filp, int on) +{ + return fasync_helper (fd, filp, on, &rtc_async_queue); +} + +static unsigned int rtc_poll(struct file *file, poll_table *wait) +{ + poll_wait (file, &rtc_wait, wait); + return (rtc_irq_data) ? 0 : POLLIN | POLLRDNORM; +} + +static loff_t rtc_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +ssize_t rtc_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long data; + ssize_t retval; + + if (count < sizeof(unsigned long)) + return -EINVAL; + + add_wait_queue(&rtc_wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + for (;;) { + spin_lock_irq (&rtc_lock); + data = rtc_irq_data; + if (data != 0) { + rtc_irq_data = 0; + break; + } + spin_unlock_irq (&rtc_lock); + + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto out; + } + + if (signal_pending(current)) { + retval = -ERESTARTSYS; + goto out; + } + + schedule(); + } + + if (data & RTC_PF) { + /* interpolate missed periods and set match for the next one */ + unsigned long period = TIMER_FREQ/rtc_freq; + unsigned long oscr = OSCR; + unsigned long osmr1 = OSMR1; + unsigned long missed = (oscr - osmr1)/period; + data += missed << 8; + OSSR = OSSR_M1; /* clear match on timer 1 */ + OSMR1 = osmr1 + (missed + 1)*period; + /* ensure we didn't miss another match in the mean time */ + while( (signed long)((osmr1 = OSMR1) - OSCR) <= 0 ) { + data += 0x100; + OSSR = OSSR_M1; /* clear match on timer 1 */ + OSMR1 = osmr1 + period; + } + } + spin_unlock_irq (&rtc_lock); + + data -= 0x100; /* the first IRQ wasn't actually missed */ + + retval = put_user(data, (unsigned long *)buf); + if (!retval) + retval = sizeof(unsigned long); + +out: + set_current_state(TASK_RUNNING); + remove_wait_queue(&rtc_wait, &wait); + return retval; +} + +static int rtc_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct rtc_time tm, tm2; + + switch (cmd) { + case RTC_AIE_OFF: + spin_lock_irq(&rtc_lock); + RTSR &= ~RTSR_ALE; + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_AIE_ON: + spin_lock_irq(&rtc_lock); + RTSR |= RTSR_ALE; + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_UIE_OFF: + spin_lock_irq(&rtc_lock); + RTSR &= ~RTSR_HZE; + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_UIE_ON: + spin_lock_irq(&rtc_lock); + RTSR |= RTSR_HZE; + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_PIE_OFF: + spin_lock_irq(&rtc_lock); + OIER &= ~OIER_E1; + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_PIE_ON: + if ((rtc_freq > 64) && !capable(CAP_SYS_RESOURCE)) + return -EACCES; + spin_lock_irq(&rtc_lock); + OSMR1 = TIMER_FREQ/rtc_freq + OSCR; + OIER |= OIER_E1; + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); + return 0; + case RTC_ALM_READ: + decodetime (RTAR, &tm); + break; + case RTC_ALM_SET: + if (copy_from_user (&tm2, (struct rtc_time*)arg, sizeof (tm2))) + return -EFAULT; + decodetime (RCNR, &tm); + if ((unsigned)tm2.tm_hour < 24) + tm.tm_hour = tm2.tm_hour; + if ((unsigned)tm2.tm_min < 60) + tm.tm_min = tm2.tm_min; + if ((unsigned)tm2.tm_sec < 60) + tm.tm_sec = tm2.tm_sec; + RTAR = mktime ( tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + return 0; + case RTC_RD_TIME: + decodetime (RCNR, &tm); + break; + case RTC_SET_TIME: + if (!capable(CAP_SYS_TIME)) + return -EACCES; + if (copy_from_user (&tm, (struct rtc_time*)arg, sizeof (tm))) + return -EFAULT; + tm.tm_year += 1900; + if (tm.tm_year < 1970 || (unsigned)tm.tm_mon >= 12 || + tm.tm_mday < 1 || tm.tm_mday > (days_in_mo[tm.tm_mon] + + (tm.tm_mon == 1 && is_leap(tm.tm_year))) || + (unsigned)tm.tm_hour >= 24 || + (unsigned)tm.tm_min >= 60 || + (unsigned)tm.tm_sec >= 60) + return -EINVAL; + RCNR = mktime ( tm.tm_year, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); + return 0; + case RTC_IRQP_READ: + return put_user(rtc_freq, (unsigned long *)arg); + case RTC_IRQP_SET: + if (arg < 1 || arg > TIMER_FREQ) + return -EINVAL; + if ((arg > 64) && (!capable(CAP_SYS_RESOURCE))) + return -EACCES; + rtc_freq = arg; + return 0; + case RTC_EPOCH_READ: + return put_user (1970, (unsigned long *)arg); + default: + return -EINVAL; + } + return copy_to_user ((void *)arg, &tm, sizeof (tm)) ? -EFAULT : 0; +} + +static struct file_operations rtc_fops = { + owner: THIS_MODULE, + llseek: rtc_llseek, + read: rtc_read, + poll: rtc_poll, + ioctl: rtc_ioctl, + open: rtc_open, + release: rtc_release, + fasync: rtc_fasync, +}; + +static struct miscdevice sa1100rtc_miscdev = { + RTC_MINOR, + "rtc", + &rtc_fops +}; + +static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + char *p = page; + int len; + struct rtc_time tm; + + decodetime (RCNR, &tm); + p += sprintf(p, "rtc_time\t: %02d:%02d:%02d\n" + "rtc_date\t: %04d-%02d-%02d\n" + "rtc_epoch\t: %04d\n", + tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 1970); + decodetime (RTAR, &tm); + p += sprintf(p, "alrm_time\t: %02d:%02d:%02d\n" + "alrm_date\t: %04d-%02d-%02d\n", + tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); + p += sprintf(p, "trim/divider\t: 0x%08x\n", RTTR); + p += sprintf(p, "alarm_IRQ\t: %s\n", (RTSR & RTSR_ALE) ? "yes" : "no" ); + p += sprintf(p, "update_IRQ\t: %s\n", (RTSR & RTSR_HZE) ? "yes" : "no"); + p += sprintf(p, "periodic_IRQ\t: %s\n", (OIER & OIER_E1) ? "yes" : "no"); + p += sprintf(p, "periodic_freq\t: %ld\n", rtc_freq); + + len = (p - page) - off; + if (len < 0) + len = 0; + + *eof = (len <= count) ? 1 : 0; + *start = page + off; + + return len; +} + +static int __init rtc_init(void) +{ + int ret; + + misc_register (&sa1100rtc_miscdev); + create_proc_read_entry ("driver/rtc", 0, 0, rtc_read_proc, NULL); + ret = request_irq (IRQ_RTC1Hz, rtc_interrupt, SA_INTERRUPT, "rtc 1Hz", NULL); + if (ret) { + printk (KERN_ERR "rtc: IRQ %d already in use.\n", IRQ_RTC1Hz); + goto IRQ_RTC1Hz_failed; + } + ret = request_irq (IRQ_RTCAlrm, rtc_interrupt, SA_INTERRUPT, "rtc Alrm", NULL); + if (ret) { + printk(KERN_ERR "rtc: IRQ %d already in use.\n", IRQ_RTCAlrm); + goto IRQ_RTCAlrm_failed; + } + ret = request_irq (IRQ_OST1, timer1_interrupt, SA_INTERRUPT, "rtc timer", NULL); + if (ret) { + printk(KERN_ERR "rtc: IRQ %d already in use.\n", IRQ_OST1); + goto IRQ_OST1_failed; + } + + printk (KERN_INFO "SA1100 Real Time Clock driver v" DRIVER_VERSION "\n"); + + /* + * According to the manual we should be able to let RTTR be zero + * and then a default diviser for a 32.768KHz clock is used. + * Apparently this doesn't work, at least for my SA1110 rev 5. + * If the clock divider is uninitialized then reset it to the + * default value to get the 1Hz clock. + */ + if (RTTR == 0) { + RTTR = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16); + printk (KERN_WARNING "rtc: warning: initializing default clock divider/trim value\n"); + /* The current RTC value probably doesn't make sense either */ + RCNR = 0; + } + + return 0; + +IRQ_OST1_failed: + free_irq (IRQ_RTCAlrm, NULL); +IRQ_RTCAlrm_failed: + free_irq (IRQ_RTC1Hz, NULL); +IRQ_RTC1Hz_failed: + remove_proc_entry ("driver/rtc", NULL); + misc_deregister (&sa1100rtc_miscdev); + return ret; +} + +static void __exit rtc_exit(void) +{ + free_irq (IRQ_OST1, NULL); + free_irq (IRQ_RTCAlrm, NULL); + free_irq (IRQ_RTC1Hz, NULL); + remove_proc_entry ("driver/rtc", NULL); + misc_deregister (&sa1100rtc_miscdev); +} + +module_init(rtc_init); +module_exit(rtc_exit); + +MODULE_AUTHOR("Nils Faerber "); +MODULE_DESCRIPTION("SA1100 Realtime Clock Driver (RTC)"); +MODULE_LICENSE("GPL"); /* so says the header */ diff -urN orig/drivers/char/tty_io.c linux/drivers/char/tty_io.c --- orig/drivers/char/tty_io.c Tue May 27 10:04:33 2003 +++ linux/drivers/char/tty_io.c Tue May 27 10:11:16 2003 @@ -1502,10 +1502,17 @@ return 0; } +/* + * In the case of pty's, "tty" is the master side + * and "real_tty" is the slave side. + */ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty, struct winsize * arg) { struct winsize tmp_ws; + struct task_struct *p; + struct list_head *l; + struct pid *pid; if (copy_from_user(&tmp_ws, arg, sizeof(*arg))) return -EFAULT; @@ -1520,8 +1527,21 @@ #endif if (tty->pgrp > 0) kill_pg(tty->pgrp, SIGWINCH, 1); - if ((real_tty->pgrp != tty->pgrp) && (real_tty->pgrp > 0)) - kill_pg(real_tty->pgrp, SIGWINCH, 1); + + /* + * Send SIGWINCH to the whole session on the slave tty. + * However, in the case of non-master pty's, be careful + * not to send two SIGWINCH to the same procress group. + */ + if (real_tty->session > 0) { + read_lock(&tasklist_lock); + for_each_task_pid(real_tty->session, PIDTYPE_SID, p, l, pid) { + if (p->pgrp != tty->pgrp) + group_send_sig_info(SIGWINCH, (void *)1L, p); + } + read_unlock(&tasklist_lock); + } + tty->winsize = tmp_ws; real_tty->winsize = tmp_ws; return 0; diff -urN orig/drivers/i2c/Kconfig linux/drivers/i2c/Kconfig --- orig/drivers/i2c/Kconfig Sun Apr 20 16:31:49 2003 +++ linux/drivers/i2c/Kconfig Sun Apr 20 15:35:22 2003 @@ -117,6 +117,10 @@ If compiled as a module, it will be called scx200_acb. +config I2C_BIT_SA1100_GPIO + bool "SA1100 I2C GPIO adapter" + depends on ARCH_SA1100 && I2C_ALGOBIT + config I2C_ALGOPCF tristate "I2C PCF 8584 interfaces" depends on I2C diff -urN orig/drivers/ide/legacy/ide-cs.c linux/drivers/ide/legacy/ide-cs.c --- orig/drivers/ide/legacy/ide-cs.c Mon May 5 17:39:06 2003 +++ linux/drivers/ide/legacy/ide-cs.c Sat May 17 22:53:23 2003 @@ -470,28 +470,25 @@ return 0; } /* ide_event */ -/*====================================================================*/ +static struct pcmcia_driver ide_cs_driver = { + .owner = THIS_MODULE, + .drv = { + .name = "ide_cs", + }, + .attach = ide_attach, + .detach = ide_detach, +}; static int __init init_ide_cs(void) { - servinfo_t serv; - DEBUG(0, "%s\n", version); - CardServices(GetCardServicesInfo, &serv); - if (serv.Revision != CS_RELEASE_CODE) { - printk(KERN_NOTICE "ide-cs: Card Services release " - "does not match!\n"); - return -EINVAL; - } - register_pccard_driver(&dev_info, &ide_attach, &ide_detach); - return 0; + return pcmcia_register_driver(&ide_cs_driver); } static void __exit exit_ide_cs(void) { - DEBUG(0, "ide-cs: unloading\n"); - unregister_pccard_driver(&dev_info); - while (dev_list != NULL) - ide_detach(dev_list); + pcmcia_unregister_driver(&ide_cs_driver); + while (dev_list != NULL) + ide_detach(dev_list); } module_init(init_ide_cs); diff -urN orig/drivers/ide/pci/sl82c105.c linux/drivers/ide/pci/sl82c105.c --- orig/drivers/ide/pci/sl82c105.c Tue Feb 25 10:57:45 2003 +++ linux/drivers/ide/pci/sl82c105.c Wed May 7 12:44:08 2003 @@ -5,8 +5,10 @@ * * Maintainer unknown. * - * Drive tuning added from Rebel.com's kernel sources - * -- Russell King (15/11/98) linux@arm.linux.org.uk + * 15/11/1998 RMK Drive tuning added from Rebel.com's kernel + * sources + * 30/03/2002 RMK Add fixes specified in W83C553F errata. + * (with special thanks to Todd Inglett) * * Merge in Russell's HW workarounds, fix various problems * with the timing registers setup. @@ -178,7 +180,7 @@ /* * The SL82C105 holds off all IDE interrupts while in DMA mode until * all DMA activity is completed. Sometimes this causes problems (eg, - * when the drive wants to report an error condition). + * when the drive wants to report an error condition.) * * 0x7e is a "chip testing" register. Bit 2 resets the DMA controller * state machine. We need to kick this to work around various bugs. @@ -308,7 +310,7 @@ /* * Ok, that is nasty, but we must make sure the DMA timings * won't be used for a PIO access. The solution here is - * to make sure the 16 bits mode is diabled on the channel + * to make sure the 16 bits mode is disabled on the channel * when DMA is enabled, thus causing the chip to use PIO0 * timings for those operations. */ @@ -352,7 +354,7 @@ * We only deal with PIO mode here - DMA mode 'using_dma' is not * initialised at the point that this function is called. */ -static void tune_sl82c105(ide_drive_t *drive, u8 pio) +static void sl82c105_tune_drive(ide_drive_t *drive, u8 pio) { DBG(("tune_sl82c105(drive:%s)\n", drive->name)); @@ -445,6 +447,9 @@ } hwif->OUTB(dma_state, dma_base + 2); + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; + ide_setup_dma(hwif, dma_base, 8); } @@ -459,7 +464,7 @@ DBG(("init_hwif_sl82c105(hwif: ide%d)\n", hwif->index)); - hwif->tuneproc = tune_sl82c105; + hwif->tuneproc = sl82c105_tune_drive; hwif->selectproc = sl82c105_selectproc; hwif->resetproc = sl82c105_resetproc; @@ -467,7 +472,7 @@ * we always autotune PIO, this is done before DMA is * checked, so there is no risk of accidentally disabling * DMA - */ + */ hwif->drives[0].pio_speed = XFER_PIO_0; hwif->drives[0].autotune = 1; hwif->drives[1].pio_speed = XFER_PIO_1; @@ -475,7 +480,7 @@ pci_read_config_dword(dev, 0x40, &val); *((u32 *)&hwif->hwif_data) = val; - + if (!hwif->dma_base) return; @@ -491,11 +496,6 @@ hwif->ide_dma_lostirq = &sl82c105_ide_dma_lost_irq; hwif->ide_dma_begin = &sl82c105_ide_dma_begin; hwif->ide_dma_timeout = &sl82c105_ide_dma_timeout; - - if (!noautodma) - hwif->autodma = 1; - hwif->drives[0].autodma = hwif->autodma; - hwif->drives[1].autodma = hwif->autodma; #endif /* CONFIG_BLK_DEV_IDEDMA */ } diff -urN orig/drivers/input/Kconfig linux/drivers/input/Kconfig --- orig/drivers/input/Kconfig Tue Feb 11 16:10:10 2003 +++ linux/drivers/input/Kconfig Mon Feb 10 23:30:04 2003 @@ -86,7 +86,7 @@ module, say M here and read . config INPUT_TSDEV - tristate "Touchscreen interface" + tristate "Compaq Touchscreen interface" depends on INPUT ---help--- Say Y here if you have an application that only can understand the @@ -110,6 +110,10 @@ depends on INPUT_TSDEV default "320" +config INPUT_TSLIBDEV + tristate "TSLIB Touchscreen interface" + depends on INPUT + config INPUT_EVDEV tristate "Event interface" depends on INPUT diff -urN orig/drivers/input/Makefile linux/drivers/input/Makefile --- orig/drivers/input/Makefile Tue Feb 11 16:10:10 2003 +++ linux/drivers/input/Makefile Mon Feb 10 23:30:04 2003 @@ -8,6 +8,7 @@ obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o obj-$(CONFIG_INPUT_JOYDEV) += joydev.o obj-$(CONFIG_INPUT_EVDEV) += evdev.o +obj-$(CONFIG_INPUT_TSLIBDEV) += tslibdev.o obj-$(CONFIG_INPUT_TSDEV) += tsdev.o obj-$(CONFIG_INPUT_POWER) += power.o obj-$(CONFIG_INPUT_EVBUG) += evbug.o diff -urN orig/drivers/input/keyboard/atkbd.c linux/drivers/input/keyboard/atkbd.c --- orig/drivers/input/keyboard/atkbd.c Mon May 5 17:39:08 2003 +++ linux/drivers/input/keyboard/atkbd.c Mon May 5 17:41:49 2003 @@ -89,7 +89,7 @@ #define ATKBD_CMD_GETID 0x02f2 #define ATKBD_CMD_ENABLE 0x00f4 #define ATKBD_CMD_RESET_DIS 0x00f5 -#define ATKBD_CMD_RESET_BAT 0x01ff +#define ATKBD_CMD_RESET_BAT 0x02ff #define ATKBD_CMD_SETALL_MB 0x00f8 #define ATKBD_CMD_RESEND 0x00fe #define ATKBD_CMD_EX_ENABLE 0x10ea @@ -255,7 +255,8 @@ while (atkbd->cmdcnt && timeout--) { - if (atkbd->cmdcnt == 1 && command == ATKBD_CMD_RESET_BAT) + if (atkbd->cmdcnt == 1 && command == ATKBD_CMD_RESET_BAT && + timeout > 100000) timeout = 100000; if (atkbd->cmdcnt == 1 && command == ATKBD_CMD_GETID && @@ -267,6 +268,9 @@ udelay(1); } + if (atkbd->cmdcnt == 1 && command == ATKBD_CMD_RESET_BAT) + atkbd->cmdcnt = 0; + if (param) for (i = 0; i < receive; i++) param[i] = atkbd->cmdbuf[(receive - 1) - i]; diff -urN orig/drivers/input/serio/Kconfig linux/drivers/input/serio/Kconfig --- orig/drivers/input/serio/Kconfig Mon Mar 24 23:47:18 2003 +++ linux/drivers/input/serio/Kconfig Tue Mar 25 00:03:25 2003 @@ -88,7 +88,7 @@ config SERIO_RPCKBD tristate "Acorn RiscPC keyboard controller" - depends on ARCH_ACORN && SERIO + depends on (ARCH_ACORN || ARCH_CLPS7500) && SERIO default y help Say Y here if you have the Acorn RiscPC and want to use an AT @@ -99,6 +99,18 @@ The module will be called rpckbd. If you want to compile it as a module, say M here and read . +config SERIO_CLPS7500 + tristate "CLPS7500 PS/2 mouse port controller" + depends on ARCH_CLPS7500 && SERIO + help + Say Y here if you have CLPS7500 based hardware and want to use + the mouse port. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called clps7500ps2. If you want to compile it + as a module, say M here and read . + config SERIO_AMBAKMI tristate "AMBA KMI keyboard controller" depends on ARCH_INTEGRATOR && SERIO @@ -119,3 +131,14 @@ The module will be called rpckbd.o. If you want to compile it as a module, say M here and read . +config SERIO_PCIPS2 + tristate "PCI PS/2 keyboard and PS/2 mouse controller" + depends on PCI && SERIO + help + Say Y here if you have a Mobility Docking station with PS/2 + keyboard and mice ports. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called rpckbd. If you want to compile it as a + module, say M here and read . diff -urN orig/drivers/input/serio/Makefile linux/drivers/input/serio/Makefile --- orig/drivers/input/serio/Makefile Mon Mar 24 23:47:18 2003 +++ linux/drivers/input/serio/Makefile Tue Mar 25 00:03:37 2003 @@ -10,7 +10,9 @@ obj-$(CONFIG_SERIO_SERPORT) += serport.o obj-$(CONFIG_SERIO_CT82C710) += ct82c710.o obj-$(CONFIG_SERIO_RPCKBD) += rpckbd.o +obj-$(CONFIG_SERIO_CLPS7500) += clps7500ps2.o obj-$(CONFIG_SERIO_SA1111) += sa1111ps2.o obj-$(CONFIG_SERIO_AMBAKMI) += ambakmi.o obj-$(CONFIG_SERIO_Q40KBD) += q40kbd.o obj-$(CONFIG_SERIO_98KBD) += 98kbd-io.o +obj-$(CONFIG_SERIO_PCIPS2) += pcips2.o diff -urN orig/drivers/input/serio/ambakmi.c linux/drivers/input/serio/ambakmi.c --- orig/drivers/input/serio/ambakmi.c Tue May 27 10:04:37 2003 +++ linux/drivers/input/serio/ambakmi.c Thu Jun 12 14:55:11 2003 @@ -1,7 +1,7 @@ /* * linux/drivers/input/serio/amba_kmi.c * - * Copyright (C) 2000 Deep Blue Solutions Ltd. + * Copyright (C) 2000-2003 Deep Blue Solutions Ltd. * Copyright (C) 2002 Russell King. * * This program is free software; you can redistribute it and/or modify @@ -22,17 +22,17 @@ #include #include #include +#include #define KMI_BASE (kmi->base) struct amba_kmi_port { struct serio io; struct amba_kmi_port *next; - void *base; + unsigned char *base; unsigned int irq; unsigned int divisor; - char name[32]; - char phys[16]; + unsigned int open; struct resource *res; }; @@ -73,7 +73,7 @@ writeb(kmi->divisor, KMICLKDIV); writeb(KMICR_EN, KMICR); - ret = request_irq(kmi->irq, amba_kmi_int, 0, kmi->phys, kmi); + ret = request_irq(kmi->irq, amba_kmi_int, 0, kmi->io.phys, kmi); if (ret) { printk(KERN_ERR "kmi: failed to claim IRQ%d\n", kmi->irq); writeb(0, KMICR); @@ -94,9 +94,7 @@ free_irq(kmi->irq, kmi); } -static struct amba_kmi_port *list; - -static int __init amba_kmi_init_one(char *type, unsigned long base, int irq, int nr) +static int amba_kmi_probe(struct amba_device *dev, void *id) { struct amba_kmi_port *kmi; @@ -110,58 +108,83 @@ kmi->io.write = amba_kmi_write; kmi->io.open = amba_kmi_open; kmi->io.close = amba_kmi_close; - kmi->io.name = kmi->name; - kmi->io.phys = kmi->phys; + kmi->io.name = dev->dev.name; + kmi->io.phys = dev->dev.bus_id; kmi->io.driver = kmi; - snprintf(kmi->name, sizeof(kmi->name), "AMBA KMI PS/2 %s port", type); - snprintf(kmi->phys, sizeof(kmi->phys), "amba/serio%d", nr); - - kmi->res = request_mem_region(base, KMI_SIZE, kmi->phys); + kmi->res = request_mem_region(dev->res.start, KMI_SIZE, kmi->io.phys); if (!kmi->res) { kfree(kmi); return -EBUSY; } - kmi->base = ioremap(base, KMI_SIZE); + kmi->base = ioremap(dev->res.start, KMI_SIZE); if (!kmi->base) { release_resource(kmi->res); kfree(kmi); return -ENOMEM; } - kmi->irq = irq; + kmi->irq = dev->irq; kmi->divisor = 24 / 8 - 1; - kmi->next = list; - list = kmi; + amba_set_drvdata(dev, kmi); serio_register_port(&kmi->io); return 0; } -static int __init amba_kmi_init(void) +static int amba_kmi_remove(struct amba_device *dev) { - amba_kmi_init_one("keyboard", KMI0_BASE, IRQ_KMIINT0, 0); - amba_kmi_init_one("mouse", KMI1_BASE, IRQ_KMIINT1, 1); + struct amba_kmi_port *kmi = amba_get_drvdata(dev); + + amba_set_drvdata(dev, NULL); + + serio_unregister_port(&kmi->io); + iounmap(kmi->base); + release_resource(kmi->res); + kfree(kmi); return 0; } -static void __exit amba_kmi_exit(void) +static int amba_kmi_resume(struct amba_device *dev, u32 level) { - struct amba_kmi_port *kmi, *next; + struct amba_kmi_port *kmi = amba_get_drvdata(dev); - kmi = list; - while (kmi) { - next = kmi->next; + if (level == RESUME_ENABLE) { + /* kick the serio layer to rescan this port */ + serio_rescan(&kmi->io); + } - serio_unregister_port(&kmi->io); - iounmap(kmi->base); - release_resource(kmi->res); - kfree(kmi); + return 0; +} - kmi = next; - } +static struct amba_id amba_kmi_idtable[] = { + { + .id = 0x00041050, + .mask = 0x000fffff, + }, + { 0, 0 } +}; + +static struct amba_driver ambakmi_driver = { + .drv = { + .name = "kmi-pl050", + }, + .id_table = amba_kmi_idtable, + .probe = amba_kmi_probe, + .remove = amba_kmi_remove, + .resume = amba_kmi_resume, +}; + +static int __init amba_kmi_init(void) +{ + return amba_driver_register(&ambakmi_driver); +} + +static void __exit amba_kmi_exit(void) +{ + return amba_driver_unregister(&ambakmi_driver); } module_init(amba_kmi_init); diff -urN orig/drivers/input/serio/clps7500ps2.c linux/drivers/input/serio/clps7500ps2.c --- orig/drivers/input/serio/clps7500ps2.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/input/serio/clps7500ps2.c Wed May 28 10:30:29 2003 @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2000-2001 Vojtech Pavlik + * Copyright (c) 2002 Russell King + */ + +/* + * CLPS7500 PS/2 keyboard controller driver for Linux/ARM + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik, Russell King"); +MODULE_DESCRIPTION("CLPS7500 PS/2 controller driver"); +MODULE_LICENSE("GPL"); + +static int clps7500ps2_write(struct serio *port, unsigned char val) +{ + while (!(iomd_readb(IOMD_MSECTL) & (1 << 7))) + cpu_relax(); + + iomd_writeb(val, IOMD_MSEDAT); + + return 0; +} + +static void clps7500ps2_rx(int irq, void *dev_id, struct pt_regs *regs) +{ + struct serio *port = dev_id; + unsigned int byte; + + while (iomd_readb(IOMD_MSECTL) & (1 << 5)) { + byte = iomd_readb(IOMD_MSEDAT); + + serio_interrupt(port, byte, 0, regs); + } +} + +static void clps7500ps2_tx(int irq, void *dev_id, struct pt_regs *regs) +{ +} + +static int clps7500ps2_open(struct serio *port) +{ + /* Reset the keyboard state machine. */ + iomd_writeb(0, IOMD_MSECTL); + iomd_writeb(8, IOMD_MSECTL); + iomd_readb(IOMD_MSEDAT); + + if (request_irq(IRQ_MOUSERX, clps7500ps2_rx, 0, "clps7500ps2", port) != 0) { + printk(KERN_ERR "clps7500ps2.c: Could not allocate keyboard receive IRQ\n"); + return -EBUSY; + } + + if (request_irq(IRQ_MOUSETX, clps7500ps2_tx, 0, "clps7500ps2", port) != 0) { + printk(KERN_ERR "clps7500ps2.c: Could not allocate keyboard transmit IRQ\n"); + free_irq(IRQ_MOUSERX, NULL); + return -EBUSY; + } + + return 0; +} + +static void clps7500ps2_close(struct serio *port) +{ + free_irq(IRQ_MOUSERX, port); + free_irq(IRQ_MOUSETX, port); +} + +static struct serio clps7500ps2_port = +{ + .type = SERIO_8042, + .open = clps7500ps2_open, + .close = clps7500ps2_close, + .write = clps7500ps2_write, + .name = "CLPS7500 PS/2 port", + .phys = "clps7500/serio1", +}; + +static int __init clps7500ps2_init(void) +{ + serio_register_port(&clps7500ps2_port); + return 0; +} + +static void __exit clps7500ps2_exit(void) +{ + serio_unregister_port(&clps7500ps2_port); +} + +module_init(clps7500ps2_init); +module_exit(clps7500ps2_exit); diff -urN orig/drivers/input/serio/pcips2.c linux/drivers/input/serio/pcips2.c --- orig/drivers/input/serio/pcips2.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/input/serio/pcips2.c Wed May 28 10:30:29 2003 @@ -0,0 +1,228 @@ +/* + * linux/drivers/input/serio/pcips2.c + * + * Copyright (C) 2003 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * I'm not sure if this is a generic PS/2 PCI interface or specific to + * the Mobility Electronics docking station. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PS2_CTRL (0) +#define PS2_STATUS (1) +#define PS2_DATA (2) + +#define PS2_CTRL_CLK (1<<0) +#define PS2_CTRL_DAT (1<<1) +#define PS2_CTRL_TXIRQ (1<<2) +#define PS2_CTRL_ENABLE (1<<3) +#define PS2_CTRL_RXIRQ (1<<4) + +#define PS2_STAT_CLK (1<<0) +#define PS2_STAT_DAT (1<<1) +#define PS2_STAT_PARITY (1<<2) +#define PS2_STAT_RXFULL (1<<5) +#define PS2_STAT_TXBUSY (1<<6) +#define PS2_STAT_TXEMPTY (1<<7) + +struct pcips2_data { + struct serio io; + unsigned int base; + struct pci_dev *dev; +}; + +static int pcips2_write(struct serio *io, unsigned char val) +{ + struct pcips2_data *ps2if = io->driver; + unsigned int stat; + + do { + stat = inb(ps2if->base + PS2_STATUS); + cpu_relax(); + } while (!(stat & PS2_STAT_TXEMPTY)); + + outb(val, ps2if->base + PS2_DATA); + + return 0; +} + +static irqreturn_t pcips2_interrupt(int irq, void *devid, struct pt_regs *regs) +{ + struct pcips2_data *ps2if = devid; + unsigned char status, scancode; + int handled = IRQ_NONE; + + do { + unsigned int flag; + + status = inb(ps2if->base + PS2_STATUS); + if (!(status & PS2_STAT_RXFULL)) + break; + scancode = inb(ps2if->base + PS2_DATA); + if (status == 0xff && scancode == 0xff) + break; + + flag = (status & PS2_STAT_PARITY) ? 0 : SERIO_PARITY; + + if (hweight8(scancode) & 1) + flag ^= SERIO_PARITY; + + serio_interrupt(&ps2if->io, scancode, flag, regs); + handled = IRQ_HANDLED; + } while (1); + + return handled; +} + +static void pcips2_flush_input(struct pcips2_data *ps2if) +{ + unsigned char status, scancode; + + do { + status = inb(ps2if->base + PS2_STATUS); + if (!(status & PS2_STAT_RXFULL)) + break; + scancode = inb(ps2if->base + PS2_DATA); + if (status == 0xff && scancode == 0xff) + break; + } while (1); +} + +static int pcips2_open(struct serio *io) +{ + struct pcips2_data *ps2if = io->driver; + int ret, val; + + outb(PS2_CTRL_ENABLE, ps2if->base); + pcips2_flush_input(ps2if); + + ret = request_irq(ps2if->dev->irq, pcips2_interrupt, SA_SHIRQ, + "pcips2", ps2if); + val = 0; + if (ret == 0) + val = PS2_CTRL_ENABLE | PS2_CTRL_RXIRQ; + outb(val, ps2if->base); + + return ret; +} + +static void pcips2_close(struct serio *io) +{ + struct pcips2_data *ps2if = io->driver; + + outb(0, ps2if->base); + + free_irq(ps2if->dev->irq, ps2if); +} + +static int __devinit pcips2_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct pcips2_data *ps2if; + int ret; + + ret = pci_enable_device(dev); + if (ret) + goto out; + + ret = pci_request_regions(dev, "pcips2"); + if (ret) + goto disable; + + ps2if = kmalloc(sizeof(struct pcips2_data), GFP_KERNEL); + if (!ps2if) { + ret = -ENOMEM; + goto release; + } + + memset(ps2if, 0, sizeof(struct pcips2_data)); + + ps2if->io.type = SERIO_8042; + ps2if->io.write = pcips2_write; + ps2if->io.open = pcips2_open; + ps2if->io.close = pcips2_close; + ps2if->io.name = dev->dev.name; + ps2if->io.phys = dev->dev.bus_id; + ps2if->io.driver = ps2if; + ps2if->dev = dev; + ps2if->base = pci_resource_start(dev, 0); + + pci_set_drvdata(dev, ps2if); + + serio_register_port(&ps2if->io); + return 0; + + release: + pci_release_regions(dev); + disable: + pci_disable_device(dev); + out: + return ret; +} + +static void __devexit pcips2_remove(struct pci_dev *dev) +{ + struct pcips2_data *ps2if = pci_get_drvdata(dev); + + serio_unregister_port(&ps2if->io); + pci_set_drvdata(dev, NULL); + kfree(ps2if); + pci_release_regions(dev); + pci_disable_device(dev); +} + +static struct pci_device_id pcips2_ids[] = { + { + .vendor = 0x14f2, /* MOBILITY */ + .device = 0x0123, /* Keyboard */ + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .class = PCI_CLASS_INPUT_KEYBOARD << 8, + .class_mask = 0xffff00, + }, + { + .vendor = 0x14f2, /* MOBILITY */ + .device = 0x0124, /* Mouse */ + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .class = PCI_CLASS_INPUT_MOUSE << 8, + .class_mask = 0xffff00, + }, + { 0, } +}; + +static struct pci_driver pcips2_driver = { + .name = "pcips2", + .id_table = pcips2_ids, + .probe = pcips2_probe, + .remove = __devexit_p(pcips2_remove), +}; + +static int __init pcips2_init(void) +{ + return pci_module_init(&pcips2_driver); +} + +static void __exit pcips2_exit(void) +{ + pci_unregister_driver(&pcips2_driver); +} + +module_init(pcips2_init); +module_exit(pcips2_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("PCI PS/2 keyboard/mouse driver"); +MODULE_DEVICE_TABLE(pci, pcips2_ids); diff -urN orig/drivers/input/serio/sa1100ir.c linux/drivers/input/serio/sa1100ir.c --- orig/drivers/input/serio/sa1100ir.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/input/serio/sa1100ir.c Wed May 28 10:30:29 2003 @@ -0,0 +1,90 @@ +/* + * linux/drivers/input/serio/sa1100ir.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2. + */ +#include +#include +#include + +#include +#include +#include + + + +struct sa1100_kbd { + struct serio io; + void *base; + int irq; +}; + +static void sa1100ir_int(int irq, void *dev_id, struct pt_regs *regs) +{ + struct sa1100_kbd *kbd = dev_id; + unsigned int status; + + do { + unsigned int flag, data; + + status = readl(kbd->base + UTSR1); + if (!(status & UTSR1_RNE)) + break; + + flag = (status & UTSR1_FRE ? SERIO_FRAME : 0) | + (status & UTSR1_PRE ? SERIO_PARITY : 0); + + data = readl(kbd->base + UTDR); + + serio_interrupt(&kbd->io, data, flag); + } while (1); + + status = readl(kbd->base + UTSR0) & UTSR0_RID | UTSR0_RBB | UTSR0_REB; + if (status) + writel(status, kbd->base + UTSR0); +} + +static int sa1100ir_kbd_open(struct serio *io) +{ + struct sa1100_kbd *kbd = io->driver; + int ret; + + ret = request_irq(kbd->irq, sa1100ir_int, 0, kbd->io.phys, kbd); + if (ret) + return ret; + + return 0; +} + +static void sa1100ir_kbd_close(struct serio *io) +{ + struct sa1100_kbd *kbd = io->driver; + + free_irq(kbd->irq, kbd); +} + +static struct sa1100_kbd sa1100_kbd = { + .io = { + .type = 0, + .open = sa1100ir_kbd_open, + .close = sa1100ir_kbd_close, + .name = "SA11x0 IR port", + .phys = "sa11x0/ir", + .driver = &sa1100_kbd, + }, +}; + +static int __init sa1100_kbd_init(void) +{ + serio_register_port(&sa1100_kbd.io); +} + +static void __exit sa1100_kbd_exit(void) +{ + serio_unregister_port(&sa1100_kbd.io); +} + +module_init(sa1100_kbd_init); +module_exit(sa1100_kbd_exit); diff -urN orig/drivers/input/serio/sa1111ps2.c linux/drivers/input/serio/sa1111ps2.c --- orig/drivers/input/serio/sa1111ps2.c Tue May 27 10:04:38 2003 +++ linux/drivers/input/serio/sa1111ps2.c Wed May 28 10:30:29 2003 @@ -64,7 +64,7 @@ status = sa1111_readl(ps2if->base + SA1111_PS2STAT); - handled = IRQ_HANDLED; + handled = IRQ_HANDLED; } return handled; diff -urN orig/drivers/input/serio/serio.c linux/drivers/input/serio/serio.c --- orig/drivers/input/serio/serio.c Mon May 5 17:39:08 2003 +++ linux/drivers/input/serio/serio.c Mon May 5 17:41:50 2003 @@ -58,6 +58,7 @@ struct list_head node; }; +static DECLARE_MUTEX(serio_sem); static LIST_HEAD(serio_list); static LIST_HEAD(serio_dev_list); static LIST_HEAD(serio_event_list); @@ -90,9 +91,11 @@ switch (event->type) { case SERIO_RESCAN : + down(&serio_sem); if (event->serio->dev && event->serio->dev->disconnect) event->serio->dev->disconnect(event->serio); serio_find_dev(event->serio); + up(&serio_sem); break; default: break; @@ -153,30 +156,37 @@ void serio_register_port(struct serio *serio) { + down(&serio_sem); list_add_tail(&serio->node, &serio_list); serio_find_dev(serio); + up(&serio_sem); } void serio_unregister_port(struct serio *serio) { + down(&serio_sem); list_del_init(&serio->node); if (serio->dev && serio->dev->disconnect) serio->dev->disconnect(serio); + up(&serio_sem); } void serio_register_device(struct serio_dev *dev) { struct serio *serio; + down(&serio_sem); list_add_tail(&dev->node, &serio_dev_list); list_for_each_entry(serio, &serio_list, node) if (!serio->dev && dev->connect) dev->connect(serio, dev); + up(&serio_sem); } void serio_unregister_device(struct serio_dev *dev) { struct serio *serio; + down(&serio_sem); list_del_init(&dev->node); list_for_each_entry(serio, &serio_list, node) { @@ -184,8 +194,10 @@ dev->disconnect(serio); serio_find_dev(serio); } + up(&serio_sem); } +/* called from serio_dev->connect/disconnect methods under serio_sem */ int serio_open(struct serio *serio, struct serio_dev *dev) { if (serio->open(serio)) @@ -194,6 +206,7 @@ return 0; } +/* called from serio_dev->connect/disconnect methods under serio_sem */ void serio_close(struct serio *serio) { serio->close(serio); diff -urN orig/drivers/input/tslibdev.c linux/drivers/input/tslibdev.c --- orig/drivers/input/tslibdev.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/input/tslibdev.c Wed May 28 10:33:20 2003 @@ -0,0 +1,356 @@ +/* + * linux/drivers/input/tslibdev.c + * + * Copyright (C) 2002 Russell King + * + * From tsdev.c: + * + * Copyright (c) 2001 "Crazy" james Simmons + * + * Input driver to Touchscreen device driver module. + * + * Sponsored by Transvirtual Technology + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ucb1x00_dev { + int exist; + char name[16]; + wait_queue_head_t wait; + struct list_head list; + struct input_handle handle; + int x; + int y; + int pressure; +}; + +struct ts_event { + u16 pressure; + u16 x; + u16 y; + u16 pad; + struct timeval stamp; +}; + +#define TSDEV_BUFFER_SIZE 64 + +struct ucb1x00_list { + struct list_head list; + struct fasync_struct *fasync; + struct ucb1x00_dev *tsdev; + unsigned int head; + unsigned int tail; + struct ts_event event[TSDEV_BUFFER_SIZE]; +}; + +static struct input_handler ucb1x00_handler; +static struct ucb1x00_dev *ucb1x00_dev; + +static void ucb1x00_remove(struct ucb1x00_dev *tsdev); + + +static int ucb1x00_fasync(int fd, struct file *file, int on) +{ + struct ucb1x00_list *list = file->private_data; + int retval; + + retval = fasync_helper(fd, file, on, &list->fasync); + return retval < 0 ? retval : 0; +} + +static int ucb1x00_open(struct inode *inode, struct file *file) +{ + struct ucb1x00_list *list; + int empty; + + if (!ucb1x00_dev || !ucb1x00_dev->exist) + return -ENODEV; + + printk(KERN_WARNING + "tslibdev: process %s (%d) uses obsolete tslib device\n", + current->comm, current->pid); + + list = kmalloc(sizeof(struct ucb1x00_list), GFP_KERNEL); + if (!list) + return -ENOMEM; + + memset(list, 0, sizeof(struct ucb1x00_list)); + + empty = list_empty(&ucb1x00_dev->list); + + list->tsdev = ucb1x00_dev; + list_add(&list->list, &list->tsdev->list); + + file->private_data = list; + + if (empty && list->tsdev->exist) + input_open_device(&list->tsdev->handle); + + return 0; +} + +static int ucb1x00_release(struct inode *inode, struct file *file) +{ + struct ucb1x00_list *list = file->private_data; + + ucb1x00_fasync(-1, file, 0); + + list_del(&list->list); + + ucb1x00_remove(list->tsdev); + + kfree(list); + + return 0; +} + +static ssize_t +ucb1x00_read(struct file *file, char *buffer, size_t count, loff_t * ppos) +{ + DECLARE_WAITQUEUE(wait, current); + struct ucb1x00_list *list = file->private_data; + int retval = 0; + + if (list->head == list->tail) { + add_wait_queue(&list->tsdev->wait, &wait); + + while (list->head == list->tail) { + set_current_state(TASK_INTERRUPTIBLE); + + if (!list->tsdev->exist) { + retval = -ENODEV; + break; + } + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&list->tsdev->wait, &wait); + } + + if (retval) + return retval; + + while (list->head != list->tail && count >= sizeof(struct ts_event)) { + if (copy_to_user(buffer, list->event + list->tail, + sizeof(struct ts_event))) + return retval ? retval : -EFAULT; + list->tail = (list->tail + 1) & (TSDEV_BUFFER_SIZE - 1); + retval += sizeof(struct ts_event); + buffer += sizeof(struct ts_event); + count -= sizeof(struct ts_event); + } + return retval; +} + +/* No kernel lock - fine */ +static unsigned int ucb1x00_poll(struct file *file, poll_table * wait) +{ + struct ucb1x00_list *list = file->private_data; + + poll_wait(file, &list->tsdev->wait, wait); + if (list->head != list->tail || !list->tsdev->exist) + return POLLIN | POLLRDNORM; + return 0; +} + +static int +ucb1x00_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + return -EINVAL; +} + +struct file_operations ucb1x00_fops = { + owner: THIS_MODULE, + open: ucb1x00_open, + release: ucb1x00_release, + read: ucb1x00_read, + poll: ucb1x00_poll, + fasync: ucb1x00_fasync, + ioctl: ucb1x00_ioctl, +}; + +/* + * The official UCB1x00 touchscreen is a miscdevice: + * 10 char Non-serial mice, misc features + * 14 = /dev/touchscreen/ucb1x00 UCB 1x00 touchscreen + */ +static struct miscdevice ucb1x00_ts_dev = { + minor: 14, + name: "touchscreen/ucb1x00", + fops: &ucb1x00_fops, +}; + +static void ucb1x00_remove(struct ucb1x00_dev *tsdev) +{ + if (list_empty(&tsdev->list)) { + if (tsdev->exist) { + input_close_device(&tsdev->handle); + wake_up_interruptible(&tsdev->wait); + } else { + misc_deregister(&ucb1x00_ts_dev); + ucb1x00_dev = NULL; + kfree(tsdev); + } + } +} + + +static void +ucb1x00_event(struct input_handle *handle, unsigned int type, unsigned int code, + int value) +{ + struct ucb1x00_dev *tsdev = handle->private; + struct list_head *l; + + /* sorry, we only handle absolute stuff */ + if (type == EV_ABS) { + switch (code) { + case ABS_X: + tsdev->x = value; + break; + case ABS_Y: + tsdev->y = value; + break; + case ABS_PRESSURE: + tsdev->pressure = value; + break; + } + return; + } + + if (type != EV_SYN || code != SYN_REPORT) + return; + + list_for_each(l, &tsdev->list) { + struct ucb1x00_list *list = list_entry(l, struct ucb1x00_list, list); + list->event[list->head].pressure = tsdev->pressure; + if (tsdev->pressure) { + list->event[list->head].x = tsdev->x; + list->event[list->head].y = tsdev->y; + } else { + list->event[list->head].x = 0; + list->event[list->head].y = 0; + } + do_gettimeofday(&list->event[list->head].stamp); + list->head = (list->head + 1) & (TSDEV_BUFFER_SIZE - 1); + kill_fasync(&list->fasync, SIGIO, POLL_IN); + } + wake_up_interruptible(&tsdev->wait); +} + +static struct input_handle * +ucb1x00_connect(struct input_handler *handler, struct input_dev *dev, + struct input_device_id *id) +{ + struct ucb1x00_dev *tsdev; + + if (ucb1x00_dev) + return NULL; + + tsdev = kmalloc(sizeof(struct ucb1x00_dev), GFP_KERNEL); + if (!tsdev) + return NULL; + + memset(tsdev, 0, sizeof(struct ucb1x00_dev)); + init_waitqueue_head(&tsdev->wait); + INIT_LIST_HEAD(&tsdev->list); + + ucb1x00_dev = tsdev; + + strcpy(tsdev->name, ucb1x00_ts_dev.name); + + tsdev->handle.dev = dev; + tsdev->handle.name = tsdev->name; + tsdev->handle.handler = handler; + tsdev->handle.private = tsdev; + + misc_register(&ucb1x00_ts_dev); + + tsdev->exist = 1; + + return &tsdev->handle; +} + +static void ucb1x00_disconnect(struct input_handle *handle) +{ + struct ucb1x00_dev *tsdev = handle->private; + + tsdev->exist = 0; + ucb1x00_remove(tsdev); +} + +static struct input_device_id ucb1x00_ids[] = { + { + flags: INPUT_DEVICE_ID_MATCH_ABSBIT, + absbit: { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) }, + }, + + {},/* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(input, ucb1x00_ids); + +static struct input_handler ucb1x00_handler = { + event: ucb1x00_event, + connect: ucb1x00_connect, + disconnect: ucb1x00_disconnect, + name: "touchscreen/ucb1x00", + id_table: ucb1x00_ids, +}; + +static int __init ucb1x00_init(void) +{ + input_register_handler(&ucb1x00_handler); + printk(KERN_INFO "ts: UCB1x00 touchscreen protocol output\n"); + return 0; +} + +static void __exit ucb1x00_exit(void) +{ + input_unregister_handler(&ucb1x00_handler); +} + +module_init(ucb1x00_init); +module_exit(ucb1x00_exit); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("Input driver to UCB1x00 touchscreen converter"); diff -urN orig/drivers/isdn/hardware/avm/avm_cs.c linux/drivers/isdn/hardware/avm/avm_cs.c --- orig/drivers/isdn/hardware/avm/avm_cs.c Tue May 27 10:04:38 2003 +++ linux/drivers/isdn/hardware/avm/avm_cs.c Tue May 27 10:11:26 2003 @@ -510,29 +510,30 @@ return 0; } /* avmcs_event */ -/*====================================================================*/ +static struct pcmcia_driver avmcs_driver = { + .owner = THIS_MODULE, + .drv = { + .name = "avmcs_cs", + }, + .attach = avmcs_attach, + .detach = avmcs_detach, +}; static int __init avmcs_init(void) { - servinfo_t serv; - CardServices(GetCardServicesInfo, &serv); - if (serv.Revision != CS_RELEASE_CODE) { - printk(KERN_NOTICE "avm_cs: Card Services release " - "does not match!\n"); - return -1; - } - register_pccard_driver(&dev_info, &avmcs_attach, &avmcs_detach); - return 0; + return pcmcia_register_driver(&avmcs_driver); } static void __exit avmcs_exit(void) { - unregister_pccard_driver(&dev_info); - while (dev_list != NULL) { - if (dev_list->state & DEV_CONFIG) - avmcs_release((u_long)dev_list); - avmcs_detach(dev_list); - } + pcmcia_unregister_driver(&avmcs_driver); + + /* XXX: this really needs to move into generic code.. */ + while (dev_list != NULL) { + if (dev_list->state & DEV_CONFIG) + avmcs_release((u_long)dev_list); + avmcs_detach(dev_list); + } } module_init(avmcs_init); diff -urN orig/drivers/isdn/hisax/avma1_cs.c linux/drivers/isdn/hisax/avma1_cs.c --- orig/drivers/isdn/hisax/avma1_cs.c Tue May 27 10:04:38 2003 +++ linux/drivers/isdn/hisax/avma1_cs.c Tue May 27 10:11:28 2003 @@ -515,30 +515,30 @@ return 0; } /* avma1cs_event */ -/*====================================================================*/ +static struct pcmcia_driver avma1cs_driver = { + .owner = THIS_MODULE, + .drv = { + .name = "avma1_cs", + }, + .attach = avma1cs_attach, + .detach = avma1cs_detach, +}; static int __init init_avma1_cs(void) { - servinfo_t serv; - DEBUG(0, "%s\n", version); - CardServices(GetCardServicesInfo, &serv); - if (serv.Revision != CS_RELEASE_CODE) { - printk(KERN_NOTICE "avma1_cs: Card Services release " - "does not match!\n"); - return -1; - } - register_pccard_driver(&dev_info, &avma1cs_attach, &avma1cs_detach); - return 0; + return pcmcia_register_driver(&avma1cs_driver); } static void __exit exit_avma1_cs(void) { - DEBUG(0, "avma1_cs: unloading\n"); - unregister_pccard_driver(&dev_info); - while (dev_list != NULL) - if (dev_list->state & DEV_CONFIG) - avma1cs_release((u_long)dev_list); - avma1cs_detach(dev_list); + pcmcia_unregister_driver(&avma1cs_driver); + + /* XXX: this really needs to move into generic code.. */ + while (dev_list != NULL) { + if (dev_list->state & DEV_CONFIG) + avma1cs_release((u_long)dev_list); + avma1cs_detach(dev_list); + } } module_init(init_avma1_cs); diff -urN orig/drivers/isdn/hisax/elsa_cs.c linux/drivers/isdn/hisax/elsa_cs.c --- orig/drivers/isdn/hisax/elsa_cs.c Mon May 5 17:39:09 2003 +++ linux/drivers/isdn/hisax/elsa_cs.c Sat May 17 22:53:23 2003 @@ -531,28 +531,27 @@ return 0; } /* elsa_cs_event */ -/*====================================================================*/ +static struct pcmcia_driver elsa_cs_driver = { + .owner = THIS_MODULE, + .drv = { + .name = "elsa_cs", + }, + .attach = elsa_cs_attach, + .detach = elsa_cs_detach, +}; static int __init init_elsa_cs(void) { - servinfo_t serv; - DEBUG(0, "%s\n", version); - CardServices(GetCardServicesInfo, &serv); - if (serv.Revision != CS_RELEASE_CODE) { - printk(KERN_NOTICE "elsa_cs: Card Services release " - "does not match!\n"); - return -1; - } - register_pccard_driver(&dev_info, &elsa_cs_attach, &elsa_cs_detach); - return 0; + return pcmcia_register_driver(&elsa_cs_driver); } static void __exit exit_elsa_cs(void) { - DEBUG(0, "elsa_cs: unloading\n"); - unregister_pccard_driver(&dev_info); - while (dev_list != NULL) - elsa_cs_detach(dev_list); + pcmcia_unregister_driver(&elsa_cs_driver); + + /* XXX: this really needs to move into generic code.. */ + while (dev_list != NULL) + elsa_cs_detach(dev_list); } module_init(init_elsa_cs); diff -urN orig/drivers/isdn/hisax/sedlbauer_cs.c linux/drivers/isdn/hisax/sedlbauer_cs.c --- orig/drivers/isdn/hisax/sedlbauer_cs.c Mon May 5 17:39:10 2003 +++ linux/drivers/isdn/hisax/sedlbauer_cs.c Sat May 17 22:53:23 2003 @@ -633,34 +633,32 @@ return 0; } /* sedlbauer_event */ -/*====================================================================*/ +static struct pcmcia_driver sedlbauer_driver = { + .owner = THIS_MODULE, + .drv = { + .name = "sedlbauer_cs", + }, + .attach = sedlbauer_attach, + .detach = sedlbauer_detach, +}; static int __init init_sedlbauer_cs(void) { - servinfo_t serv; - DEBUG(0, "%s\n", version); - CardServices(GetCardServicesInfo, &serv); - if (serv.Revision != CS_RELEASE_CODE) { - printk(KERN_NOTICE "sedlbauer_cs: Card Services release " - "does not match!\n"); - return -1; - } - register_pccard_driver(&dev_info, &sedlbauer_attach, &sedlbauer_detach); - return 0; + return pcmcia_register_driver(&sedlbauer_driver); } static void __exit exit_sedlbauer_cs(void) { - DEBUG(0, "sedlbauer_cs: unloading\n"); - unregister_pccard_driver(&dev_info); - while (dev_list != NULL) { - del_timer(&dev_list->release); - if (dev_list->state & DEV_CONFIG) - sedlbauer_release((u_long)dev_list); - sedlbauer_detach(dev_list); - } + pcmcia_unregister_driver(&sedlbauer_driver); + + /* XXX: this really needs to move into generic code.. */ + while (dev_list != NULL) { + del_timer(&dev_list->release); + if (dev_list->state & DEV_CONFIG) + sedlbauer_release((u_long)dev_list); + sedlbauer_detach(dev_list); + } } module_init(init_sedlbauer_cs); module_exit(exit_sedlbauer_cs); - diff -urN orig/drivers/l3/Kconfig linux/drivers/l3/Kconfig --- orig/drivers/l3/Kconfig Thu Jan 1 01:00:00 1970 +++ linux/drivers/l3/Kconfig Fri Nov 1 16:13:26 2002 @@ -0,0 +1,24 @@ +# +# L3 bus configuration +# + +menu "L3 serial bus support" + +config L3 + tristate "L3 support" + +config L3_ALGOBIT + bool "L3 bit-banging interfaces" + depends on L3=y + +config L3_BIT_SA1100_GPIO + bool "SA11x0 GPIO adapter" + depends on L3_ALGOBIT && ARCH_SA1100 + +# i2c must come before this +config BIT_SA1100_GPIO + bool + depends on L3_BIT_SA1100_GPIO || I2C_BIT_SA1100_GPIO=y + default y + +endmenu diff -urN orig/drivers/l3/Makefile linux/drivers/l3/Makefile --- orig/drivers/l3/Makefile Thu Jan 1 01:00:00 1970 +++ linux/drivers/l3/Makefile Tue Feb 18 00:34:51 2003 @@ -0,0 +1,11 @@ +# +# Makefile for the L3 bus driver. +# + +# Link order: +# (core, adapters, algorithms, drivers) then clients + +l3-$(CONFIG_L3_ALGOBIT) += l3-algo-bit.o +l3-$(CONFIG_BIT_SA1100_GPIO) += l3-bit-sa1100.o + +obj-$(CONFIG_L3) += l3-core.o $(l3-y) $(l3-drv-y) diff -urN orig/drivers/l3/l3-algo-bit.c linux/drivers/l3/l3-algo-bit.c --- orig/drivers/l3/l3-algo-bit.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/l3/l3-algo-bit.c Tue Oct 23 20:15:55 2001 @@ -0,0 +1,175 @@ +/* + * L3 bus algorithm module. + * + * Copyright (C) 2001 Russell King, 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. + * + * Note that L3 buses can share the same pins as I2C buses, so we must + * _not_ generate an I2C start condition. An I2C start condition is + * defined as a high-to-low transition of the data line while the clock + * is high. Therefore, we must only change the data line while the + * clock is low. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define setdat(adap,val) adap->setdat(adap->data, val) +#define setclk(adap,val) adap->setclk(adap->data, val) +#define setmode(adap,val) adap->setmode(adap->data, val) +#define setdatin(adap) adap->setdir(adap->data, 1) +#define setdatout(adap) adap->setdir(adap->data, 0) +#define getdat(adap) adap->getdat(adap->data) + +/* + * Send one byte of data to the chip. Data is latched into the chip on + * the rising edge of the clock. + */ +static void sendbyte(struct l3_algo_bit_data *adap, unsigned int byte) +{ + int i; + + for (i = 0; i < 8; i++) { + setclk(adap, 0); + udelay(adap->data_hold); + setdat(adap, byte & 1); + udelay(adap->data_setup); + setclk(adap, 1); + udelay(adap->clock_high); + byte >>= 1; + } +} + +/* + * Send a set of bytes to the chip. We need to pulse the MODE line + * between each byte, but never at the start nor at the end of the + * transfer. + */ +static void sendbytes(struct l3_algo_bit_data *adap, const char *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) { + if (i) { + udelay(adap->mode_hold); + setmode(adap, 0); + udelay(adap->mode); + } + setmode(adap, 1); + udelay(adap->mode_setup); + sendbyte(adap, buf[i]); + } +} + +/* + * Read one byte of data from the chip. Data is latched into the chip on + * the rising edge of the clock. + */ +static unsigned int readbyte(struct l3_algo_bit_data *adap) +{ + unsigned int byte = 0; + int i; + + for (i = 0; i < 8; i++) { + setclk(adap, 0); + udelay(adap->data_hold + adap->data_setup); + setclk(adap, 1); + if (getdat(adap)) + byte |= 1 << i; + udelay(adap->clock_high); + } + + return byte; +} + +/* + * Read a set of bytes from the chip. We need to pulse the MODE line + * between each byte, but never at the start nor at the end of the + * transfer. + */ +static void readbytes(struct l3_algo_bit_data *adap, char *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) { + if (i) { + udelay(adap->mode_hold); + setmode(adap, 0); + } + setmode(adap, 1); + udelay(adap->mode_setup); + buf[i] = readbyte(adap); + } +} + +static int l3_xfer(struct l3_adapter *l3_adap, struct l3_msg msgs[], int num) +{ + struct l3_algo_bit_data *adap = l3_adap->algo_data; + int i; + + /* + * If we share an I2C bus, ensure that it is in STOP mode + */ + setclk(adap, 1); + setdat(adap, 1); + setmode(adap, 1); + setdatout(adap); + udelay(adap->mode); + + for (i = 0; i < num; i++) { + struct l3_msg *pmsg = &msgs[i]; + + if (!(pmsg->flags & L3_M_NOADDR)) { + setmode(adap, 0); + udelay(adap->mode_setup); + sendbyte(adap, pmsg->addr); + udelay(adap->mode_hold); + } + + if (pmsg->flags & L3_M_RD) { + setdatin(adap); + readbytes(adap, pmsg->buf, pmsg->len); + } else { + setdatout(adap); + sendbytes(adap, pmsg->buf, pmsg->len); + } + } + + /* + * Ensure that we leave the bus in I2C stop mode. + */ + setclk(adap, 1); + setdat(adap, 1); + setmode(adap, 0); + setdatin(adap); + + return num; +} + +static struct l3_algorithm l3_bit_algo = { + name: "L3 bit-shift algorithm", + xfer: l3_xfer, +}; + +int l3_bit_add_bus(struct l3_adapter *adap) +{ + adap->algo = &l3_bit_algo; + return l3_add_adapter(adap); +} + +int l3_bit_del_bus(struct l3_adapter *adap) +{ + return l3_del_adapter(adap); +} + +EXPORT_SYMBOL(l3_bit_add_bus); +EXPORT_SYMBOL(l3_bit_del_bus); diff -urN orig/drivers/l3/l3-bit-sa1100.c linux/drivers/l3/l3-bit-sa1100.c --- orig/drivers/l3/l3-bit-sa1100.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/l3/l3-bit-sa1100.c Tue May 27 16:25:22 2003 @@ -0,0 +1,274 @@ +/* + * linux/drivers/l3/l3-bit-sa1100.c + * + * Copyright (C) 2001 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. + * + * This is a combined I2C and L3 bus driver. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define NAME "l3-bit-sa1100-gpio" + +struct bit_data { + unsigned int sda; + unsigned int scl; + unsigned int l3_mode; +}; + +static int getsda(void *data) +{ + struct bit_data *bits = data; + + return GPLR & bits->sda; +} + +#ifdef CONFIG_I2C_BIT_SA1100_GPIO +static void i2c_setsda(void *data, int state) +{ + struct bit_data *bits = data; + unsigned long flags; + + local_irq_save(flags); + if (state) + GPDR &= ~bits->sda; + else { + GPCR = bits->sda; + GPDR |= bits->sda; + } + local_irq_restore(flags); +} + +static void i2c_setscl(void *data, int state) +{ + struct bit_data *bits = data; + unsigned long flags; + + local_irq_save(flags); + if (state) + GPDR &= ~bits->scl; + else { + GPCR = bits->scl; + GPDR |= bits->scl; + } + local_irq_restore(flags); +} + +static int i2c_getscl(void *data) +{ + struct bit_data *bits = data; + + return GPLR & bits->scl; +} + +static struct i2c_algo_bit_data i2c_bit_data = { + .setsda = i2c_setsda, + .setscl = i2c_setscl, + .getsda = getsda, + .getscl = i2c_getscl, + .udelay = 10, + .mdelay = 10, + .timeout = 100, +}; + +static struct i2c_adapter i2c_adapter = { + .dev = { + .name = NAME, + }, + .algo_data = &i2c_bit_data, +}; + +#define LOCK &i2c_adapter.bus_lock + +static int __init i2c_init(struct bit_data *bits) +{ + i2c_bit_data.data = bits; + return i2c_bit_add_bus(&i2c_adapter); +} + +static void i2c_exit(void) +{ + i2c_bit_del_bus(&i2c_adapter); +} + +#else +static DECLARE_MUTEX(l3_lock); +#define LOCK &l3_lock +#define i2c_init(bits) (0) +#define i2c_exit() do { } while (0) +#endif + +#ifdef CONFIG_L3_BIT_SA1100_GPIO +/* + * iPAQs need the clock line driven hard high and low. + */ +static void l3_setscl(void *data, int state) +{ + struct bit_data *bits = data; + unsigned long flags; + + local_irq_save(flags); + if (state) + GPSR = bits->scl; + else + GPCR = bits->scl; + GPDR |= bits->scl; + local_irq_restore(flags); +} + +static void l3_setsda(void *data, int state) +{ + struct bit_data *bits = data; + + if (state) + GPSR = bits->sda; + else + GPCR = bits->sda; +} + +static void l3_setdir(void *data, int in) +{ + struct bit_data *bits = data; + unsigned long flags; + + local_irq_save(flags); + if (in) + GPDR &= ~bits->sda; + else + GPDR |= bits->sda; + local_irq_restore(flags); +} + +static void l3_setmode(void *data, int state) +{ + struct bit_data *bits = data; + + if (state) + GPSR = bits->l3_mode; + else + GPCR = bits->l3_mode; +} + +static struct l3_algo_bit_data l3_bit_data = { + .data = NULL, + .setdat = l3_setsda, + .setclk = l3_setscl, + .setmode = l3_setmode, + .setdir = l3_setdir, + .getdat = getsda, + .data_hold = 1, + .data_setup = 1, + .clock_high = 1, + .mode_hold = 1, + .mode_setup = 1, +}; + +static struct l3_adapter l3_adapter = { + .owner = THIS_MODULE, + .name = NAME, + .algo_data = &l3_bit_data, + .lock = LOCK, +}; + +static int __init l3_init(struct bit_data *bits) +{ + l3_bit_data.data = bits; + return l3_bit_add_bus(&l3_adapter); +} + +static void __exit l3_exit(void) +{ + l3_bit_del_bus(&l3_adapter); +} +#else +#define l3_init(bits) (0) +#define l3_exit() do { } while (0) +#endif + +static struct bit_data bit_data; + +static int __init bus_init(void) +{ + struct bit_data *bit = &bit_data; + unsigned long flags; + int ret; + + if (machine_is_assabet() || machine_is_pangolin()) { + bit->sda = GPIO_GPIO15; + bit->scl = GPIO_GPIO18; + bit->l3_mode = GPIO_GPIO17; + } + + if (machine_is_h3600() || machine_is_h3100()) { + bit->sda = GPIO_GPIO14; + bit->scl = GPIO_GPIO16; + bit->l3_mode = GPIO_GPIO15; + } + + if (machine_is_stork()) { + bit->sda = GPIO_GPIO15; + bit->scl = GPIO_GPIO18; + bit->l3_mode = GPIO_GPIO17; + } + + if (!bit->sda) + return -ENODEV; + + /* + * Default level for L3 mode is low. + * We set SCL and SDA high (i2c idle state). + */ + local_irq_save(flags); + GPDR &= ~(bit->scl | bit->sda); + GPCR = bit->l3_mode | bit->scl | bit->sda; + GPDR |= bit->l3_mode; + local_irq_restore(flags); + + if (machine_is_assabet()) { + /* + * Release reset on UCB1300, ADI7171 and UDA1341. We + * need to do this here so that we can communicate on + * the I2C/L3 buses. + */ + ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); + mdelay(1); + ASSABET_BCR_clear(ASSABET_BCR_CODEC_RST); + mdelay(1); + ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); + } + + ret = i2c_init(bit); + if (ret == 0 && bit->l3_mode) { + ret = l3_init(bit); + if (ret) + i2c_exit(); + } + + return ret; +} + +static void __exit bus_exit(void) +{ + l3_exit(); + i2c_exit(); +} + +module_init(bus_init); +module_exit(bus_exit); diff -urN orig/drivers/l3/l3-core.c linux/drivers/l3/l3-core.c --- orig/drivers/l3/l3-core.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/l3/l3-core.c Tue Mar 25 14:30:02 2003 @@ -0,0 +1,376 @@ +/* + * linux/drivers/l3/l3-core.c + * + * Copyright (C) 2001 Russell King + * + * General structure taken from i2c-core.c by Simon G. Vogl + * + * 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. + * + * See linux/Documentation/l3 for further documentation. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +static DECLARE_MUTEX(adapter_lock); +static LIST_HEAD(adapter_list); + +static DECLARE_MUTEX(driver_lock); +static LIST_HEAD(driver_list); + +/** + * l3_add_adapter - register a new L3 bus adapter + * @adap: l3_adapter structure for the registering adapter + * + * Make the adapter available for use by clients using name adap->name. + * The adap->adapters list is initialised by this function. + * + * Returns 0; + */ +int l3_add_adapter(struct l3_adapter *adap) +{ + INIT_LIST_HEAD(&adap->clients); + down(&adapter_lock); + list_add(&adap->adapters, &adapter_list); + up(&adapter_lock); + return 0; +} + +/** + * l3_del_adapter - unregister a L3 bus adapter + * @adap: l3_adapter structure to unregister + * + * Remove an adapter from the list of available L3 Bus adapters. + * + * Returns 0; + */ +int l3_del_adapter(struct l3_adapter *adap) +{ + down(&adapter_lock); + list_del(&adap->adapters); + up(&adapter_lock); + return 0; +} + +static struct l3_adapter *__l3_get_adapter(const char *name) +{ + struct list_head *l; + + list_for_each(l, &adapter_list) { + struct l3_adapter *adap = list_entry(l, struct l3_adapter, adapters); + + if (strcmp(adap->name, name) == 0) + return adap; + } + + return NULL; +} + +/** + * l3_get_adapter - get a reference to an adapter + * @name: driver name + * + * Obtain a l3_adapter structure for the specified adapter. If the adapter + * is not currently load, then load it. The adapter will be locked in core + * until all references are released via l3_put_adapter. + */ +struct l3_adapter *l3_get_adapter(const char *name) +{ + struct l3_adapter *adap; + int try; + + for (try = 0; try < 2; try ++) { + down(&adapter_lock); + adap = __l3_get_adapter(name); + if (adap && !try_module_get(adap->owner)) + adap = NULL; + up(&adapter_lock); + + if (adap) + break; + + if (try == 0) + request_module(name); + } + + return adap; +} + +/** + * l3_put_adapter - release a reference to an adapter + * @adap: driver to release reference + * + * Indicate to the L3 core that you no longer require the adapter reference. + * The adapter module may be unloaded when there are no references to its + * data structure. + * + * You must not use the reference after calling this function. + */ +void l3_put_adapter(struct l3_adapter *adap) +{ + if (adap && adap->owner) + module_put(adap->owner); +} + +/** + * l3_add_driver - register a new L3 device driver + * @driver - driver structure to make available + * + * Make the driver available for use by clients using name driver->name. + * The driver->drivers list is initialised by this function. + * + * Returns 0; + */ +int l3_add_driver(struct l3_driver *driver) +{ + down(&driver_lock); + list_add(&driver->drivers, &driver_list); + up(&driver_lock); + return 0; +} + +/** + * l3_del_driver - unregister a L3 device driver + * @driver: driver to remove + * + * Remove an driver from the list of available L3 Bus device drivers. + * + * Returns 0; + */ +int l3_del_driver(struct l3_driver *driver) +{ + down(&driver_lock); + list_del(&driver->drivers); + up(&driver_lock); + return 0; +} + +static struct l3_driver *__l3_get_driver(const char *name) +{ + struct list_head *l; + + list_for_each(l, &driver_list) { + struct l3_driver *drv = list_entry(l, struct l3_driver, drivers); + + if (strcmp(drv->name, name) == 0) + return drv; + } + + return NULL; +} + +/** + * l3_get_driver - get a reference to a driver + * @name: driver name + * + * Obtain a l3_driver structure for the specified driver. If the driver is + * not currently load, then load it. The driver will be locked in core + * until all references are released via l3_put_driver. + */ +struct l3_driver *l3_get_driver(const char *name) +{ + struct l3_driver *drv; + int try; + + for (try = 0; try < 2; try ++) { + down(&adapter_lock); + drv = __l3_get_driver(name); + if (drv && !try_module_get(drv->owner)) + drv = NULL; + up(&adapter_lock); + + if (drv) + break; + + if (try == 0) + request_module(name); + } + + return drv; +} + +/** + * l3_put_driver - release a reference to a driver + * @drv: driver to release reference + * + * Indicate to the L3 core that you no longer require the driver reference. + * The driver module may be unloaded when there are no references to its + * data structure. + * + * You must not use the reference after calling this function. + */ +void l3_put_driver(struct l3_driver *drv) +{ + if (drv) + module_put(drv->owner); +} + +/** + * l3_attach_client - attach a client to an adapter and driver + * @client: client structure to attach + * @adap: adapter (module) name + * @drv: driver (module) name + * + * Attempt to attach a client (a user of a device driver) to a particular + * driver and adapter. If the specified driver or adapter aren't registered, + * request_module is used to load the relevant modules. + * + * Returns 0 on success, or negative error code. + */ +int l3_attach_client(struct l3_client *client, const char *adap, const char *drv) +{ + struct l3_adapter *adapter = l3_get_adapter(adap); + struct l3_driver *driver = l3_get_driver(drv); + int ret = -ENOENT; + + if (!adapter) + printk(KERN_ERR "%s: unable to get adapter: %s\n", + __FUNCTION__, adap); + if (!driver) + printk(KERN_ERR "%s: unable to get driver: %s\n", + __FUNCTION__, drv); + + if (adapter && driver) { + ret = 0; + + client->adapter = adapter; + client->driver = driver; + + list_add(&client->__adap, &adapter->clients); + + if (driver->attach_client) + ret = driver->attach_client(client); + } + + if (ret) { + l3_put_driver(driver); + l3_put_adapter(adapter); + } + return ret; +} + +/** + * l3_detach_client - detach a client from an adapter and driver + * @client: client structure to detach + * + * Detach the client from the adapter and driver. + */ +int l3_detach_client(struct l3_client *client) +{ + struct l3_adapter *adapter = client->adapter; + struct l3_driver *driver = client->driver; + + driver->detach_client(client); + + client->adapter = NULL; + client->driver = NULL; + + l3_put_driver(driver); + l3_put_adapter(adapter); + + list_del(&client->__adap); + + return 0; +} + +/** + * l3_transfer - transfer information on an L3 bus + * @adap: adapter structure to perform transfer on + * @msgs: array of l3_msg structures describing transfer + * @num: number of l3_msg structures + * + * Transfer the specified messages to/from a device on the L3 bus. + * + * Returns number of messages successfully transferred, otherwise negative + * error code. + */ +int l3_transfer(struct l3_adapter *adap, struct l3_msg msgs[], int num) +{ + int ret = -ENOSYS; + + if (adap->algo->xfer) { + down(adap->lock); + ret = adap->algo->xfer(adap, msgs, num); + up(adap->lock); + } + return ret; +} + +/** + * l3_write - send data to a device on an L3 bus + * @client: registered client structure + * @addr: L3 bus address + * @buf: buffer for bytes to send + * @len: number of bytes to send + * + * Send len bytes pointed to by buf to device address addr on the L3 bus + * described by client. + * + * Returns the number of bytes transferred, or negative error code. + */ +int l3_write(struct l3_client *client, int addr, const char *buf, int len) +{ + struct l3_adapter *adap = client->adapter; + struct l3_msg msg; + int ret; + + msg.addr = addr; + msg.flags = 0; + msg.buf = (char *)buf; + msg.len = len; + + ret = l3_transfer(adap, &msg, 1); + return ret == 1 ? len : ret; +} + +/** + * l3_read - receive data from a device on an L3 bus + * @client: registered client structure + * @addr: L3 bus address + * @buf: buffer for bytes to receive + * @len: number of bytes to receive + * + * Receive len bytes from device address addr on the L3 bus described by + * client to a buffer pointed to by buf. + * + * Returns the number of bytes transferred, or negative error code. + */ +int l3_read(struct l3_client *client, int addr, char *buf, int len) +{ + struct l3_adapter *adap = client->adapter; + struct l3_msg msg; + int ret; + + msg.addr = addr; + msg.flags = L3_M_RD; + msg.buf = buf; + msg.len = len; + + ret = l3_transfer(adap, &msg, 1); + return ret == 1 ? len : ret; +} + +EXPORT_SYMBOL(l3_add_adapter); +EXPORT_SYMBOL(l3_del_adapter); +EXPORT_SYMBOL(l3_get_adapter); +EXPORT_SYMBOL(l3_put_adapter); + +EXPORT_SYMBOL(l3_add_driver); +EXPORT_SYMBOL(l3_del_driver); +EXPORT_SYMBOL(l3_get_driver); +EXPORT_SYMBOL(l3_put_driver); + +EXPORT_SYMBOL(l3_attach_client); +EXPORT_SYMBOL(l3_detach_client); + +EXPORT_SYMBOL(l3_transfer); +EXPORT_SYMBOL(l3_write); +EXPORT_SYMBOL(l3_read); diff -urN orig/drivers/media/video/Kconfig linux/drivers/media/video/Kconfig --- orig/drivers/media/video/Kconfig Sun Apr 20 16:31:59 2003 +++ linux/drivers/media/video/Kconfig Sun Apr 20 15:56:47 2003 @@ -257,5 +257,9 @@ whenever you want). If you want to compile it as a module, say M here and read . +config VIDEO_CYBERPRO + tristate "NetWinder Video for Linux (EXPERIMENTAL)" + depends on EXPERIMENTAL && ARCH_NETWINDER + endmenu diff -urN orig/drivers/media/video/Makefile linux/drivers/media/video/Makefile --- orig/drivers/media/video/Makefile Sun Apr 20 16:31:59 2003 +++ linux/drivers/media/video/Makefile Sun Apr 20 15:35:53 2003 @@ -21,6 +21,7 @@ obj-$(CONFIG_VIDEO_ZORAN_DC10) += saa7110.o adv7175.o obj-$(CONFIG_VIDEO_ZORAN_LML33) += bt819.o bt856.o obj-$(CONFIG_VIDEO_ZORAN) += zr36067.o +obj-$(CONFIG_VIDEO_CYBERPRO) += cyberpro.o saa7111.o obj-$(CONFIG_VIDEO_PMS) += pms.o obj-$(CONFIG_VIDEO_PLANB) += planb.o obj-$(CONFIG_VIDEO_VINO) += vino.o diff -urN orig/drivers/media/video/cyberpro.c linux/drivers/media/video/cyberpro.c --- orig/drivers/media/video/cyberpro.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/media/video/cyberpro.c Wed Apr 24 21:18:53 2002 @@ -0,0 +1,2023 @@ +/* + * CyberPro 2000 video capture driver for the Rebel.com NetWinder + * + * (C) 1999-2002 Russell King + * + * Re-written from Rebel.com's vidcap driver. + * + * Architecture + * ------------ + * The NetWinder video capture consists of a SAA7111 video decoder chip + * connected to the CyberPro feature bus. The video data is captured to + * the VGA memory, where the CyberPro can overlay (by chromakeying) the + * data onto the VGA display. + * + * The CyberPro also has some nifty features, including a second overlay + * and picture in picture mode. We do not currently use these features. + * + * Power Saving + * ------------ + * Please note that rev.5 NetWinders have the ability to hold the SAA7111 + * decoder chip into reset, which saves power. The only time at which + * this is done is when the driver is unloaded, which implies that this + * is compiled as a module. + * + * In this case, you will want the kernel to automatically load this + * driver when required. Place the following line in /etc/modules.conf + * to enable this: + * + * alias char-major-81-0 cyberpro + * + * The relevant modules will be automatically loaded by modprobe on a + * as and when needed basis. + * + * Capture resolution + * ------------------ + * The maximum useful capture resolution is: + * 625-line UK: 716x576 + * 525-line US: ? + * + * Bugs + * ---- + * 1. The CyberPro chip seems to be prone to randomly scribbling over VGA + * memory [hopefully fixed with new capture enable/freeze stuff] + * 2. read()ing pauses video capture, and sometimes triggers bug 1. + * 3. mmap() is not supported (requires BM-DMA - see bug 4) + * 4. Really, we want to do scatter BM-DMA. Is the CyberPro capable of this? + * The Cyberpro seems to randomly scribble to various PCI addresses if you + * transfer >16 words. + * 5. We shouldn't ignore O_NONBLOCK when reading a frame. + * 6. The incoming stream on the NetWinder is CCIR656, which is YUV422. + * CyberPro docs also call the format we capture and overlay "YUV422", + * but we actually seem to have Y, U, Y, V bytes (is this YUYV format?) + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("CyberPro v4l video grabber"); +MODULE_LICENSE("GPL"); + +#include "../../video/cyber2000fb.h" + +/* + * These enable various experimental features. Most of these + * are just plain broken or just don't work at the moment. + */ +/* + * Enable this if you want mmap() access. (see bug 4) + */ +#undef USE_MMAP + +/* + * Enable this if you want mmio access. (slow) + */ +#define USE_MMIO + +/* + * The V4L API is unclear whether VIDIOCSCAPTURE call is allowed while + * capture is running. The default is to disallow the call. + * + * Define this if you do want to allow the call while capture is active. + */ +#undef ALLOW_SCAPTURE_WHILE_CAP + +/* + * We capture two frames + */ +#define NR_FRAMES 2 + +/* + * One frame of video is 202 pages, assuming YUV422 format, 716x576 + */ +#define NR_PAGES 202 + +struct src_info { + unsigned int offset; /* offset of source data */ + unsigned int x; /* source x */ + unsigned int y; /* source y */ + unsigned int width; /* source width */ + unsigned int height; /* source height */ + unsigned int format; /* source format */ +}; + +struct dst_info { + unsigned int x; /* destination x */ + unsigned int y; /* destination y */ + unsigned int width; /* destination width */ + unsigned int height; /* destination height */ + unsigned int chromakey; /* chromakey */ + unsigned int flags; /* flags (eg, chromakey enable) */ +}; + +struct cyberpro_vidinfo; + +struct win_info { + void (*init)(struct cyberpro_vidinfo *dp, struct win_info *wi); + void (*set_src)(struct cyberpro_vidinfo *dp, struct win_info *wi); + void (*set_win)(struct cyberpro_vidinfo *dp, struct win_info *wi); + void (*ctl)(struct cyberpro_vidinfo *dp, struct win_info *wi, int on_off); + + /* public */ + struct src_info src; + struct dst_info dst; + + /* private */ + unsigned short vid_fifo_ctl; + unsigned char vid_fmt; + unsigned char vid_disp_ctl1; + unsigned char vid_fifo_ctl1; + unsigned char vid_misc_ctl1; +}; + +struct framebuf { + unsigned int offset; /* mmap offset for this frame */ + unsigned int status; +#define FRAME_FREE 0 +#define FRAME_DONE 1 +#define FRAME_WAITING 2 +#define FRAME_GRABBING 3 + + /* + * Bus-Master DMA stuff. Note that we should + * probably use the kiovec stuff instead. + */ + unsigned long bus_addr[NR_PAGES]; /* list of pages */ + struct page *pages[NR_PAGES]; + void *buffer; + int dbg; +}; + +struct cyberpro_vidinfo { + struct video_device dev; + struct i2c_bus bus; + struct cyberpro_info info; /* host information */ + unsigned char *regs; + unsigned int irq; /* PCI interrupt number */ + + /* hardware configuration */ + unsigned int stream_fmt; /* format of stream from decoder*/ + + /* software settings */ + unsigned int decoder:1; /* decoder loaded */ + unsigned int interlace:1; /* interlace */ + unsigned int buf_set:1; /* VIDIOCSFBUF has been issued */ + unsigned int win_set:1; /* VIDIOCSWIN has been issued */ + unsigned int cap_active:1; /* capture is active */ + unsigned int ovl_active:1; /* overlay is active */ + unsigned int mmaped:1; /* buffer is mmap()d */ + unsigned int unused:25; + + unsigned int users; /* number of users */ + unsigned long cap_mem_offset; /* capture framebuffer offset */ + void * buffer; /* kernel capture buffer */ + unsigned int norm; /* video standard */ + + struct video_capability cap; /* capabilities */ + struct video_picture pic; /* current picture settings */ + struct video_buffer buf; /* display parameters */ + struct video_capture capt; /* video capture params */ + + struct win_info *ovl; /* overlay window set */ + struct win_info ext; /* "Extended" window info */ + struct win_info v2; /* "V2" window info */ + struct win_info x2; /* "X2" window info */ + + unsigned int bm_offset; /* Cap memory bus master offset */ + unsigned int bm_index; /* Cap page index */ + +#ifdef USE_MMAP + unsigned int frame_idx; /* currently grabbing frame */ + unsigned int frame_size; + struct framebuf frame[NR_FRAMES]; + wait_queue_head_t frame_wait; +#endif + + wait_queue_head_t vbl_wait; + + /* + * cyberpro registers + */ + unsigned char cap_mode1; + unsigned char cap_mode2; + unsigned char cap_miscctl; + unsigned char vfac1; + unsigned char vfac3; +}; + +/* + * Our access methods. + */ +#define cyberpro_writel(val,reg,dp) writel(val, (dp)->regs + (reg)) +#define cyberpro_writew(val,reg,dp) writew(val, (dp)->regs + (reg)) +#define cyberpro_writeb(val,reg,dp) writeb(val, (dp)->regs + (reg)) + +#define cyberpro_readb(reg,dp) readb((dp)->regs + (reg)) + +static inline void +cyberpro_grphw(unsigned int reg, unsigned int val, struct cyberpro_vidinfo *dp) +{ + cyberpro_writew((reg & 255) | val << 8, 0x3ce, dp); +} + +static void cyberpro_grphw8(unsigned int reg, unsigned int val, struct cyberpro_vidinfo *dp) +{ + cyberpro_grphw(reg, val, dp); +} + +static unsigned char cyberpro_grphr8(int reg, struct cyberpro_vidinfo *dp) +{ + cyberpro_writeb(reg, 0x3ce, dp); + return cyberpro_readb(0x3cf, dp); +} + +static void cyberpro_grphw16(int reg, unsigned int val, struct cyberpro_vidinfo *dp) +{ + cyberpro_grphw(reg, val, dp); + cyberpro_grphw(reg + 1, val >> 8, dp); +} + +static void cyberpro_grphw24(int reg, unsigned int val, struct cyberpro_vidinfo *dp) +{ + cyberpro_grphw(reg, val, dp); + cyberpro_grphw(reg + 1, val >> 8, dp); + cyberpro_grphw(reg + 2, val >> 16, dp); +} + +#if 0 +static void +cyberpro_dbg_dump(void) +{ + int i; + unsigned char idx[] = + { 0x30, 0x3e, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad }; + printk(KERN_DEBUG); + for (i = 0; i < sizeof(idx); i++) + printk("%02x ", idx[i]); + printk("\n" KERN_DEBUG); + for (i = 0; i < sizeof(idx); i++) + printk("%02x ", cyberpro_grphr8(idx[i])); + printk("\n"); +} +#endif + +/* + * On the NetWinder, we can put the SAA7111 to sleep by holding + * it in reset. + * + * Note: once we have initialised the SAA7111, we can't put it back to + * sleep and expect it to keep its settings. Maybe a better solution + * is to register/de-register the i2c bus in open/release? + */ +static void +decoder_sleep(int sleep) +{ +#ifdef CONFIG_ARCH_NETWINDER + extern spinlock_t gpio_lock; + + spin_lock_irq(&gpio_lock); + cpld_modify(CPLD_7111_DISABLE, sleep ? CPLD_7111_DISABLE : 0); + spin_unlock_irq(&gpio_lock); + + if (!sleep) { + /* + * wait 20ms for device to wake up + */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 50); + } +#endif +} + +/* -------------------------------- I2C support ---------------------------- */ + +#define I2C_DELAY 100 + +static void +cyberpro_i2c_setlines(struct i2c_bus *bus, int ctrl, int data) +{ + struct cyberpro_vidinfo *dp = bus->data; + int v; + + v = (ctrl ? EXT_LATCH2_I2C_CLKEN : 0x00) | (data ? EXT_LATCH2_I2C_DATEN : 0x00); + cyberpro_grphw8(EXT_LATCH2, v, dp); + + udelay(I2C_DELAY); +} + +static int +cyberpro_i2c_getdataline(struct i2c_bus *bus) +{ + struct cyberpro_vidinfo *dp = bus->data; + unsigned long flags; + int v; + + save_flags(flags); + cli(); + + v = cyberpro_grphr8(EXT_LATCH2, dp); + + restore_flags(flags); + + return v & EXT_LATCH2_I2C_DAT ? 1 : 0; +} + +static void +cyberpro_i2c_attach(struct i2c_bus *bus, int id) +{ + struct cyberpro_vidinfo *dp = bus->data; + int zero = 0; + + if (id == I2C_DRIVERID_VIDEODECODER) { + __u16 norm = dp->norm; + i2c_control_device(bus, id, DECODER_SET_NORM, &norm); + i2c_control_device(bus, id, DECODER_SET_PICTURE, &dp->pic); + i2c_control_device(bus, id, DECODER_ENABLE_OUTPUT, &zero); + + dp->decoder = 1; + } +} + +static void +cyberpro_i2c_detach(struct i2c_bus *bus, int id) +{ + struct cyberpro_vidinfo *dp = bus->data; + + if (id == I2C_DRIVERID_VIDEODECODER) + dp->decoder = 0; +} + +static struct i2c_bus cyberpro_i2c_bus = { + name: "", + id: I2C_BUSID_CYBER2000, + bus_lock: SPIN_LOCK_UNLOCKED, + attach_inform: cyberpro_i2c_attach, + detach_inform: cyberpro_i2c_detach, + i2c_setlines: cyberpro_i2c_setlines, + i2c_getdataline: cyberpro_i2c_getdataline, +}; + +/*------------------------- Extended Overlay Window ------------------------- + * Initialise 1st overlay window (works) + */ +static void +cyberpro_ext_init(struct cyberpro_vidinfo *dp, struct win_info *wi) +{ + wi->vid_fifo_ctl = 0xf87c; + wi->vid_fmt = EXT_VID_FMT_YUV422; + wi->vid_disp_ctl1 = EXT_VID_DISP_CTL1_VINTERPOL_OFF | + EXT_VID_DISP_CTL1_NOCLIP; + wi->vid_fifo_ctl1 = EXT_VID_FIFO_CTL1_INTERLEAVE | + EXT_VID_FIFO_CTL1_OE_HIGH; + wi->vid_misc_ctl1 = 0; + + cyberpro_grphw8 (EXT_VID_DISP_CTL1, wi->vid_disp_ctl1, dp); + cyberpro_grphw16(EXT_DDA_X_INIT, 0x0800, dp); + cyberpro_grphw16(EXT_DDA_Y_INIT, 0x0800, dp); + cyberpro_grphw16(EXT_VID_FIFO_CTL, wi->vid_fifo_ctl, dp); + cyberpro_grphw8 (EXT_VID_FIFO_CTL1, wi->vid_fifo_ctl1, dp); +} + +/* + * Set the source parameters for the extended window + */ +static void +cyberpro_ext_set_src(struct cyberpro_vidinfo *dp, struct win_info *wi) +{ + unsigned int phase, pitch; + + pitch = (wi->src.width >> 2) & 0x0fff; + phase = (wi->src.width + 3) >> 2; + + wi->vid_fmt &= ~7; + switch (wi->src.format) { + case VIDEO_PALETTE_RGB565: wi->vid_fmt |= EXT_VID_FMT_RGB565; break; + case VIDEO_PALETTE_RGB24: wi->vid_fmt |= EXT_VID_FMT_RGB888_24; break; + case VIDEO_PALETTE_RGB32: wi->vid_fmt |= EXT_VID_FMT_RGB888_32; break; + case VIDEO_PALETTE_RGB555: wi->vid_fmt |= EXT_VID_FMT_RGB555; break; + case VIDEO_PALETTE_YUV422: wi->vid_fmt |= EXT_VID_FMT_YUV422; break; + } + + cyberpro_grphw24(EXT_MEM_START, wi->src.offset, dp); + cyberpro_grphw16(EXT_SRC_WIDTH, pitch | ((phase << 4) & 0xf000), dp); + cyberpro_grphw8 (EXT_SRC_WIN_WIDTH, phase, dp); + cyberpro_grphw8 (EXT_VID_FMT, wi->vid_fmt, dp); +} + +/* + * Set overlay1 window + */ +static void +cyberpro_ext_set_win(struct cyberpro_vidinfo *dp, struct win_info *wi) +{ + unsigned int xscale, yscale; + unsigned int xoff, yoff; + + /* + * Note: the offset does not appear to be influenced by + * hardware scrolling. + */ + xoff = yoff = 0; + + xoff += wi->dst.x; + yoff += wi->dst.y; + + xscale = wi->src.width; + + if (wi->dst.width >= wi->src.width * 2) { + wi->vid_fmt |= EXT_VID_FMT_DBL_H_PIX; + xscale *= 2; + } else { + wi->vid_fmt &= ~EXT_VID_FMT_DBL_H_PIX; + } + + xscale = ((xscale - /*2*/0) * 4096) / wi->dst.width; + yscale = ((wi->src.height - /*2*/0) * 4096) / wi->dst.height; + + cyberpro_grphw16(EXT_X_START, xoff, dp); + cyberpro_grphw16(EXT_X_END, xoff + wi->dst.width, dp); + cyberpro_grphw16(EXT_Y_START, yoff, dp); + cyberpro_grphw16(EXT_Y_END, yoff + wi->dst.height, dp); + cyberpro_grphw24(EXT_COLOUR_COMPARE, wi->dst.chromakey, dp); + cyberpro_grphw16(EXT_DDA_X_INC, xscale, dp); + cyberpro_grphw16(EXT_DDA_Y_INC, yscale, dp); + cyberpro_grphw8(EXT_VID_FMT, wi->vid_fmt, dp); + + if (wi->dst.flags & VIDEO_WINDOW_CHROMAKEY) + wi->vid_disp_ctl1 &= ~EXT_VID_DISP_CTL1_IGNORE_CCOMP; + else + wi->vid_disp_ctl1 |= EXT_VID_DISP_CTL1_IGNORE_CCOMP; +} + +/* + * Enable or disable the 1st overlay window. Note that for anything + * useful to be displayed, we must have capture enabled. + */ +static void +cyberpro_ext_ctl(struct cyberpro_vidinfo *dp, struct win_info *wi, int on) +{ + if (on) + wi->vid_disp_ctl1 |= EXT_VID_DISP_CTL1_ENABLE_WINDOW; + else + wi->vid_disp_ctl1 &= ~EXT_VID_DISP_CTL1_ENABLE_WINDOW; + + cyberpro_grphw8(EXT_VID_DISP_CTL1, wi->vid_disp_ctl1, dp); +} + +/*------------------------------- V2 Overlay Window ------------------------- + * Initialise 2nd overlay window (guesswork) + */ +static void +cyberpro_v2_init(struct cyberpro_vidinfo *dp, struct win_info *wi) +{ + wi->vid_fifo_ctl = 0xf87c; + wi->vid_fmt = EXT_VID_FMT_YUV422; + wi->vid_disp_ctl1 = EXT_VID_DISP_CTL1_VINTERPOL_OFF | + EXT_VID_DISP_CTL1_NOCLIP; + wi->vid_fifo_ctl1 = 0x06; + wi->vid_misc_ctl1 = 0; + + cyberpro_grphw8(REG_BANK, REG_BANK_Y, dp); + cyberpro_grphw8 (Y_V2_VID_DISP_CTL1, wi->vid_disp_ctl1, dp); + /* No DDA init values */ + cyberpro_grphw16(Y_V2_VID_FIFO_CTL, wi->vid_fifo_ctl, dp); + cyberpro_grphw8 (Y_V2_VID_FIFO_CTL1, wi->vid_fifo_ctl1, dp); +} + +/* + * Set the source parameters for the v2 window + */ +static void +cyberpro_v2_set_src(struct cyberpro_vidinfo *dp, struct win_info *wi) +{ + unsigned int phase, pitch; + + pitch = (wi->src.width >> 2) & 0x0fff; + phase = (wi->src.width + 3) >> 2; + + wi->vid_fmt &= ~7; + switch (wi->src.format) { + case VIDEO_PALETTE_RGB565: wi->vid_fmt |= EXT_VID_FMT_RGB565; break; + case VIDEO_PALETTE_RGB24: wi->vid_fmt |= EXT_VID_FMT_RGB888_24; break; + case VIDEO_PALETTE_RGB32: wi->vid_fmt |= EXT_VID_FMT_RGB888_32; break; + case VIDEO_PALETTE_RGB555: wi->vid_fmt |= EXT_VID_FMT_RGB555; break; + case VIDEO_PALETTE_YUV422: wi->vid_fmt |= EXT_VID_FMT_YUV422; break; + } + + cyberpro_grphw8(REG_BANK, REG_BANK_X, dp); + cyberpro_grphw24(X_V2_VID_MEM_START, wi->src.offset, dp); + cyberpro_grphw16(X_V2_VID_SRC_WIDTH, pitch | ((phase << 4) & 0xf000), dp); + cyberpro_grphw8 (X_V2_VID_SRC_WIN_WIDTH, phase, dp); + + cyberpro_grphw8(REG_BANK, REG_BANK_Y, dp); + cyberpro_grphw8(Y_V2_VID_FMT, wi->vid_fmt, dp); +} + +/* + * Set v2 window + */ +static void +cyberpro_v2_set_win(struct cyberpro_vidinfo *dp, struct win_info *wi) +{ + unsigned int xscale, yscale; + unsigned int xoff, yoff; + + /* + * Note: the offset does not appear to be influenced by + * hardware scrolling. + */ + xoff = yoff = 0; + + xoff += wi->dst.x; + yoff += wi->dst.y; + + xscale = (wi->src.width * 4096) / wi->dst.width; + yscale = (wi->src.height * 4096) / wi->dst.height; + + cyberpro_grphw8(REG_BANK, REG_BANK_X, dp); + cyberpro_grphw16(X_V2_X_START, xoff, dp); + cyberpro_grphw16(X_V2_X_END, xoff + wi->dst.width, dp); + cyberpro_grphw16(X_V2_Y_START, yoff, dp); + cyberpro_grphw16(X_V2_Y_END, yoff + wi->dst.height, dp); + + cyberpro_grphw8(REG_BANK, REG_BANK_Y, dp); + cyberpro_grphw16(Y_V2_DDA_X_INC, xscale, dp); + cyberpro_grphw16(Y_V2_DDA_Y_INC, yscale, dp); +} + +/* + * Enable or disable the 2nd overlay window. Note that for anything + * useful to be displayed, we must have capture enabled. + */ +static void +cyberpro_v2_ctl(struct cyberpro_vidinfo *dp, struct win_info *wi, int on) +{ + if (on) + wi->vid_disp_ctl1 |= EXT_VID_DISP_CTL1_ENABLE_WINDOW; + else + wi->vid_disp_ctl1 &= ~EXT_VID_DISP_CTL1_ENABLE_WINDOW; + + cyberpro_grphw8(REG_BANK, REG_BANK_Y, dp); + cyberpro_grphw8(Y_V2_VID_DISP_CTL1, wi->vid_disp_ctl1, dp); +} + +/*--------------------------- X2 Overlay Window ----------------------------- + * Initialise 3rd overlay window (guesswork) + */ +static void +cyberpro_x2_init(struct cyberpro_vidinfo *dp, struct win_info *wi) +{ + wi->vid_fmt = EXT_VID_FMT_YUV422; + wi->vid_disp_ctl1 = 0x40; + wi->vid_misc_ctl1 = 0; + + cyberpro_grphw8(REG_BANK, REG_BANK_K, dp); + cyberpro_grphw8 (K_X2_VID_DISP_CTL1, wi->vid_disp_ctl1, dp); + cyberpro_grphw16(K_X2_DDA_X_INIT, 0x0800, dp); + cyberpro_grphw16(K_X2_DDA_Y_INIT, 0x0800, dp); +} + +/* + * Set the source parameters for the x2 window + */ +static void +cyberpro_x2_set_src(struct cyberpro_vidinfo *dp, struct win_info *wi) +{ + unsigned int phase, pitch; + + pitch = (wi->src.width >> 2) & 0x0fff; + phase = (wi->src.width + 3) >> 2; + + wi->vid_fmt &= ~7; + switch (wi->src.format) { + case VIDEO_PALETTE_RGB565: wi->vid_fmt |= EXT_VID_FMT_RGB565; break; + case VIDEO_PALETTE_RGB24: wi->vid_fmt |= EXT_VID_FMT_RGB888_24; break; + case VIDEO_PALETTE_RGB32: wi->vid_fmt |= EXT_VID_FMT_RGB888_32; break; + case VIDEO_PALETTE_RGB555: wi->vid_fmt |= EXT_VID_FMT_RGB555; break; + case VIDEO_PALETTE_YUV422: wi->vid_fmt |= EXT_VID_FMT_YUV422; break; + } + + cyberpro_grphw8(REG_BANK, REG_BANK_J, dp); + cyberpro_grphw24(J_X2_VID_MEM_START, wi->src.offset, dp); + cyberpro_grphw16(J_X2_VID_SRC_WIDTH, pitch | ((phase << 4) & 0xf000), dp); + cyberpro_grphw8 (J_X2_VID_SRC_WIN_WIDTH, phase, dp); + + cyberpro_grphw8(REG_BANK, REG_BANK_K, dp); + cyberpro_grphw8(K_X2_VID_FMT, wi->vid_fmt, dp); +} + +/* + * Set x2 window + */ +static void +cyberpro_x2_set_win(struct cyberpro_vidinfo *dp, struct win_info *wi) +{ + unsigned int xscale, yscale; + unsigned int xoff, yoff; + + /* + * Note: the offset does not appear to be influenced by + * hardware scrolling. + */ + xoff = yoff = 0; + + xoff += wi->dst.x; + yoff += wi->dst.y; + + xscale = (wi->src.width * 4096) / wi->dst.width; + yscale = (wi->src.height * 4096) / wi->dst.height; + + cyberpro_grphw8(REG_BANK, REG_BANK_J, dp); + cyberpro_grphw16(J_X2_X_START, xoff, dp); + cyberpro_grphw16(J_X2_X_END, xoff + wi->dst.width, dp); + cyberpro_grphw16(J_X2_Y_START, yoff, dp); + cyberpro_grphw16(J_X2_Y_END, yoff + wi->dst.height, dp); + + cyberpro_grphw8(REG_BANK, REG_BANK_K, dp); + cyberpro_grphw16(K_X2_DDA_X_INC, xscale, dp); + cyberpro_grphw16(K_X2_DDA_Y_INC, yscale, dp); +} + +/* + * Enable or disable the 3rd overlay window. Note that for anything + * useful to be displayed, we must have capture enabled. + */ +static void +cyberpro_x2_ctl(struct cyberpro_vidinfo *dp, struct win_info *wi, int on) +{ + if (on) + wi->vid_disp_ctl1 |= EXT_VID_DISP_CTL1_ENABLE_WINDOW; + else + wi->vid_disp_ctl1 &= ~EXT_VID_DISP_CTL1_ENABLE_WINDOW; + + cyberpro_grphw8(REG_BANK, REG_BANK_K, dp); + cyberpro_grphw8(K_X2_VID_DISP_CTL1, wi->vid_disp_ctl1, dp); +} + +/* ------------------------------------------------------------------------- */ + +#if 0 +static void reset_seq(struct cyberpro_vidinfo *dp) +{ + unsigned char ext_mem_ctl = cyberpro_grphr8(0x70, dp); + + cyberpro_grphw8(ext_mem_ctl | 0x80, 0x70, dp); + cyberpro_grphw8(ext_mem_ctl, 0x70, dp); +} +#endif + +#ifdef USE_MMAP +/* + * Buffer support + */ +static int +cyberpro_alloc_frame_buffer(struct cyberpro_vidinfo *dp, + struct framebuf *frame) +{ + unsigned long addr; + void *buffer; + int pgidx; + + if (frame->buffer) + return 0; + + /* + * Allocate frame buffer + */ + buffer = vmalloc(NR_PAGES * PAGE_SIZE); + + if (frame->buffer) { + vfree(buffer); + return 0; + } + + if (!buffer) + return -ENOMEM; + + printk("Buffer allocated @ %p [", buffer); + + frame->buffer = buffer; + frame->dbg = 1; + + /* + * Don't leak information from the kernel. + */ + memset(buffer, 0x5a, NR_PAGES * PAGE_SIZE); + + /* + * Now, reserve all the pages, and calculate + * each pages' bus address. + */ + addr = (unsigned long)buffer; + for (pgidx = 0; pgidx < NR_PAGES; pgidx++, addr += PAGE_SIZE) { + struct page *page; + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; + + /* + * The page should be present. If not, + * vmalloc has gone nuts. + */ + pgd = pgd_offset_k(addr); + if (pgd_none(*pgd)) + BUG(); + pmd = pmd_offset(pgd, addr); + if (pmd_none(*pmd)) + BUG(); + pte = pte_offset(pmd, addr); + if (!pte_present(*pte)) + BUG(); + + page = pte_page(*pte); + + frame->bus_addr[pgidx] = virt_to_bus((void *)page_address(page)); + frame->pages[pgidx] = page; + SetPageReserved(page); + + printk("%08lx (%08lx) ", page_address(page), frame->bus_addr[pgidx]); + } + printk("\n"); + + return 0; +} + +static void +cyberpro_frames_free_one(struct cyberpro_vidinfo *dp, struct framebuf *frame) +{ + void *buffer; + int pgidx; + + frame->status = FRAME_FREE; + buffer = frame->buffer; + frame->buffer = NULL; + + if (buffer) { + for (pgidx = 0; pgidx < NR_PAGES; pgidx++) { + frame->bus_addr[pgidx] = 0; + ClearPageReserved(frame->pages[pgidx]); + frame->pages[pgidx] = NULL; + } + vfree(buffer); + } +} + +static void +cyberpro_busmaster_frame(struct cyberpro_vidinfo *dp, struct framebuf *frame) +{ + unsigned long bus_addr; + + bus_addr = frame->bus_addr[dp->bm_index]; + + if (frame->dbg) { + printk("Frame%d: %06x -> %08lx\n", + dp->frame_idx, + dp->bm_offset, + bus_addr); + } + + cyber2000_outw(dp->bm_offset, BM_VID_ADDR_LOW); + cyber2000_outw(dp->bm_offset >> 16, BM_VID_ADDR_HIGH); + + cyber2000_outw(bus_addr, BM_ADDRESS_LOW); + cyber2000_outw(bus_addr >> 16, BM_ADDRESS_HIGH); + + /* + * One page-full only + */ + cyber2000_outw(1023, BM_LENGTH); + + /* + * Load length + */ + cyber2000_outw(BM_CONTROL_INIT, BM_CONTROL); + + /* + * Enable transfer + */ + cyber2000_outw(BM_CONTROL_ENABLE|BM_CONTROL_IRQEN, BM_CONTROL); + + dp->bm_offset += 1024; + dp->bm_index += 1; +} + +static void cyberpro_busmaster_interrupt(struct cyberpro_vidinfo *dp) +{ + struct framebuf *frame = dp->frame + dp->frame_idx; + + /* + * Disable Busmaster operations + */ + cyber2000_outw(0, BM_CONTROL); + + if (frame->status == FRAME_GRABBING) { + /* + * We are still grabbing this frame to system + * memory. Transfer next page if there are + * more, or else flag this frame as complete. + */ + if (dp->bm_index < NR_PAGES) + cyberpro_busmaster_frame(dp); + else { + unsigned int idx; + + frame->status = FRAME_DONE; + frame->dbg = 0; + + idx = dp->frame_idx + 1; + if (idx >= NR_FRAMES) + idx = 0; + + dp->frame_idx = idx; + + wake_up(&dp->frame_wait); + } + } +} + +static void cyberpro_frames_vbl(struct cyberpro_vidinfo *dp, unsigned int stat) +{ + struct framebuf *frame = dp->frame + dp->frame_idx; + + /* + * No point capturing frames if the grabber isn't active. + */ + if (stat & EXT_ROM_UCB4GH_FREEZE) + return; + + /* + * If the next buffer is ready for grabbing, + * set up the bus master registers for the + * transfer. + */ + if (frame->status == FRAME_WAITING) { + frame->status = FRAME_GRABBING; + + dp->bm_offset = dp->cap_mem_offset; + dp->bm_index = 0; + + cyberpro_busmaster_frame(dp, frame); + } +} + +static void __init cyberpro_frames_init(struct cyberpro_vidinfo *dp) +{ + unsigned int offset, maxsize; + int i; + + init_waitqueue_head(&dp->frame_wait); + + maxsize = 2 * dp->cap.maxwidth * dp->cap.maxheight; + dp->frame_size = PAGE_ALIGN(maxsize); + dp->frame_idx = 0; + + for (i = offset = 0; i < NR_FRAMES; i++) { + dp->frame[i].offset = offset; + dp->frame[i].status = FRAME_FREE; + offset += dp->frame_size; + } +} + +static void cyberpro_frames_free(struct cyberpro_vidinfo *dp) +{ + int i; + + dp->mmaped = 0; + + /* + * Free all frame buffers + */ + for (i = 0; i < NR_FRAMES; i++) + cyberpro_frames_free_one(dp, dp->frame + i); +} + +#else +#define cyberpro_frames_vbl(dp,stat) do { } while (0) +#define cyberpro_frames_init(dp) do { } while (0) +#define cyberpro_frames_free(dp) do { } while (0) +#endif + +/* + * CyberPro Interrupts + * ------------------- + * + * We don't really know how to signal an IRQ clear to the chip. However, + * disabling and re-enabling the capture interrupt enable seems to do what + * we want. + */ +static void cyberpro_interrupt(int nr, void *dev_id, struct pt_regs *regs) +{ + struct cyberpro_vidinfo *dp = dev_id; + unsigned char old_grphidx; + unsigned int status; + + /* + * Save old graphics index register + */ + old_grphidx = cyberpro_readb(0x3ce, dp); + + status = cyberpro_grphr8(EXT_ROM_UCB4GH, dp); + + /* + * Was it due to the Capture VSYNC? + */ + if (status & EXT_ROM_UCB4GH_INTSTAT) { + /* + * Frob the IRQ enable bit to drop the request. + */ + cyberpro_grphw8(VFAC_CTL3, dp->vfac3 & ~VFAC_CTL3_CAP_IRQ, dp); + cyberpro_grphw8(VFAC_CTL3, dp->vfac3, dp); + + cyberpro_frames_vbl(dp, status); + wake_up(&dp->vbl_wait); + } + + /* + * Restore graphics controller index + */ + cyberpro_writeb(old_grphidx, 0x3ce, dp); + +#ifdef USE_MMAP + /* + * Do Bus-Master IRQ stuff + */ + if (cyber2000_inb(BM_CONTROL) & (1 << 7)) + cyberpro_busmaster_interrupt(dp); +#endif +} + +static void cyberpro_capture(struct cyberpro_vidinfo *dp, int on) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned int status; + + status = cyberpro_grphr8(EXT_ROM_UCB4GH, dp); + + add_wait_queue(&dp->vbl_wait, &wait); + set_current_state(TASK_UNINTERRUPTIBLE); + + if (!!on ^ !(status & EXT_ROM_UCB4GH_FREEZE)) { + if (on) { + schedule_timeout(40 * HZ / 1000); + dp->vfac1 &= ~(VFAC_CTL1_FREEZE_CAPTURE|VFAC_CTL1_FREEZE_CAPTURE_SYNC); + cyberpro_grphw8(VFAC_CTL1, dp->vfac1, dp); + + status = cyberpro_grphr8(EXT_ROM_UCB4GH, dp); + } else { + dp->vfac1 |= VFAC_CTL1_FREEZE_CAPTURE_SYNC; + cyberpro_grphw8(VFAC_CTL1, dp->vfac1, dp); + + status = cyberpro_grphr8(EXT_ROM_UCB4GH, dp); + if (!(status & EXT_ROM_UCB4GH_FREEZE)) + schedule_timeout(40 * HZ / 1000); + } + } + + current->state = TASK_RUNNING; + remove_wait_queue(&dp->vbl_wait, &wait); +} + +static void cyberpro_capture_set_win(struct cyberpro_vidinfo *dp) +{ + unsigned int xstart, xend, ystart, yend; + + xstart = 4 + dp->capt.x; + xend = xstart + dp->capt.width; + + if (dp->cap_mode1 & EXT_CAP_MODE1_8BIT) { + /* 8-bit capture */ + xstart *= 2; + xend *= 2; + } + + xstart -= 1; + xend -= 1; + + ystart = 18 + dp->capt.y; + yend = ystart + dp->capt.height / 2; + + cyberpro_grphw16(CAP_X_START, xstart, dp); + cyberpro_grphw16(CAP_X_END, xend + 1, dp); + cyberpro_grphw16(CAP_Y_START, ystart, dp); + cyberpro_grphw16(CAP_Y_END, yend + 2, dp); + + /* + * This should take account of capt.decimation + */ + cyberpro_grphw16(CAP_DDA_X_INIT, 0x0800, dp); + cyberpro_grphw16(CAP_DDA_X_INC, 0x1000, dp); + cyberpro_grphw16(CAP_DDA_Y_INIT, 0x0800, dp); + cyberpro_grphw16(CAP_DDA_Y_INC, 0x1000, dp); + + cyberpro_grphw8(CAP_PITCH, dp->capt.width >> 2, dp); +} + +static void cyberpro_set_interlace(struct cyberpro_vidinfo *dp) +{ + /* + * set interlace mode + */ + if (dp->interlace) { + dp->vfac3 |= VFAC_CTL3_CAP_INTERLACE; + dp->cap_miscctl &= ~CAP_CTL_MISC_ODDEVEN; + dp->ovl->src.height = dp->capt.height; + } else { + dp->vfac3 &= ~VFAC_CTL3_CAP_INTERLACE; + dp->cap_miscctl |= CAP_CTL_MISC_ODDEVEN; + dp->ovl->src.height = dp->capt.height / 2; + } + + cyberpro_grphw8(VFAC_CTL3, dp->vfac3, dp); + cyberpro_grphw8(CAP_CTL_MISC, dp->cap_miscctl, dp); + + dp->ovl->set_src(dp, dp->ovl); + + if (dp->win_set) + dp->ovl->set_win(dp, dp->ovl); +} + +/* + * Calculate and set the address of the capture buffer. Note we + * also update the extended memory buffer for the overlay window. + * + * base: phys base address of display + * width: pixel width of display + * height: height of display + * depth: depth of display (8/16/24) + * bytesperline: number of bytes on a line + * + * We place the capture buffer 16K after the screen. + */ +static int +cyberpro_set_buffer(struct cyberpro_vidinfo *dp, struct video_buffer *b) +{ + unsigned long screensize, maxbufsz; + + if (b->height <= 0 || b->width <= 0 || b->bytesperline <= 0) + return -EINVAL; + + maxbufsz = dp->cap.maxwidth * dp->cap.maxheight * 2; + screensize = b->height * b->bytesperline + 16384; + + if ((screensize + maxbufsz) >= dp->info.fb_size) + return -EINVAL; + + dp->buf.base = b->base; + dp->buf.width = b->width; + dp->buf.height = b->height; + dp->buf.depth = b->depth; + dp->buf.bytesperline = b->bytesperline; + dp->cap_mem_offset = screensize >> 2; + + cyberpro_grphw24(CAP_MEM_START, dp->cap_mem_offset, dp); + + /* + * Setup the overlay source information. + */ + dp->ovl->src.offset = dp->cap_mem_offset; + dp->ovl->set_src(dp, dp->ovl); + + return 0; +} + +static void cyberpro_hw_init(struct cyberpro_vidinfo *dp) +{ + unsigned char old; + + /* + * Enable access to bus-master registers + */ + dp->info.enable_extregs(dp->info.info); + + dp->vfac1 = VFAC_CTL1_PHILIPS | + VFAC_CTL1_FREEZE_CAPTURE | + VFAC_CTL1_FREEZE_CAPTURE_SYNC; + dp->vfac3 = VFAC_CTL3_CAP_IRQ; + + dp->cap_miscctl = CAP_CTL_MISC_DISPUSED | + CAP_CTL_MISC_SYNCTZOR | + CAP_CTL_MISC_SYNCTZHIGH; + + /* + * Setup bus-master mode + */ + cyberpro_grphw8(BM_CTRL1, 0x88, dp); + cyberpro_grphw8(PCI_BM_CTL, PCI_BM_CTL_ENABLE, dp); + cyberpro_grphw8(BM_CTRL0, 0x44, dp); + cyberpro_grphw8(BM_CTRL1, 0x84, dp); + + cyberpro_grphw24(CAP_MEM_START, 0, dp); + + cyberpro_grphw8(VFAC_CTL1, dp->vfac1, dp); + cyberpro_grphw8(VFAC_CTL3, dp->vfac3, dp); + cyberpro_grphw8(VFAC_CTL2, 0, dp); + + cyberpro_grphw8(EXT_CAP_CTL1, 0x27, dp); /* disable PIP */ + cyberpro_grphw8(EXT_CAP_CTL2, 0xc0 | EXT_CAP_CTL2_ODDFRAMEIRQ, dp); + + /* + * Configure capture mode to match the + * external video processor format + */ + cyberpro_grphw8(EXT_CAP_MODE1, dp->cap_mode1, dp); + cyberpro_grphw8(EXT_CAP_MODE2, dp->cap_mode2, dp); + + /* setup overlay */ +// cyberpro_grphw16(EXT_FIFO_CTL, 0x1010, dp); + cyberpro_grphw16(EXT_FIFO_CTL, 0x1b0f, dp); + + /* + * Always reset the capture parameters on each open. + */ + dp->capt.x = 0; + dp->capt.y = 0; + dp->capt.width = dp->cap.maxwidth; + dp->capt.height = dp->cap.maxheight; + dp->capt.decimation = 0; + dp->capt.flags = 0; + + cyberpro_capture_set_win(dp); + + /* + * Enable VAFC + */ + old = cyberpro_grphr8(EXT_LATCH1, dp); + cyberpro_grphw8(EXT_LATCH1, old | EXT_LATCH1_VAFC_EN, dp); + + /* + * Enable capture (we hope that VSYNC=1) + */ + dp->vfac1 |= VFAC_CTL1_CAPTURE; + cyberpro_grphw8(VFAC_CTL1, dp->vfac1, dp); + + /* + * The overlay source format is always the + * same as the capture stream format. + */ + dp->ovl->src.width = dp->capt.width; + dp->ovl->src.height = dp->capt.height; + dp->ovl->src.format = dp->stream_fmt; + + /* + * Initialise the overlay windows + */ + dp->ext.init(dp, &dp->ext); + dp->v2.init(dp, &dp->v2); + dp->x2.init(dp, &dp->x2); +} + +static void cyberpro_deinit(struct cyberpro_vidinfo *dp) +{ + unsigned char old; + + /* + * Stop any bus-master activity + */ + cyberpro_writew(0, BM_CONTROL, dp); + + /* + * Shut down overlay + */ + if (dp->ovl_active) + dp->ovl->ctl(dp, dp->ovl, 0); + dp->ovl_active = 0; + + /* + * Shut down capture + */ + if (dp->cap_active) + cyberpro_capture(dp, 0); + dp->cap_active = 0; + + /* + * Disable all capture + */ + cyberpro_grphw8(VFAC_CTL1, 0, dp); + + /* + * Disable VAFC + */ + old = cyberpro_grphr8(EXT_LATCH1, dp); + cyberpro_grphw8(EXT_LATCH1, old & ~EXT_LATCH1_VAFC_EN, dp); + + /* + * Disable interrupt (this allows it to float) + */ + dp->vfac3 &= ~VFAC_CTL3_CAP_IRQ; + cyberpro_grphw8(VFAC_CTL3, dp->vfac3, dp); + + /* + * Switch off bus-master mode + */ + cyberpro_grphw8(PCI_BM_CTL, 0, dp); + + /* + * Disable access to bus-master registers + */ + dp->info.disable_extregs(dp->info.info); +} + +static int cyberpro_grabber_open(struct inode *ino, struct file *filp) +{ + struct video_device *dev = video_devdata(filp); + struct cyberpro_vidinfo *dp = dev->priv; + int ret, one = 1; + + ret = video_exclusive_open(ino, filp); + if (ret) + return ret; + + ret = request_irq(dp->irq, cyberpro_interrupt, SA_SHIRQ, + dp->info.dev_name, dp); + + if (ret) { + dp->users -= 1; + goto out; + } + + /* + * Initialise the VGA chip + */ + cyberpro_hw_init(dp); + + /* + * Enable the IRQ. This allows the IRQ to work as expected + * even if the IRQ line is missing the pull-up resistor. + */ + enable_irq(dp->irq); + + i2c_control_device(&dp->bus, I2C_DRIVERID_VIDEODECODER, + DECODER_ENABLE_OUTPUT, &one); + +out: + if (ret) + video_exclusive_release(ino, filp); + return ret; +} + +static int cyberpro_grabber_release(struct inode *ino, struct file *filp) +{ + struct video_device *dev = video_devdata(filp); + struct cyberpro_vidinfo *dp = dev->priv; + int zero = 0; + + /* + * Disable the IRQ. This prevents problems with missing + * pull-up resistors on the PCI interrupt line. + */ + disable_irq(dp->irq); + + cyberpro_frames_free(dp); + + /* + * Turn off the SAA7111 decoder + */ + i2c_control_device(&dp->bus, I2C_DRIVERID_VIDEODECODER, + DECODER_ENABLE_OUTPUT, &zero); + + /* + * Disable grabber + */ + cyberpro_deinit(dp); + + free_irq(dp->irq, dp); + + video_exclusive_release(ino, filp); + return 0; +} + +/* + * Our general plan here is: + * 1. Set the CyberPro to perform a BM-DMA of one frame to this memory + * 2. Copy the frame to the userspace + * + * However, BM-DMA seems to be unreliable at the moment, especially on + * rev. 4 NetWinders. + */ +static ssize_t +cyberpro_grabber_read(struct file *filp, char *buf, size_t count, loff_t *pos) +{ + struct video_device *dev = video_devdata(filp); + struct cyberpro_vidinfo *dp = dev->priv; + int ret = -EINVAL; + +#ifdef USE_MMIO + unsigned long maxbufsz = dp->capt.width * dp->capt.height * 2; + char *disp = dp->info.fb + (dp->cap_mem_offset << 2); + + /* + * If the buffer is mmap()'d, we shouldn't be using read() + */ + if (dp->mmaped) + return -EINVAL; + + if (count > maxbufsz) + count = maxbufsz; + + if (dp->cap_active) + cyberpro_capture(dp, 0); + + ret = (int)count; + if (copy_to_user(buf, disp, count)) + ret = -EFAULT; + + /* + * unfreeze capture + */ + if (dp->cap_active) + cyberpro_capture(dp, 1); +#endif + + return ret; +} + +/* + * We don't support writing to the grabber + * (In theory, we could allow writing to a separate region of VGA memory, + * and display this using the second overlay window. This would allow us + * to do video conferencing for example). + */ +static ssize_t +cyberpro_grabber_write(struct file *filp, const char *buf, size_t count, loff_t *pos) +{ + return -EINVAL; +} + +static int +__cyberpro_grabber_ioctl(struct inode *ino, struct file *filp, unsigned int cmd, void *arg) +{ + struct video_device *dev = video_devdata(filp); + struct cyberpro_vidinfo *dp = dev->priv; + + switch (cmd) { + case VIDIOCGCAP: + memcpy(arg, &dp->cap, sizeof(dp->cap)); + return 0; + + case VIDIOCGCHAN: + { + struct video_channel *chan = arg; + + memset(chan, 0, sizeof(*chan)); + + chan->channel = 0; + strcpy(chan->name, "Composite"); + chan->tuners = 0; + chan->flags = 0; + chan->type = VIDEO_TYPE_CAMERA; + chan->norm = dp->norm; + + return 0; + } + + case VIDIOCGPICT: + memcpy(arg, &dp->pic, sizeof(dp->pic)); + return 0; + + case VIDIOCGWIN: + { + struct video_window *win = arg; + + memset(win, 0, sizeof(*win)); + + win->x = dp->ovl->dst.x; + win->y = dp->ovl->dst.y; + win->width = dp->ovl->dst.width; + win->height = dp->ovl->dst.height; + win->chromakey = dp->ovl->dst.chromakey; + win->flags = VIDEO_WINDOW_CHROMAKEY | + (dp->interlace ? VIDEO_WINDOW_INTERLACE : 0); + win->clips = NULL; + win->clipcount = 0; + + return 0; + } + + case VIDIOCGFBUF: + memcpy(arg, &dp->buf, sizeof(dp->buf)); + return 0; + + case VIDIOCGUNIT: + { + struct video_unit *unit = arg; + + memset(unit, 0, sizeof(*unit)); + + unit->video = dev->minor; + unit->vbi = VIDEO_NO_UNIT; + unit->radio = VIDEO_NO_UNIT; + unit->audio = VIDEO_NO_UNIT; + unit->teletext = VIDEO_NO_UNIT; + + return 0; + } + + case VIDIOCGCAPTURE: + memcpy(arg, &dp->capt, sizeof(dp->capt)); + return 0; + + case VIDIOCSCHAN: + { + struct video_decoder_capability vdc; + struct video_channel *v = arg; + int ok; + + if (v->channel != 0) + return -EINVAL; + + i2c_control_device(&dp->bus, I2C_DRIVERID_VIDEODECODER, + DECODER_GET_CAPABILITIES, &vdc); + + switch (v->norm) { + case VIDEO_MODE_PAL: + ok = vdc.flags & VIDEO_DECODER_PAL; + break; + case VIDEO_MODE_NTSC: + ok = vdc.flags & VIDEO_DECODER_NTSC; + break; + case VIDEO_MODE_AUTO: + ok = vdc.flags & VIDEO_DECODER_AUTO; + break; + default: + ok = 0; + } + if (!ok) + return -EINVAL; + + dp->norm = v->norm; + + i2c_control_device(&dp->bus, I2C_DRIVERID_VIDEODECODER, + DECODER_SET_NORM, &v->norm); + + return 0; + } + + case VIDIOCSPICT: + { + struct video_picture *p = arg; + + if (p->palette != dp->stream_fmt || p->depth != 8) + return -EINVAL; + + dp->pic = *p; + + /* p.depth sets the capture depth */ + /* p.palette sets the capture palette */ + + i2c_control_device(&dp->bus, I2C_DRIVERID_VIDEODECODER, + DECODER_SET_PICTURE, p); + + return 0; + } + + case VIDIOCSWIN: /* set the size & position of the overlay window */ + { + struct video_window *w = arg; + int diff; + + if (!dp->buf_set) + return -EINVAL; + + if (w->clipcount) + return -EINVAL; + + /* + * Bound the overlay window by the size of the screen + */ + if (w->x < 0) + w->x = 0; + if (w->y < 0) + w->y = 0; + + if (w->x > dp->buf.width) + w->x = dp->buf.width; + if (w->y > dp->buf.height) + w->y = dp->buf.height; + + if (w->width < dp->capt.width) + w->width = dp->capt.width; + if (w->height < dp->capt.height) + w->height = dp->capt.height; + + if (w->x + w->width > dp->buf.width) + w->width = dp->buf.width - w->x; + if (w->y + w->height > dp->buf.height) + w->height = dp->buf.height - w->y; + + /* + * We've tried to make the values fit, but + * they just won't. + */ + if (w->width < dp->capt.width || w->height < dp->capt.height) + return -EINVAL; + + diff = dp->ovl->dst.x != w->x || + dp->ovl->dst.y != w->y || + dp->ovl->dst.width != w->width || + dp->ovl->dst.height != w->height || + dp->ovl->dst.chromakey != w->chromakey || + dp->ovl->dst.flags != w->flags;; + + if (!dp->win_set || diff) { + dp->ovl->dst.x = w->x; + dp->ovl->dst.y = w->y; + dp->ovl->dst.width = w->width; + dp->ovl->dst.height = w->height; + dp->ovl->dst.chromakey = w->chromakey; + dp->ovl->dst.flags = w->flags; + + if (dp->ovl_active) + dp->ovl->ctl(dp, dp->ovl, 0); + + dp->ovl->set_win(dp, dp->ovl); + + if (dp->ovl_active) + dp->ovl->ctl(dp, dp->ovl, 1); + + diff = w->flags & VIDEO_WINDOW_INTERLACE ? 1 : 0; + if (!dp->win_set || dp->interlace != diff) { + dp->interlace = diff; + cyberpro_set_interlace(dp); + } + } + + dp->win_set = 1; + + return 0; + } + + case VIDIOCSFBUF: /* set frame buffer info */ + { + struct video_buffer *b = arg; + int ret; + + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) + return -EPERM; + + if (dp->cap_active) + return -EINVAL; + + ret = cyberpro_set_buffer(dp, b); + if (ret == 0) { + dp->buf_set = 1; + dp->win_set = 0; + } + + return ret; + } + + case VIDIOCCAPTURE: + { + int on = *(int *)arg; + + if (( on && dp->ovl_active) || + (!on && !dp->ovl_active)) + return 0; + + if (on && (!dp->buf_set || !dp->win_set)) + return -EINVAL; + + cyberpro_capture(dp, on); + dp->cap_active = on; + dp->ovl->ctl(dp, dp->ovl, on); + dp->ovl_active = on; + + return 0; + } + +#ifdef USE_MMAP + case VIDIOCSYNC: + { + DECLARE_WAITQUEUE(wait, current); + int buf = *(int *)arg; + + /* + * The buffer must have been mmaped + * for this call to work. + */ + if (!dp->mmaped) + return -EINVAL; + + if (buf < 0 || buf >= NR_FRAMES) + return -EINVAL; + + switch (dp->frame[buf].status) { + case FRAME_FREE: + return -EINVAL; + + case FRAME_WAITING: + case FRAME_GRABBING: + add_wait_queue(&dp->frame_wait, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) + break; + if (dp->frame[buf].status == FRAME_DONE) + break; + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&dp->frame_wait, &wait); + if (signal_pending(current)) + return -EINTR; + /*FALLTHROUGH*/ + case DONE: + dp->frame[buf].status = FRAME_FREE; + break; + } + return 0; + } + + case VIDIOCMCAPTURE: + { + struct video_mmap *vmap = arg; + + /* + * The buffer must have been mmaped + * for this call to work. + */ + if (!dp->mmaped) + return -EINVAL; + + /* + * We can only capture in our source format/size. + */ + if (vmap->frame >= NR_FRAMES || + vmap->format != dp->stream_fmt || + vmap->width != dp->capt.width || + vmap->height != dp->capt.height) + return -EINVAL; + + if (dp->frame[vmap->frame].status == FRAME_WAITING || + dp->frame[vmap->frame].status == FRAME_GRABBING) + return -EBUSY; + + dp->frame[vmap->frame].status = FRAME_WAITING; + return 0; + } + + case VIDIOCGMBUF: + { + struct video_mbuf *vmb = arg; + unsigned int frame_size, frame_offset, i; + + memset(vmb, 0, sizeof(*vmb)); + + vmb->frames = NR_FRAMES; + vmb->size = dp->frame_size * vmb.frames; + + for (i = 0; i < NR_FRAMES; i++) + vmb->offsets[i] = dp->frame[i].offset; + + return 0; + } +#endif + + case VIDIOCSCAPTURE: + { + struct video_capture *capt = arg; + +#ifndef ALLOW_SCAPTURE_WHILE_CAP + if (dp->cap_active) + return -EINVAL; +#endif + + if (capt->x < 0 || capt->width < 0 || + capt->y < 0 || capt->height < 0 || + capt->x + capt->width > dp->cap.maxwidth || + capt->y + capt->height > dp->cap.maxheight) + return -EINVAL; + + /* + * The capture width must be a multiple of 4 + */ + if (dp->capt.width & 3) + return -EINVAL; + + dp->capt.x = capt->x; + dp->capt.y = capt->y; + dp->capt.width = capt->width; + dp->capt.height = capt->height; +#ifdef ALLOW_SCAPTURE_WHILE_CAP + if (dp->ovl_active) + dp->ovl->ctl(dp, dp->ovl, 0); + if (dp->cap_active) + cyberpro_capture(dp, 0); +#endif + cyberpro_capture_set_win(dp); + + /* + * Update the overlay window information + */ + dp->ovl->src.width = capt->width; + dp->ovl->src.height = capt->height; + + dp->ovl->set_src(dp, dp->ovl); + if (dp->win_set) + dp->ovl->set_win(dp, dp->ovl); + +#ifdef ALLOW_SCAPTURE_WHILE_CAP + if (dp->cap_active) + cyberpro_capture(dp, 1); + if (dp->ovl_active) + dp->ovl->ctl(dp, dp->ovl, 1); +#endif + return 0; + } + + case VIDIOCGTUNER: /* no tuner */ + case VIDIOCSTUNER: + return -EINVAL; + } + + return -EINVAL; +} + +#ifdef USE_MMAP +static int cyberpro_grabber_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct video_device *dev = video_devdata(filp); + struct cyberpro_vidinfo *dp = dev->priv; + unsigned long vaddr = vma->vm_start; + unsigned long size = vma->vm_end - vma->vm_start; + pgprot_t prot; + int frame_idx, ret = -EINVAL; + +#if defined(__arm__) + prot = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_USER | L_PTE_WRITE | L_PTE_DIRTY); +#elif defined(__i386__) + prot = __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED); + if (boot_cpu_data.x86 > 3) + pgprot_val(prot) |= _PAGE_PCD; +#else +#error "Unsupported architecture" +#endif + + /* + * The mmap() request must have the correct size. + */ + if (size != NR_FRAMES * dp->frame_size) + goto out; + + /* + * If it's already mapped, don't re-do + */ + if (dp->mmaped) + goto out; + dp->mmaped = 1; + + /* + * Map in each frame + */ + for (frame_idx = 0; frame_idx < NR_FRAMES; frame_idx++) { + struct framebuf *frame; + int pgidx; + + frame = dp->frame + frame_idx; + + ret = cyberpro_alloc_frame_buffer(dp, frame); + + /* + * If an error occurs, we can be lazy and leave what we've + * been able to do. Our release function will free any + * allocated buffers, and do_mmap_pgoff() will zap any + * inserted mappings. + */ + if (ret) + goto out2; + + /* + * Map in each page on a page by page basis. This is just + * a little on the inefficient side, but it's only run once. + */ + for (pgidx = 0; pgidx < NR_PAGES; pgidx++) { + unsigned long virt; + + virt = page_address(frame->pages[pgidx]); + + ret = remap_page_range(vaddr, virt_to_phys((void *)virt), + PAGE_SIZE, prot); + + if (ret) + goto out2; + + vaddr += PAGE_SIZE; + } + } + + out2: + if (ret) + dp->mmaped = 0; + out: + return ret; +} +#endif + +static int +cyberpro_grabber_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, filp, cmd, arg, __cyberpro_grabber_ioctl); +} + +static struct file_operations cyberpro_fops = { + open: cyberpro_grabber_open, + release: cyberpro_grabber_release, + read: cyberpro_grabber_read, + write: cyberpro_grabber_write, + ioctl: cyberpro_grabber_ioctl, +#ifdef USE_MMAP + mmap: cyberpro_grabber_mmap, +#endif +}; + +static struct video_device cyberpro_grabber = { + owner: THIS_MODULE, + type: VID_TYPE_CAPTURE | VID_TYPE_OVERLAY | + VID_TYPE_CHROMAKEY | VID_TYPE_SCALES | + VID_TYPE_SUBCAPTURE, + hardware: 0, + fops: &cyberpro_fops, +}; + +static struct cyberpro_vidinfo *dev; + +int __init init_cyber2000fb_viddev(void) +{ + struct cyberpro_vidinfo *dp; + int ret; + + dp = kmalloc(sizeof(*dp), GFP_KERNEL); + if (!dp) + return -ENOMEM; + + memset(dp, 0, sizeof(*dp)); + dev = dp; + + if (!cyber2000fb_attach(&dp->info, 0)) { + kfree(dp); + return -ENXIO; + } + + dp->dev = cyberpro_grabber; + strncpy(dp->dev.name, dp->info.dev_name, sizeof(dp->dev.name)); + dp->dev.priv = dp; + dp->bus = cyberpro_i2c_bus; + dp->regs = dp->info.regs; + dp->irq = dp->info.dev->irq; + + strncpy(dp->cap.name, dp->info.dev_name, sizeof(dp->cap.name)); + dp->cap.type = dp->dev.type; + dp->cap.channels = 1; + dp->cap.audios = 0; + dp->cap.minwidth = 32; + dp->cap.maxwidth = 716; + dp->cap.minheight = 32; + dp->cap.maxheight = 576; + + dp->pic.brightness = 32768; + dp->pic.hue = 32768; + dp->pic.colour = 32768; + dp->pic.contrast = 32768; + dp->pic.whiteness = 0; + dp->pic.depth = 8; + dp->pic.palette = VIDEO_PALETTE_YUV422; + + /* dp->buf is setup by the user */ + /* dp->cap_mem_offset setup by dp->buf */ + + dp->norm = VIDEO_MODE_AUTO; + + /* + * The extended overlay window + */ + dp->ext.init = cyberpro_ext_init; + dp->ext.set_src = cyberpro_ext_set_src; + dp->ext.set_win = cyberpro_ext_set_win; + dp->ext.ctl = cyberpro_ext_ctl; + + /* + * The V2 overlay window + */ + dp->v2.init = cyberpro_v2_init; + dp->v2.set_src = cyberpro_v2_set_src; + dp->v2.set_win = cyberpro_v2_set_win; + dp->v2.ctl = cyberpro_v2_ctl; + + /* + * The X2 overlay window + */ + dp->x2.init = cyberpro_x2_init; + dp->x2.set_src = cyberpro_x2_set_src; + dp->x2.set_win = cyberpro_x2_set_win; + dp->x2.ctl = cyberpro_x2_ctl; + + /* + * Set the overlay window which we shall be using + */ + dp->ovl = &dp->ext; + + dp->cap_mode1 = EXT_CAP_MODE1_ALTFIFO; + + /* + * Initialise hardware specific values. + * - CCIR656 8bit mode (YUV422 data) + * - Ignore Hgood signal + * - Invert Odd/Even field signal + */ + dp->cap_mode1 |= EXT_CAP_MODE1_CCIR656 | EXT_CAP_MODE1_8BIT; + dp->cap_mode2 = EXT_CAP_MODE2_FIXSONY | EXT_CAP_MODE2_CCIRINVHGT | + EXT_CAP_MODE2_CCIRINVOE; + dp->stream_fmt = VIDEO_PALETTE_YUV422; + + + init_waitqueue_head(&dp->vbl_wait); + cyberpro_frames_init(dp); + + /* + * wake up the decoder + */ + decoder_sleep(0); + + dp->bus.data = dp; + strncpy(dp->bus.name, dp->info.dev_name, sizeof(dp->bus.name)); + + pci_set_master(dp->info.dev); + + ret = i2c_register_bus(&dp->bus); + + /* + * If we successfully registered the bus, but didn't initialise + * the decoder (because its driver is not present), request + * that it is loaded. + */ + if (ret == 0 && !dp->decoder) + request_module("saa7111"); + + /* + * If that didn't work, then we're out of luck. + */ + if (ret == 0 && !dp->decoder) { + i2c_unregister_bus(&dp->bus); + ret = -ENXIO; + } + + if (ret) { + kfree(dp); + + /* + * put the decoder back to sleep + */ + decoder_sleep(1); + } + + return video_register_device(&dp->dev, VFL_TYPE_GRABBER, -1); +} + +/* + * This can be cleaned up when the SAA7111 code is fixed. + */ +#ifdef MODULE +static int __init cyberpro_init(void) +{ + disable_irq(35); + return init_cyber2000fb_viddev(); +} + +static void __exit cyberpro_exit(void) +{ + struct cyberpro_vidinfo *dp = dev; + + video_unregister_device(&dp->dev); + i2c_unregister_bus(&dp->bus); + kfree(dp); + + /* + * put the decoder back to sleep + */ + decoder_sleep(1); + + cyber2000fb_detach(0); +} + +module_init(cyberpro_init); +module_exit(cyberpro_exit); +#endif diff -urN orig/drivers/media/video/saa7111.c linux/drivers/media/video/saa7111.c --- orig/drivers/media/video/saa7111.c Tue May 27 10:04:40 2003 +++ linux/drivers/media/video/saa7111.c Tue May 27 10:11:32 2003 @@ -20,7 +20,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - +#include #include #include #include @@ -98,9 +98,17 @@ 0x00, /* 0d - HUE=0 */ 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0, FCTC=0, CHBW=1 */ 0x00, /* 0f - reserved */ +#ifndef CONFIG_ARCH_NETWINDER 0x48, /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */ +#else + 0xc8, /* 10 - YUV CCIR 656, 8 bits */ +#endif 0x1c, /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1, OEYC=1, OEHV=1, VIPB=0, COLO=0 */ +#ifndef CONFIG_ARCH_NETWINDER 0x00, /* 12 - output control 2 */ +#else + 0x80, /* 12 - Horizontal Locked on pin 39 */ +#endif 0x00, /* 13 - output control 3 */ 0x00, /* 14 - reserved */ 0x00, /* 15 - VBI */ diff -urN orig/drivers/misc/Kconfig linux/drivers/misc/Kconfig --- orig/drivers/misc/Kconfig Sat Nov 2 18:57:50 2002 +++ linux/drivers/misc/Kconfig Fri Nov 1 16:11:55 2002 @@ -4,5 +4,62 @@ menu "Misc devices" +menu "Multimedia Capabilities Port drivers" + +config MCP + bool "Multimedia drivers" + +# Interface drivers +config MCP_SA1100 + bool "Support SA1100 MCP interface" + depends on MCP && ARCH_SA1100 + +# Chip drivers +config MCP_UCB1200 + tristate "Support for UCB1200 / UCB1300" + depends on MCP + +config MCP_UCB1200_AUDIO + tristate "Audio / Telephony interface support" + depends on MCP_UCB1200 && SOUND + +config MCP_UCB1200_TS + tristate "Touchscreen interface support" + depends on MCP_UCB1200 && INPUT + +endmenu + + +menu "Console Switches" + +config SWITCHES + tristate "Console Switch Support" + help + Say Y here to include support for simple console momentary switches. + This driver implements a miscellaneous character device (named + `switches' in /proc/misc) which can be read by userland programs + to respond to switch press events. This mechanism is efficient for + systems which may not implement a traditional heavyweight console + server. + + It is also possible to say M to build this driver as a module (named + `switches.o'). + +config SWITCHES_SA1100 + tristate "SA-1100 switches" + depends on SWITCHES && ARCH_SA1100 + help + Say Y here to include support for switches routed directly to + interruptable signals on StrongARM SA-1100 systems. + +config SWITCHES_UCB1X00 + tristate "UCB1x00 switches" + depends on SWITCHES && MCP_UCB1200 + help + Say Y here to include support for switches routed through a + UCB1x00 modem/audio analog front-end device. + +endmenu + endmenu diff -urN orig/drivers/misc/Makefile linux/drivers/misc/Makefile --- orig/drivers/misc/Makefile Mon Dec 23 18:43:55 2002 +++ linux/drivers/misc/Makefile Mon Jun 9 21:07:23 2003 @@ -1,4 +1,19 @@ # # Makefile for misc devices that really don't fit anywhere else. # -obj- := misc.o # Dummy rule to force built-in.o to be made + +obj-$(CONFIG_MCP) += mcp-core.o +obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o +obj-$(CONFIG_MCP_UCB1200_AUDIO) += ucb1x00-audio.o +obj-$(CONFIG_MCP_UCB1200_TS) += ucb1x00-ts.o + +ifeq ($(CONFIG_SA1100_ASSABET),y) +obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o +endif + +obj-$(CONFIG_SWITCHES) += switches-core.o +obj-$(CONFIG_SWITCHES_SA1100) += switches-sa1100.o +obj-$(CONFIG_SWITCHES_UCB1X00) += switches-ucb1x00.o + +obj-$(CONFIG_MCP_SA1100) += mcp-sa1100.o + diff -urN orig/drivers/misc/mcp-core.c linux/drivers/misc/mcp-core.c --- orig/drivers/misc/mcp-core.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/misc/mcp-core.c Tue Dec 31 00:04:35 2002 @@ -0,0 +1,133 @@ +/* + * linux/drivers/misc/mcp-core.c + * + * Copyright (C) 2001 Russell King + * + * 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. + * + * Generic MCP (Multimedia Communications Port) layer. All MCP locking + * is solely held within this file. + */ +#include +#include +#include +#include +#include + +#include +#include + +#include "mcp.h" + +/** + * mcp_set_telecom_divisor - set the telecom divisor + * @mcp: MCP interface structure + * @div: SIB clock divisor + * + * Set the telecom divisor on the MCP interface. The resulting + * sample rate is SIBCLOCK/div. + */ +void mcp_set_telecom_divisor(struct mcp *mcp, unsigned int div) +{ + spin_lock_irq(&mcp->lock); + mcp->set_telecom_divisor(mcp, div); + spin_unlock_irq(&mcp->lock); +} + +/** + * mcp_set_audio_divisor - set the audio divisor + * @mcp: MCP interface structure + * @div: SIB clock divisor + * + * Set the audio divisor on the MCP interface. + */ +void mcp_set_audio_divisor(struct mcp *mcp, unsigned int div) +{ + spin_lock_irq(&mcp->lock); + mcp->set_audio_divisor(mcp, div); + spin_unlock_irq(&mcp->lock); +} + +/** + * mcp_reg_write - write a device register + * @mcp: MCP interface structure + * @reg: 4-bit register index + * @val: 16-bit data value + * + * Write a device register. The MCP interface must be enabled + * to prevent this function hanging. + */ +void mcp_reg_write(struct mcp *mcp, unsigned int reg, unsigned int val) +{ + unsigned long flags; + + spin_lock_irqsave(&mcp->lock, flags); + mcp->reg_write(mcp, reg, val); + spin_unlock_irqrestore(&mcp->lock, flags); +} + +/** + * mcp_reg_read - read a device register + * @mcp: MCP interface structure + * @reg: 4-bit register index + * + * Read a device register and return its value. The MCP interface + * must be enabled to prevent this function hanging. + */ +unsigned int mcp_reg_read(struct mcp *mcp, unsigned int reg) +{ + unsigned long flags; + unsigned int val; + + spin_lock_irqsave(&mcp->lock, flags); + val = mcp->reg_read(mcp, reg); + spin_unlock_irqrestore(&mcp->lock, flags); + + return val; +} + +/** + * mcp_enable - enable the MCP interface + * @mcp: MCP interface to enable + * + * Enable the MCP interface. Each call to mcp_enable will need + * a corresponding call to mcp_disable to disable the interface. + */ +void mcp_enable(struct mcp *mcp) +{ + spin_lock_irq(&mcp->lock); + if (mcp->use_count++ == 0) + mcp->enable(mcp); + spin_unlock_irq(&mcp->lock); +} + +/** + * mcp_disable - disable the MCP interface + * @mcp: MCP interface to disable + * + * Disable the MCP interface. The MCP interface will only be + * disabled once the number of calls to mcp_enable matches the + * number of calls to mcp_disable. + */ +void mcp_disable(struct mcp *mcp) +{ + unsigned long flags; + + spin_lock_irqsave(&mcp->lock, flags); + if (--mcp->use_count == 0) + mcp->disable(mcp); + spin_unlock_irqrestore(&mcp->lock, flags); +} + +EXPORT_SYMBOL(mcp_set_telecom_divisor); +EXPORT_SYMBOL(mcp_set_audio_divisor); +EXPORT_SYMBOL(mcp_reg_write); +EXPORT_SYMBOL(mcp_reg_read); +EXPORT_SYMBOL(mcp_enable); +EXPORT_SYMBOL(mcp_disable); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("Core multimedia communications port driver"); +MODULE_LICENSE("GPL"); diff -urN orig/drivers/misc/mcp-sa1100.c linux/drivers/misc/mcp-sa1100.c --- orig/drivers/misc/mcp-sa1100.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/misc/mcp-sa1100.c Tue May 20 14:19:21 2003 @@ -0,0 +1,275 @@ +/* + * linux/drivers/misc/mcp-sa1100.c + * + * Copyright (C) 2001 Russell King + * + * 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. + * + * SA1100 MCP (Multimedia Communications Port) driver. + * + * MCP read/write timeouts from Jordi Colomer, rehacked by rmk. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "mcp.h" + +static void +mcp_sa1100_set_telecom_divisor(struct mcp *mcp, unsigned int divisor) +{ + unsigned int mccr0; + + divisor /= 32; + + mccr0 = Ser4MCCR0 & ~0x00007f00; + mccr0 |= divisor << 8; + Ser4MCCR0 = mccr0; +} + +static void +mcp_sa1100_set_audio_divisor(struct mcp *mcp, unsigned int divisor) +{ + unsigned int mccr0; + + divisor /= 32; + + mccr0 = Ser4MCCR0 & ~0x0000007f; + mccr0 |= divisor; + Ser4MCCR0 = mccr0; +} + +/* + * Write data to the device. The bit should be set after 3 subframe + * times (each frame is 64 clocks). We wait a maximum of 6 subframes. + * We really should try doing something more productive while we + * wait. + */ +static void +mcp_sa1100_write(struct mcp *mcp, unsigned int reg, unsigned int val) +{ + int ret = -ETIME; + int i; + + Ser4MCDR2 = reg << 17 | MCDR2_Wr | (val & 0xffff); + + for (i = 0; i < 2; i++) { + udelay(mcp->rw_timeout); + if (Ser4MCSR & MCSR_CWC) { + ret = 0; + break; + } + } + + if (ret < 0) + printk(KERN_WARNING "mcp: write timed out\n"); +} + +/* + * Read data from the device. The bit should be set after 3 subframe + * times (each frame is 64 clocks). We wait a maximum of 6 subframes. + * We really should try doing something more productive while we + * wait. + */ +static unsigned int +mcp_sa1100_read(struct mcp *mcp, unsigned int reg) +{ + int ret = -ETIME; + int i; + + Ser4MCDR2 = reg << 17 | MCDR2_Rd; + + for (i = 0; i < 2; i++) { + udelay(mcp->rw_timeout); + if (Ser4MCSR & MCSR_CRC) { + ret = Ser4MCDR2 & 0xffff; + break; + } + } + + if (ret < 0) + printk(KERN_WARNING "mcp: read timed out\n"); + + return ret; +} + +static void mcp_sa1100_enable(struct mcp *mcp) +{ + Ser4MCSR = -1; + Ser4MCCR0 |= MCCR0_MCE; +} + +static void mcp_sa1100_disable(struct mcp *mcp) +{ + Ser4MCCR0 &= ~MCCR0_MCE; +} + +struct mcp_sa1100_state { + u32 mccr0; + u32 mccr1; +}; + +static int mcp_sa1100_suspend(struct device *dev, u32 state, u32 level) +{ + struct mcp_sa1100_state *s = (struct mcp_sa1100_state *)dev->saved_state; + + if (!s) { + s = kmalloc(sizeof(struct mcp_sa1100_state), GFP_KERNEL); + dev->saved_state = (unsigned char *)s; + } + + if (s) { + s->mccr0 = Ser4MCCR0; + s->mccr1 = Ser4MCCR1; + } + + if (level == SUSPEND_DISABLE) + Ser4MCCR0 &= ~MCCR0_MCE; + return 0; +} + +static int mcp_sa1100_resume(struct device *dev, u32 level) +{ + struct mcp_sa1100_state *s = (struct mcp_sa1100_state *)dev->saved_state; + + if (s && level == RESUME_RESTORE_STATE) { + Ser4MCCR1 = s->mccr1; + Ser4MCCR0 = s->mccr0; + + dev->saved_state = NULL; + kfree(s); + } + return 0; +} + +/* + * Our methods. + */ +static struct mcp mcp_sa1100 = { + .owner = THIS_MODULE, + .lock = SPIN_LOCK_UNLOCKED, + .sclk_rate = 11981000, + .dma_audio_rd = DMA_Ser4MCP0Rd, + .dma_audio_wr = DMA_Ser4MCP0Wr, + .dma_telco_rd = DMA_Ser4MCP1Rd, + .dma_telco_wr = DMA_Ser4MCP1Wr, + .set_telecom_divisor = mcp_sa1100_set_telecom_divisor, + .set_audio_divisor = mcp_sa1100_set_audio_divisor, + .reg_write = mcp_sa1100_write, + .reg_read = mcp_sa1100_read, + .enable = mcp_sa1100_enable, + .disable = mcp_sa1100_disable, +}; + +static int mcp_sa1100_probe(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mcp *mcp = &mcp_sa1100; + int ret; + + if (!request_mem_region(0x80060000, 0x60, "sa11x0-mcp")) + return -EBUSY; + + if (!machine_is_adsbitsy() && !machine_is_assabet() && + !machine_is_cerf() && !machine_is_flexanet() && + !machine_is_freebird() && !machine_is_graphicsclient() && + !machine_is_graphicsmaster() && !machine_is_lart() && + !machine_is_omnimeter() && !machine_is_pfs168() && + !machine_is_shannon() && !machine_is_simpad() && + !machine_is_yopy()) { + release_mem_region(0x80060000, 0x60); + return -ENODEV; + } + + mcp_sa1100.me = dev; + + if (machine_is_assabet()) { + ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); + } + + /* + * Setup the PPC unit correctly. + */ + PPDR &= ~PPC_RXD4; + PPDR |= PPC_TXD4 | PPC_SCLK | PPC_SFRM; + PSDR |= PPC_RXD4; + PSDR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + PPSR &= ~(PPC_TXD4 | PPC_SCLK | PPC_SFRM); + + Ser4MCSR = -1; + Ser4MCCR1 = 0; + Ser4MCCR0 = 0x00007f7f | MCCR0_ADM; + + /* + * Calculate the read/write timeout (us) from the bit clock + * rate. This is the period for 3 64-bit frames. Always + * round this time up. + */ + mcp->rw_timeout = (64 * 3 * 1000000 + mcp->sclk_rate - 1) / + mcp->sclk_rate; + + ret = ucb1x00_probe(mcp); + + if (ret == 0) + dev_set_drvdata(dev, mcp); + else + release_mem_region(0x80060000, 0x60); + + return ret; +} + +static int mcp_sa1100_remove(struct device *dev) +{ + struct mcp *mcp = dev_get_drvdata(dev); + + dev_set_drvdata(dev, NULL); + + ucb1x00_remove(mcp); + release_mem_region(0x80060000, 0x60); +} + +/* + * The driver for the SA11x0 MCP port. + */ +static struct device_driver mcp_sa1100_driver = { + .name = "sa11x0-mcp", + .bus = &platform_bus_type, + .probe = mcp_sa1100_probe, + .remove = mcp_sa1100_remove, + .suspend = mcp_sa1100_suspend, + .resume = mcp_sa1100_resume, +}; + +/* + * This needs re-working + */ +static int __init mcp_sa1100_init(void) +{ + return driver_register(&mcp_sa1100_driver); +} + +static void __exit mcp_sa1100_exit(void) +{ + driver_unregister(&mcp_sa1100_driver); +} + +module_init(mcp_sa1100_init); +module_exit(mcp_sa1100_exit); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("SA11x0 multimedia communications port driver"); +MODULE_LICENSE("GPL"); diff -urN orig/drivers/misc/mcp.h linux/drivers/misc/mcp.h --- orig/drivers/misc/mcp.h Thu Jan 1 01:00:00 1970 +++ linux/drivers/misc/mcp.h Tue Dec 31 00:06:09 2002 @@ -0,0 +1,45 @@ +/* + * linux/drivers/misc/mcp.h + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + */ +#ifndef MCP_H +#define MCP_H + +struct mcp { + struct module *owner; + struct device *me; + spinlock_t lock; + int use_count; + unsigned int sclk_rate; + unsigned int rw_timeout; + dma_device_t dma_audio_rd; + dma_device_t dma_audio_wr; + dma_device_t dma_telco_rd; + dma_device_t dma_telco_wr; + void (*set_telecom_divisor)(struct mcp *, unsigned int); + void (*set_audio_divisor)(struct mcp *, unsigned int); + void (*reg_write)(struct mcp *, unsigned int, unsigned int); + unsigned int (*reg_read)(struct mcp *, unsigned int); + void (*enable)(struct mcp *); + void (*disable)(struct mcp *); + void *attached_drvdata; +}; + +void mcp_set_telecom_divisor(struct mcp *, unsigned int); +void mcp_set_audio_divisor(struct mcp *, unsigned int); +void mcp_reg_write(struct mcp *, unsigned int, unsigned int); +unsigned int mcp_reg_read(struct mcp *, unsigned int); +void mcp_enable(struct mcp *); +void mcp_disable(struct mcp *); + +#define mcp_get_sclk_rate(mcp) ((mcp)->sclk_rate) + +#define mcp_get_drvdata(mcp) ((mcp)->attached_drvdata) +#define mcp_set_drvdata(mcp,d) ((mcp)->attached_drvdata = (d)) + +#endif diff -urN orig/drivers/misc/switches-core.c linux/drivers/misc/switches-core.c --- orig/drivers/misc/switches-core.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/misc/switches-core.c Mon Jul 29 23:34:49 2002 @@ -0,0 +1,199 @@ +/* + * linux/drivers/misc/switches-core.c + * + * Copyright (C) 2000-2001 John Dorsey + * + * 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. + * + * 5 October 2000 - created. + * + * 25 October 2000 - userland file interface added. + * + * 13 January 2001 - added support for Spot. + * + * 11 September 2001 - UCB1200 driver framework support added. + * + * 19 December 2001 - separated out SA-1100 and UCB1x00 code. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "switches.h" + + +MODULE_AUTHOR("John Dorsey"); +MODULE_DESCRIPTION("Console switch support"); +MODULE_LICENSE("GPL"); + + +struct switches_action { + struct list_head list; + switches_mask_t mask; +}; + + +static int switches_users = 0; + +static spinlock_t switches_lock = SPIN_LOCK_UNLOCKED; + +DECLARE_WAIT_QUEUE_HEAD(switches_wait); +LIST_HEAD(switches_event_queue); + + +static ssize_t switches_read(struct file *file, char *buffer, + size_t count, loff_t *pos) +{ + unsigned long flags; + struct list_head *event; + struct switches_action *action; + + if (count < sizeof(struct switches_mask_t)) + return -EINVAL; + + while (list_empty(&switches_event_queue)) { + + if (file->f_flags & O_NDELAY) + return -EAGAIN; + + interruptible_sleep_on(&switches_wait); + + if (signal_pending(current)) + return -ERESTARTSYS; + + } + + if (verify_area(VERIFY_WRITE, buffer, sizeof(struct switches_mask_t))) + return -EFAULT; + + spin_lock_irqsave(&switches_lock, flags); + + event = switches_event_queue.next; + action = list_entry(event, struct switches_action, list); + copy_to_user(buffer, &(action->mask), sizeof(struct switches_mask_t)); + list_del(event); + kfree(action); + + spin_unlock_irqrestore(&switches_lock, flags); + + return 0; + +} + +static ssize_t switches_write(struct file *file, const char *buffer, + size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +static unsigned int switches_poll(struct file *file, poll_table *wait) +{ + + poll_wait(file, &switches_wait, wait); + + if (!list_empty(&switches_event_queue)) + return POLLIN | POLLRDNORM; + + return 0; + +} + +static int switches_open(struct inode *inode, struct file *file) +{ + + if (switches_users > 0) + return -EBUSY; + + MOD_INC_USE_COUNT; + ++switches_users; + return 0; + +} + +static int switches_release(struct inode *inode, struct file *file) +{ + + --switches_users; + MOD_DEC_USE_COUNT; + return 0; + +} + +static struct file_operations switches_ops = { + read: switches_read, + write: switches_write, + poll: switches_poll, + open: switches_open, + release: switches_release +}; + +static struct miscdevice switches_misc = { + MISC_DYNAMIC_MINOR, SWITCHES_NAME, &switches_ops +}; + +int switches_event(switches_mask_t *mask) +{ + struct switches_action *action; + + if ((switches_users > 0) && (SWITCHES_COUNT(mask) > 0)) { + + if ((action = (struct switches_action *) + kmalloc(sizeof(struct switches_action), + GFP_KERNEL)) == NULL) { + printk(KERN_ERR "%s: unable to allocate action " + "descriptor\n", SWITCHES_NAME); + return -1; + } + + action->mask = *mask; + + spin_lock(&switches_lock); + list_add_tail(&action->list, &switches_event_queue); + spin_unlock(&switches_lock); + + wake_up_interruptible(&switches_wait); + + } + + return 0; + +} + +EXPORT_SYMBOL(switches_event); + + +static int __init switches_init(void) +{ + if (misc_register(&switches_misc) < 0) { + printk(KERN_ERR "%s: unable to register misc device\n", + SWITCHES_NAME); + return -EIO; + } + + printk(KERN_INFO "Console switches initialized\n"); + + return 0; +} + +static void __exit switches_exit(void) +{ + if (misc_deregister(&switches_misc) < 0) + printk(KERN_ERR "%s: unable to deregister misc device\n", + SWITCHES_NAME); +} + +module_init(switches_init); +module_exit(switches_exit); diff -urN orig/drivers/misc/switches-sa1100.c linux/drivers/misc/switches-sa1100.c --- orig/drivers/misc/switches-sa1100.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/misc/switches-sa1100.c Sun Apr 6 23:39:22 2003 @@ -0,0 +1,316 @@ +/* + * linux/drivers/misc/switches-sa1100.c + * + * Copyright (C) 2001 John Dorsey + * + * 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. + * + * 19 December 2001 - created from sa1100_switches.c. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "switches.h" + + +static void switches_sa1100_handler(int irq, void *dev_id, + struct pt_regs *regs); + + +#ifdef CONFIG_SA1100_ASSABET + +/* Assabet + * ^^^^^^^ + * We have two general-purpose switches, S1 and S2, available via GPIO + * on Assabet. This code sets bits in the range [1, 2] in the mask that + * we return to userland. + */ + +static int assabet_switches_sa1100_init(void) +{ + + if (machine_has_neponset()) + NCR_0 |= NCR_GP01_OFF; + + if (request_irq(IRQ_GPIO0, switches_sa1100_handler, SA_INTERRUPT, + SWITCHES_NAME, NULL) < 0) { + printk(KERN_ERR "%s: unable to register IRQ for GPIO 0\n", + SWITCHES_NAME); + return -EIO; + } + + if (request_irq(IRQ_GPIO1, switches_sa1100_handler, SA_INTERRUPT, + SWITCHES_NAME, NULL) < 0) { + printk(KERN_ERR "%s: unable to register IRQ for GPIO 1\n", + SWITCHES_NAME); + free_irq(IRQ_GPIO0); + return -EIO; + } + + set_irq_type(IRQ_GPIO0, IRQT_BOTHEDGE); + set_irq_type(IRQ_GPIO1, IRQT_BOTHEDGE); + + return 0; + +} + +static void assabet_switches_sa1100_shutdown(void) +{ + + free_irq(IRQ_GPIO1, NULL); + free_irq(IRQ_GPIO0, NULL); + +} + +static void assabet_switches_sa1100_handler(int irq, switches_mask_t *mask) +{ + unsigned int s, last, this; + static unsigned int states = 0; + + switch (irq) { + + case IRQ_GPIO0: s = 0; break; + + case IRQ_GPIO1: s = 1; break; + + default: return; + + } + + last = ((states & (1 << s)) != 0); + this = ((GPLR & GPIO_GPIO(s)) != 0); + + if (last == this) /* debounce */ + return; + + SWITCHES_SET(mask, s + 1, this); + + states = this ? (states | (1 << s)) : (states & ~(1 << s)); + +} +#endif /* CONFIG_SA1100_ASSABET */ + +#ifdef CONFIG_SA1100_BADGE4 + +/* BadgePAD 4 + * ^^^^^^^^^^ + * + * Here we use test point J6 (BADGE4_GPIO_TESTPT_J6 aka GPIO 23) as a + * general purpose switch input. We map this to switch #0. + */ + +#define BADGE4_SW0_GPIO GPIO_GPIO23 /* aka BADGE4_GPIO_TESTPT_J6 */ +#define BADGE4_SW0_IRQ IRQ_GPIO23 + +static int badge4_switches_sa1100_init(void) +{ + if (request_irq(BADGE4_SW0_IRQ, switches_sa1100_handler, SA_INTERRUPT, + SWITCHES_NAME, NULL) < 0) { + printk(KERN_ERR "%s: unable to register IRQ for SW0\n", + SWITCHES_NAME); + return -EIO; + } + + set_irq_type(BADGE4_SW0_IRQ, IRQT_BOTHEDGE); + + return 0; +} + +static void badge4_switches_sa1100_shutdown(void) +{ + free_irq(BADGE4_SW0_IRQ, NULL); +} + +static void badge4_switches_sa1100_handler(int irq, switches_mask_t *mask) +{ + unsigned int swno, last, this, gpio; + static unsigned int states = 0; + + switch (irq) { + case BADGE4_SW0_IRQ: + swno = 0; + gpio = BADGE4_SW0_GPIO; + break; + default: + return; + } + + last = ((states & gpio) != 0); + this = ((GPLR & gpio) != 0); + + if (last == this) /* debounce */ + return; + + SWITCHES_SET(mask, swno, this); + + states = this ? (states | gpio) : (states & ~gpio); +} +#endif /* CONFIG_SA1100_BADGE4 */ + + +#ifdef CONFIG_SA1100_SPOT + +/* Spot + * ^^^^ + * Spot (R2, R3) has a single general-purpose switch (S1), which is + * also the power-on switch. We set bit [1] in the mask we return to + * userland. + */ + +static int spot_switches_sa1100_init(void) +{ + + set_GPIO_IRQ_edge(GPIO_SW1, GPIO_BOTH_EDGES); + + if (request_irq(IRQ_GPIO_SW1, switches_sa1100_handler, SA_INTERRUPT, + SWITCHES_NAME, NULL) < 0) { + printk(KERN_ERR "%s: unable to register IRQ for SW1\n", + SWITCHES_NAME); + return -EIO; + } + + return 0; + +} + +static void spot_switches_sa1100_shutdown(void) +{ + + free_irq(IRQ_GPIO_SW1, NULL); + +} + +static void spot_switches_sa1100_handler(int irq, switches_mask_t *mask) +{ + unsigned int s, last, this; + static unsigned int states = 0; + + switch (irq) { + + case IRQ_GPIO_SW1: s = 0; break; + + default: return; + + } + + last = ((states & (1 << s)) != 0); + this = ((GPLR & GPIO_GPIO(s)) != 0); + + if (last == this) /* debounce */ + return; + + SWITCHES_SET(mask, s + 1, this); + + states = this ? (states | (1 << s)) : (states & ~(1 << s)); + +} +#endif /* CONFIG_SA1100_SPOT */ + + +/* switches_sa1100_handler() + * ^^^^^^^^^^^^^^^^^^^^^^^^^ + * This routine is a generalized handler for SA-1100 switches + * which manages action descriptors and calls a board-specific + * service routine. This routine is appropriate for GPIO switches + * or other primary interrupt sources, and can be registered as a + * first-class IRQ handler using request_irq(). + */ +static void switches_sa1100_handler(int irq, void *dev_id, + struct pt_regs *regs) +{ + switches_mask_t mask; + + SWITCHES_ZERO(&mask); + + /* Porting note: call a board-specific switch interrupt handler + * here. The handler can assume that sufficient storage for + * `mask' has been allocated, and that the corresponding + * switches_mask_t structure has been zeroed. + */ + + if (machine_is_assabet()) { +#ifdef CONFIG_SA1100_ASSABET + assabet_switches_sa1100_handler(irq, &mask); +#endif + } else if (machine_is_badge4()) { +#ifdef CONFIG_SA1100_BADGE4 + badge4_switches_sa1100_handler(irq, &mask); +#endif + } else if (machine_is_spot()) { +#ifdef CONFIG_SA1100_SPOT + spot_switches_sa1100_handler(irq, &mask); +#endif + } + + switches_event(&mask); + +} + +int __init switches_sa1100_init(void) +{ + + /* Porting note: call a board-specific init routine here. */ + + if (machine_is_assabet()) { +#ifdef CONFIG_SA1100_ASSABET + if (assabet_switches_sa1100_init() < 0) + return -EIO; +#endif + } else if (machine_is_badge4()) { +#ifdef CONFIG_SA1100_BADGE4 + if (badge4_switches_sa1100_init() < 0) + return -EIO; +#endif + } else if (machine_is_spot()) { +#ifdef CONFIG_SA1100_SPOT + if (spot_switches_sa1100_init() < 0) + return -EIO; +#endif + } + + return 0; + +} + +void __exit switches_sa1100_exit(void) +{ + + /* Porting note: call a board-specific shutdown routine here. */ + + if (machine_is_assabet()) { +#ifdef CONFIG_SA1100_ASSABET + assabet_switches_sa1100_shutdown(); +#endif + } else if (machine_is_badge4()) { +#ifdef CONFIG_SA1100_BADGE4 + badge4_switches_sa1100_shutdown(); +#endif + } else if (machine_is_spot()) { +#ifdef CONFIG_SA1100_SPOT + spot_switches_sa1100_shutdown(); +#endif + } + +} + +module_init(switches_sa1100_init); +module_exit(switches_sa1100_exit); + +MODULE_DESCRIPTION("SA-1100 switches driver"); +MODULE_LICENSE("GPL"); diff -urN orig/drivers/misc/switches-ucb1x00.c linux/drivers/misc/switches-ucb1x00.c --- orig/drivers/misc/switches-ucb1x00.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/misc/switches-ucb1x00.c Mon Jul 29 23:34:49 2002 @@ -0,0 +1,203 @@ +/* + * linux/drivers/misc/switches-ucb1x00.c + * + * Copyright (C) 2001 John Dorsey + * + * 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. + * + * 19 December 2001 - created from sa1100_switches.c. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_SA1100_ASSABET +#include +#endif + +#include "switches.h" +#include "ucb1x00.h" + + +static struct ucb1x00 *ucb1x00; + +static void switches_ucb1x00_handler(int irq, void *devid); + + +#ifdef CONFIG_SA1100_ASSABET + +/* Assabet + * ^^^^^^^ + * Six switches are routed to GPIO pins on the UCB1300: S3 -- S8. + * This code sets bits in the range [3, 8] in the mask that we + * return to userland. Note that we transpose signals SW7 and SW8; + * see assabet_switches_ucb1x00_handler(). + */ + +static int assabet_switches_ucb1x00_init(void) +{ + int i; + + /* Note that ucb1x00_init() must complete before this point: */ + + if ((ucb1x00 = ucb1x00_get()) == NULL) { + printk(KERN_ERR "%s: UCB1300 driver not ready; switches " + "3 -- 8 will not be available\n", + SWITCHES_NAME); + return 0; + } + + ucb1x00_enable(ucb1x00); + + ucb1x00_io_set_dir(ucb1x00, + UCB_IO_0 | UCB_IO_1 | UCB_IO_2 | + UCB_IO_3 | UCB_IO_4 | UCB_IO_5, 0); + + for (i = 0; i < 6; ++i) { + + ucb1x00_enable_irq(ucb1x00, i, UCB_RISING | UCB_FALLING); + + if (ucb1x00_hook_irq(ucb1x00, i, + switches_ucb1x00_handler, NULL) < 0) { + printk(KERN_ERR "%s: unable to hook IRQ for " + "UCB1300 IO_%d\n", SWITCHES_NAME, i); + return -EBUSY; + } + + } + + return 0; + +} + +static void assabet_switches_ucb1x00_shutdown(void) +{ + int i; + + for (i = 5; i >= 0; --i) { + + ucb1x00_disable_irq(ucb1x00, i, UCB_RISING | UCB_FALLING); + + /* Only error conditions are ENOENT and EINVAL; silently + * ignore: + */ + ucb1x00_free_irq(ucb1x00, i, NULL); + + } + +} + +static void assabet_switches_ucb1x00_handler(int irq, switches_mask_t *mask) +{ + unsigned int last, this; + static unsigned int states = 0; + + last = ((states & (1 << irq)) != 0); + this = ((ucb1x00_io_read(ucb1x00) & (1 << irq)) != 0); + + if (last == this) /* debounce */ + return; + + /* Intel StrongARM SA-1110 Development Board + * Schematics Figure 5, Sheet 5 of 12 + * + * See switches S8 and S7. Notice their + * relationship to signals SW7 and SW8. Hmmm. + */ + + switch (irq) { + + case 4: + + SWITCHES_SET(mask, 8, this); + break; + + case 5: + + SWITCHES_SET(mask, 7, this); + break; + + default: + + SWITCHES_SET(mask, irq + 3, this); + + } + + states = this ? (states | (1 << irq)) : (states & ~(1 << irq)); + +} +#endif /* CONFIG_SA1100_ASSABET */ + + +/* switches_ucb1x00_handler() + * ^^^^^^^^^^^^^^^^^^^^^^^^^^ + * This routine is a generalized handler for UCB1x00 GPIO switches + * which calls a board-specific service routine and passes an event + * mask to the core event handler. This routine is appropriate for + * systems which use the ucb1x00 framework, and can be registered + * using ucb1x00_hook_irq(). + */ +static void switches_ucb1x00_handler(int irq, void *devid) +{ + switches_mask_t mask; + + SWITCHES_ZERO(&mask); + + /* Porting note: call a board-specific UCB1x00 switch handler here. + * The handler can assume that sufficient storage for `mask' has + * been allocated, and that the corresponding switches_mask_t + * structure has been zeroed. + */ + + if (machine_is_assabet()) { +#ifdef CONFIG_SA1100_ASSABET + assabet_switches_ucb1x00_handler(irq, &mask); +#endif + } + + switches_event(&mask); + +} + +int __init switches_ucb1x00_init(void) +{ + + /* Porting note: call a board-specific init routine here. */ + + if (machine_is_assabet()) { +#ifdef CONFIG_SA1100_ASSABET + if (assabet_switches_ucb1x00_init() < 0) + return -EIO; +#endif + } + + return 0; + +} + +void __exit switches_ucb1x00_exit(void) +{ + + /* Porting note: call a board-specific shutdown routine here. */ + + if (machine_is_assabet()) { +#ifdef CONFIG_SA1100_ASSABET + assabet_switches_ucb1x00_shutdown(); +#endif + } + +} + +module_init(switches_ucb1x00_init); +module_exit(switches_ucb1x00_exit); + +MODULE_DESCRIPTION("ucb1x00 switches driver"); +MODULE_LICENSE("GPL"); diff -urN orig/drivers/misc/switches.h linux/drivers/misc/switches.h --- orig/drivers/misc/switches.h Thu Jan 1 01:00:00 1970 +++ linux/drivers/misc/switches.h Sun Mar 24 15:24:06 2002 @@ -0,0 +1,28 @@ +/* + * linux/drivers/misc/switches.h + * + * Copyright (C) 2001 John Dorsey + * + * 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. + * + * 19 December 2001 - created. + */ + +#if !defined(_SWITCHES_H) +# define _SWITCHES_H + +#include + +#define SWITCHES_NAME "switches" + +extern int switches_event(switches_mask_t *mask); + +extern int switches_sa1100_init(void); +extern void switches_sa1100_exit(void); + +extern int switches_ucb1x00_init(void); +extern void switches_ucb1x00_exit(void); + +#endif /* !defined(_SWITCHES_H) */ diff -urN orig/drivers/misc/ucb1x00-assabet.c linux/drivers/misc/ucb1x00-assabet.c --- orig/drivers/misc/ucb1x00-assabet.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/misc/ucb1x00-assabet.c Mon Dec 30 20:41:22 2002 @@ -0,0 +1,115 @@ +/* + * linux/drivers/misc/ucb1x00-assabet.c + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * We handle the machine-specific bits of the UCB1x00 driver here. + */ +#include +#include +#include +#include +#include + +#include + +#include "ucb1x00.h" + +static struct proc_dir_entry *dir; +static struct ucb1x00 *ucb; + +static int ucb1x00_assabet_read_vbatt(struct ucb1x00 *ucb) +{ + int val; + ucb1x00_adc_enable(ucb); + val = ucb1x00_adc_read(ucb, UCB_ADC_INP_AD1, UCB_NOSYNC); + ucb1x00_adc_disable(ucb); + + return val; +} + +static int ucb1x00_assabet_read_vcharger(struct ucb1x00 *ucb) +{ + int val; + ucb1x00_adc_enable(ucb); + val = ucb1x00_adc_read(ucb, UCB_ADC_INP_AD0, UCB_NOSYNC); + ucb1x00_adc_disable(ucb); + + return val; +} + +static int ucb1x00_assabet_read_batt_temp(struct ucb1x00 *ucb) +{ + int val; + ucb1x00_adc_enable(ucb); + val = ucb1x00_adc_read(ucb, UCB_ADC_INP_AD2, UCB_NOSYNC); + ucb1x00_adc_disable(ucb); + + return val; +} + +static int ucb_read(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + char *p = page; + int (*fn)(struct ucb1x00 *) = data; + int v, len; + + v = fn(ucb); + + p += sprintf(p, "%d\n", v); + + len = (p - page) - off; + if (len < 0) + len = 0; + + *eof = (len <= count) ? 1 : 0; + *start = page + off; + + return len; +} + +static int __init ucb1x00_assabet_init(void) +{ + struct proc_dir_entry *res; + + ucb = ucb1x00_get(); + + if (!ucb) + return -ENODEV; + + dir = proc_mkdir("ucb1x00", NULL); + if (!dir) + return -ENOMEM; + + res = create_proc_read_entry("vbatt", S_IRUGO, dir, ucb_read, ucb1x00_assabet_read_vbatt); + if (!res) + return -ENOMEM; + + res = create_proc_read_entry("vcharger", S_IRUGO, dir, ucb_read, ucb1x00_assabet_read_vcharger); + if (!res) + return -ENOMEM; + + res = create_proc_read_entry("batt_temp", S_IRUGO, dir, ucb_read, ucb1x00_assabet_read_batt_temp); + if (!res) + return -ENOMEM; + + return 0; +} + +static void __exit ucb1x00_assabet_exit(void) +{ + remove_proc_entry("vbatt", dir); + remove_proc_entry("vcharger", dir); + remove_proc_entry("batt_temp", dir); +} + +module_init(ucb1x00_assabet_init); +module_exit(ucb1x00_assabet_exit); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("Assabet noddy testing only example ADC driver"); +MODULE_LICENSE("GPL"); diff -urN orig/drivers/misc/ucb1x00-audio.c linux/drivers/misc/ucb1x00-audio.c --- orig/drivers/misc/ucb1x00-audio.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/misc/ucb1x00-audio.c Tue May 27 20:39:44 2003 @@ -0,0 +1,430 @@ +/* + * linux/drivers/misc/ucb1x00-audio.c + * + * Copyright (C) 2001 Russell King, 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ucb1x00.h" + +#include "../sound/oss/sa1100-audio.h" + +#define MAGIC 0x41544154 + +struct ucb1x00_audio { + struct file_operations fops; + struct file_operations mops; + struct ucb1x00 *ucb; + audio_stream_t output_stream; + audio_stream_t input_stream; + audio_state_t state; + unsigned int rate; + int dev_id; + int mix_id; + unsigned int daa_oh_bit; + unsigned int telecom; + unsigned int magic; + unsigned int ctrl_a; + unsigned int ctrl_b; + + /* mixer info */ + unsigned int mod_cnt; + unsigned short output_level; + unsigned short input_level; +}; + +#define REC_MASK (SOUND_MASK_VOLUME | SOUND_MASK_MIC) +#define DEV_MASK REC_MASK + +static int +ucb1x00_mixer_ioctl(struct inode *ino, struct file *filp, uint cmd, ulong arg) +{ + struct ucb1x00_audio *ucba; + unsigned int val, gain; + int ret = 0; + + ucba = list_entry(filp->f_op, struct ucb1x00_audio, mops); + + if (_IOC_TYPE(cmd) != 'M') + return -EINVAL; + + if (cmd == SOUND_MIXER_INFO) { + struct mixer_info mi; + + strncpy(mi.id, "UCB1x00", sizeof(mi.id)); + strncpy(mi.name, "Philips UCB1x00", sizeof(mi.name)); + mi.modify_counter = ucba->mod_cnt; + return copy_to_user((void *)arg, &mi, sizeof(mi)) ? -EFAULT : 0; + } + + if (_IOC_DIR(cmd) & _IOC_WRITE) { + unsigned int left, right; + + ret = get_user(val, (unsigned int *)arg); + if (ret) + goto out; + + left = val & 255; + right = val >> 8; + + if (left > 100) + left = 100; + if (right > 100) + right = 100; + + gain = (left + right) / 2; + + ret = -EINVAL; + if (!ucba->telecom) { + switch(_IOC_NR(cmd)) { + case SOUND_MIXER_VOLUME: + ucba->output_level = gain | gain << 8; + ucba->mod_cnt++; + ucba->ctrl_b = (ucba->ctrl_b & 0xff00) | + ((gain * 31) / 100); + ucb1x00_reg_write(ucba->ucb, UCB_AC_B, + ucba->ctrl_b); + ret = 0; + break; + + case SOUND_MIXER_MIC: + ucba->input_level = gain | gain << 8; + ucba->mod_cnt++; + ucba->ctrl_a = (ucba->ctrl_a & 0x7f) | + (((gain * 31) / 100) << 7); + ucb1x00_reg_write(ucba->ucb, UCB_AC_A, + ucba->ctrl_a); + ret = 0; + break; + } + } + } + + if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_VOLUME: + val = ucba->output_level; + break; + + case SOUND_MIXER_MIC: + val = ucba->input_level; + break; + + case SOUND_MIXER_RECSRC: + case SOUND_MIXER_RECMASK: + val = ucba->telecom ? 0 : REC_MASK; + break; + + case SOUND_MIXER_DEVMASK: + val = ucba->telecom ? 0 : DEV_MASK; + break; + + case SOUND_MIXER_CAPS: + case SOUND_MIXER_STEREODEVS: + val = 0; + break; + + default: + val = 0; + ret = -EINVAL; + break; + } + + if (ret == 0) + ret = put_user(val, (int *)arg); + } + out: + return ret; +} + +static int ucb1x00_audio_setrate(struct ucb1x00_audio *ucba, int rate) +{ + unsigned int div_rate = ucb1x00_clkrate(ucba->ucb) / 32; + unsigned int div; + + div = (div_rate + (rate / 2)) / rate; + if (div < 6) + div = 6; + if (div > 127) + div = 127; + + ucba->ctrl_a = (ucba->ctrl_a & ~0x7f) | div; + + if (ucba->telecom) { + ucb1x00_reg_write(ucba->ucb, UCB_TC_B, 0); + ucb1x00_set_telecom_divisor(ucba->ucb, div * 32); + ucb1x00_reg_write(ucba->ucb, UCB_TC_A, ucba->ctrl_a); + ucb1x00_reg_write(ucba->ucb, UCB_TC_B, ucba->ctrl_b); + } else { + ucb1x00_reg_write(ucba->ucb, UCB_AC_B, 0); + ucb1x00_set_audio_divisor(ucba->ucb, div * 32); + ucb1x00_reg_write(ucba->ucb, UCB_AC_A, ucba->ctrl_a); + ucb1x00_reg_write(ucba->ucb, UCB_AC_B, ucba->ctrl_b); + } + + ucba->rate = div_rate / div; + + return ucba->rate; +} + +static int ucb1x00_audio_getrate(struct ucb1x00_audio *ucba) +{ + return ucba->rate; +} + +static void ucb1x00_audio_startup(void *data) +{ + struct ucb1x00_audio *ucba = data; + + ucb1x00_enable(ucba->ucb); + ucb1x00_audio_setrate(ucba, ucba->rate); + + ucb1x00_reg_write(ucba->ucb, UCB_MODE, UCB_MODE_DYN_VFLAG_ENA); + + /* + * Take off-hook + */ + if (ucba->daa_oh_bit) + ucb1x00_io_write(ucba->ucb, 0, ucba->daa_oh_bit); +} + +static void ucb1x00_audio_shutdown(void *data) +{ + struct ucb1x00_audio *ucba = data; + + /* + * Place on-hook + */ + if (ucba->daa_oh_bit) + ucb1x00_io_write(ucba->ucb, ucba->daa_oh_bit, 0); + + ucb1x00_reg_write(ucba->ucb, ucba->telecom ? UCB_TC_B : UCB_AC_B, 0); + ucb1x00_disable(ucba->ucb); +} + +static int +ucb1x00_audio_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) +{ + struct ucb1x00_audio *ucba; + int val, ret = 0; + + ucba = list_entry(file->f_op, struct ucb1x00_audio, fops); + + /* + * Make sure we have our magic number + */ + if (ucba->magic != MAGIC) + return -ENODEV; + + switch (cmd) { + case SNDCTL_DSP_STEREO: + ret = get_user(val, (int *)arg); + if (ret) + return ret; + if (val != 0) + return -EINVAL; + val = 0; + break; + + case SNDCTL_DSP_CHANNELS: + case SOUND_PCM_READ_CHANNELS: + val = 1; + break; + + case SNDCTL_DSP_SPEED: + ret = get_user(val, (int *)arg); + if (ret) + return ret; + val = ucb1x00_audio_setrate(ucba, val); + break; + + case SOUND_PCM_READ_RATE: + val = ucb1x00_audio_getrate(ucba); + break; + + case SNDCTL_DSP_SETFMT: + case SNDCTL_DSP_GETFMTS: + val = AFMT_S16_LE; + break; + + default: + return ucb1x00_mixer_ioctl(inode, file, cmd, arg); + } + + return put_user(val, (int *)arg); +} + +static int ucb1x00_audio_open(struct inode *inode, struct file *file) +{ + struct ucb1x00_audio *ucba; + + ucba = list_entry(file->f_op, struct ucb1x00_audio, fops); + + return sa1100_audio_attach(inode, file, &ucba->state); +} + +static struct ucb1x00_audio *ucb1x00_audio_alloc(struct ucb1x00 *ucb) +{ + struct ucb1x00_audio *ucba; + + ucba = kmalloc(sizeof(*ucba), GFP_KERNEL); + if (ucba) { + memset(ucba, 0, sizeof(*ucba)); + + ucba->magic = MAGIC; + ucba->ucb = ucb; + ucba->fops.owner = THIS_MODULE; + ucba->fops.open = ucb1x00_audio_open; + ucba->mops.owner = THIS_MODULE; + ucba->mops.ioctl = ucb1x00_mixer_ioctl; + ucba->state.output_stream = &ucba->output_stream; + ucba->state.input_stream = &ucba->input_stream; + ucba->state.data = ucba; + ucba->state.hw_init = ucb1x00_audio_startup; + ucba->state.hw_shutdown = ucb1x00_audio_shutdown; + ucba->state.client_ioctl = ucb1x00_audio_ioctl; + + /* There is a bug in the StrongARM causes corrupt MCP data to be sent to + * the codec when the FIFOs are empty and writes are made to the OS timer + * match register 0. To avoid this we must make sure that data is always + * sent to the codec. + */ + ucba->state.need_tx_for_rx = 1; + + init_MUTEX(&ucba->state.sem); + ucba->rate = 8000; + } + return ucba; +} + +static int ucb1x00_audio_probe(struct device *_dev) +{ + struct ucb1x00_device *dev = ucb1x00_dev(_dev); + struct ucb1x00 *ucb = dev->ucb; + struct ucb1x00_audio *a; + + a = ucb1x00_audio_alloc(ucb); + if (a) { + ucb1x00_set_drvdata(dev, a); + a->telecom = dev->id == UCBDEVID_TELECOM; + + a->input_stream.dev = _dev; + a->output_stream.dev = _dev; + a->ctrl_a = 0; + + if (a->telecom) { + a->input_stream.dma_dev = ucb->mcp->dma_telco_rd; + a->input_stream.id = "UCB1x00 telco in"; + a->output_stream.dma_dev = ucb->mcp->dma_telco_wr; + a->output_stream.id = "UCB1x00 telco out"; + a->ctrl_b = UCB_TC_B_IN_ENA|UCB_TC_B_OUT_ENA; +#if 0 + a->daa_oh_bit = UCB_IO_8; + + ucb1x00_enable(ucb); + ucb1x00_io_write(ucb, a->daa_oh_bit, 0); + ucb1x00_io_set_dir(ucb, UCB_IO_7 | UCB_IO_6, a->daa_oh_bit); + ucb1x00_disable(ucb); +#endif + } else { + a->input_stream.dma_dev = ucb->mcp->dma_audio_rd; + a->input_stream.id = "UCB1x00 audio in"; + a->output_stream.dma_dev = ucb->mcp->dma_audio_wr; + a->output_stream.id = "UCB1x00 audio out"; + a->ctrl_b = UCB_AC_B_IN_ENA|UCB_AC_B_OUT_ENA; + } + + a->dev_id = register_sound_dsp(&a->fops, -1); + a->mix_id = register_sound_mixer(&a->mops, -1); + + printk("Sound: UCB1x00 %s: dsp id %d mixer id %d\n", + a->telecom ? "telecom" : "audio", + a->dev_id, a->mix_id); + } + + return a ? 0 : -ENOMEM; +} + +static int ucb1x00_audio_remove(struct device *_dev) +{ + struct ucb1x00_device *dev = ucb1x00_dev(_dev); + struct ucb1x00_audio *a = ucb1x00_get_drvdata(dev); + + unregister_sound_dsp(a->dev_id); + unregister_sound_mixer(a->mix_id); + + return 0; +} + +#ifdef CONFIG_PM +static int ucb1x00_audio_suspend(struct device *_dev, u32 state, u32 level) +{ + struct ucb1x00_device *dev = ucb1x00_dev(_dev); + struct ucb1x00_audio *a = ucb1x00_get_drvdata(dev); + + return sa1100_audio_suspend(&a->state, state, level); +} + +static int ucb1x00_audio_resume(struct device *_dev, u32 level) +{ + struct ucb1x00_device *dev = ucb1x00_dev(_dev); + struct ucb1x00_audio *a = ucb1x00_get_drvdata(dev); + + return sa1100_audio_resume(&a->state, level); +} +#else +#define ucb1x00_audio_suspend NULL +#define ucb1x00_audio_resume NULL +#endif + +static unsigned int ucb1x00_audio_ids[] = { + UCBDEVID_AUDIO, + UCBDEVID_TELECOM, + UCBDEVID_LAST, +}; + +static struct ucb1x00_device_driver ucb1x00_audio_driver = { + .drv = { + .name = "ucb1x00-audio", + .bus = &ucb1x00_bus_type, + .probe = ucb1x00_audio_probe, + .remove = ucb1x00_audio_remove, + .suspend = ucb1x00_audio_suspend, + .resume = ucb1x00_audio_resume, + }, + .ids = ucb1x00_audio_ids, +}; + +static int __init ucb1x00_audio_init(void) +{ + return driver_register(&ucb1x00_audio_driver.drv); +} + +static void __exit ucb1x00_audio_exit(void) +{ + driver_unregister(&ucb1x00_audio_driver.drv); +} + +module_init(ucb1x00_audio_init); +module_exit(ucb1x00_audio_exit); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("UCB1x00 telecom/audio driver"); +MODULE_LICENSE("GPL"); diff -urN orig/drivers/misc/ucb1x00-core.c linux/drivers/misc/ucb1x00-core.c --- orig/drivers/misc/ucb1x00-core.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/misc/ucb1x00-core.c Tue May 27 20:55:08 2003 @@ -0,0 +1,573 @@ +/* + * linux/drivers/misc/ucb1x00-core.c + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * The UCB1x00 core driver provides basic services for handling IO, + * the ADC, interrupts, and accessing registers. It is designed + * such that everything goes through this layer, thereby providing + * a consistent locking methodology, as well as allowing the drivers + * to be used on other non-MCP-enabled hardware platforms. + * + * Note that all locks are private to this file. Nothing else may + * touch them. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ucb1x00.h" + +/** + * ucb1x00_io_set_dir - set IO direction + * @ucb: UCB1x00 structure describing chip + * @in: bitfield of IO pins to be set as inputs + * @out: bitfield of IO pins to be set as outputs + * + * Set the IO direction of the ten general purpose IO pins on + * the UCB1x00 chip. The @in bitfield has priority over the + * @out bitfield, in that if you specify a pin as both input + * and output, it will end up as an input. + * + * ucb1x00_enable must have been called to enable the comms + * before using this function. + * + * This function takes a spinlock, disabling interrupts. + */ +void ucb1x00_io_set_dir(struct ucb1x00 *ucb, unsigned int in, unsigned int out) +{ + unsigned long flags; + + spin_lock_irqsave(&ucb->io_lock, flags); + ucb->io_dir |= out; + ucb->io_dir &= ~in; + + ucb1x00_reg_write(ucb, UCB_IO_DIR, ucb->io_dir); + spin_unlock_irqrestore(&ucb->io_lock, flags); +} + +/** + * ucb1x00_io_write - set or clear IO outputs + * @ucb: UCB1x00 structure describing chip + * @set: bitfield of IO pins to set to logic '1' + * @clear: bitfield of IO pins to set to logic '0' + * + * Set the IO output state of the specified IO pins. The value + * is retained if the pins are subsequently configured as inputs. + * The @clear bitfield has priority over the @set bitfield - + * outputs will be cleared. + * + * ucb1x00_enable must have been called to enable the comms + * before using this function. + * + * This function takes a spinlock, disabling interrupts. + */ +void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int set, unsigned int clear) +{ + unsigned long flags; + + spin_lock_irqsave(&ucb->io_lock, flags); + ucb->io_out |= set; + ucb->io_out &= ~clear; + + ucb1x00_reg_write(ucb, UCB_IO_DATA, ucb->io_out); + spin_unlock_irqrestore(&ucb->io_lock, flags); +} + +/** + * ucb1x00_io_read - read the current state of the IO pins + * @ucb: UCB1x00 structure describing chip + * + * Return a bitfield describing the logic state of the ten + * general purpose IO pins. + * + * ucb1x00_enable must have been called to enable the comms + * before using this function. + * + * This function does not take any semaphores or spinlocks. + */ +unsigned int ucb1x00_io_read(struct ucb1x00 *ucb) +{ + return ucb1x00_reg_read(ucb, UCB_IO_DATA); +} + +/* + * UCB1300 data sheet says we must: + * 1. enable ADC => 5us (including reference startup time) + * 2. select input => 51*tsibclk => 4.3us + * 3. start conversion => 102*tsibclk => 8.5us + * (tsibclk = 1/11981000) + * Period between SIB 128-bit frames = 10.7us + */ + +/** + * ucb1x00_adc_enable - enable the ADC converter + * @ucb: UCB1x00 structure describing chip + * + * Enable the ucb1x00 and ADC converter on the UCB1x00 for use. + * Any code wishing to use the ADC converter must call this + * function prior to using it. + * + * This function takes the ADC semaphore to prevent two or more + * concurrent uses, and therefore may sleep. As a result, it + * can only be called from process context, not interrupt + * context. + * + * You should release the ADC as soon as possible using + * ucb1x00_adc_disable. + */ +void ucb1x00_adc_enable(struct ucb1x00 *ucb) +{ + down(&ucb->adc_sem); + + ucb->adc_cr |= UCB_ADC_ENA; + + ucb1x00_enable(ucb); + ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr); +} + +/** + * ucb1x00_adc_read - read the specified ADC channel + * @ucb: UCB1x00 structure describing chip + * @adc_channel: ADC channel mask + * @sync: wait for syncronisation pulse. + * + * Start an ADC conversion and wait for the result. Note that + * synchronised ADC conversions (via the ADCSYNC pin) must wait + * until the trigger is asserted and the conversion is finished. + * + * This function currently spins waiting for the conversion to + * complete (2 frames max without sync). + * + * If called for a synchronised ADC conversion, it may sleep + * with the ADC semaphore held. + */ +unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync) +{ + unsigned int val; + + if (sync) + adc_channel |= UCB_ADC_SYNC_ENA; + + ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr | adc_channel); + ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr | adc_channel | UCB_ADC_START); + + for (;;) { + val = ucb1x00_reg_read(ucb, UCB_ADC_DATA); + if (val & UCB_ADC_DAT_VAL) + break; + /* yield to other processes */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + } + + return UCB_ADC_DAT(val); +} + +/** + * ucb1x00_adc_disable - disable the ADC converter + * @ucb: UCB1x00 structure describing chip + * + * Disable the ADC converter and release the ADC semaphore. + */ +void ucb1x00_adc_disable(struct ucb1x00 *ucb) +{ + ucb->adc_cr &= ~UCB_ADC_ENA; + ucb1x00_reg_write(ucb, UCB_ADC_CR, ucb->adc_cr); + ucb1x00_disable(ucb); + + up(&ucb->adc_sem); +} + +/* + * UCB1x00 Interrupt handling. + * + * The UCB1x00 can generate interrupts when the SIBCLK is stopped. + * Since we need to read an internal register, we must re-enable + * SIBCLK to talk to the chip. We leave the clock running until + * we have finished processing all interrupts from the chip. + */ +static irqreturn_t ucb1x00_irq(int irqnr, void *devid, struct pt_regs *regs) +{ + struct ucb1x00 *ucb = devid; + struct ucb1x00_irq *irq; + unsigned int isr, i; + + ucb1x00_enable(ucb); + isr = ucb1x00_reg_read(ucb, UCB_IE_STATUS); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, isr); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); + + for (i = 0, irq = ucb->irq_handler; i < 16 && isr; i++, isr >>= 1, irq++) + if (isr & 1 && irq->fn) + irq->fn(i, irq->devid); + ucb1x00_disable(ucb); + + return IRQ_HANDLED; +} + +/** + * ucb1x00_hook_irq - hook a UCB1x00 interrupt + * @ucb: UCB1x00 structure describing chip + * @idx: interrupt index + * @fn: function to call when interrupt is triggered + * @devid: device id to pass to interrupt handler + * + * Hook the specified interrupt. You can only register one handler + * for each interrupt source. The interrupt source is not enabled + * by this function; use ucb1x00_enable_irq instead. + * + * Interrupt handlers will be called with other interrupts enabled. + * + * Returns zero on success, or one of the following errors: + * -EINVAL if the interrupt index is invalid + * -EBUSY if the interrupt has already been hooked + */ +int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid) +{ + struct ucb1x00_irq *irq; + int ret = -EINVAL; + + if (idx < 16) { + irq = ucb->irq_handler + idx; + ret = -EBUSY; + + spin_lock_irq(&ucb->lock); + if (irq->fn == NULL) { + irq->devid = devid; + irq->fn = fn; + ret = 0; + } + spin_unlock_irq(&ucb->lock); + } + return ret; +} + +/** + * ucb1x00_enable_irq - enable an UCB1x00 interrupt source + * @ucb: UCB1x00 structure describing chip + * @idx: interrupt index + * @edges: interrupt edges to enable + * + * Enable the specified interrupt to trigger on %UCB_RISING, + * %UCB_FALLING or both edges. The interrupt should have been + * hooked by ucb1x00_hook_irq. + */ +void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges) +{ + unsigned long flags; + + if (idx < 16) { + spin_lock_irqsave(&ucb->lock, flags); + + ucb1x00_enable(ucb); + if (edges & UCB_RISING) { + ucb->irq_ris_enbl |= 1 << idx; + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); + } + if (edges & UCB_FALLING) { + ucb->irq_fal_enbl |= 1 << idx; + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); + } + ucb1x00_disable(ucb); + spin_unlock_irqrestore(&ucb->lock, flags); + } +} + +/** + * ucb1x00_disable_irq - disable an UCB1x00 interrupt source + * @ucb: UCB1x00 structure describing chip + * @edges: interrupt edges to disable + * + * Disable the specified interrupt triggering on the specified + * (%UCB_RISING, %UCB_FALLING or both) edges. + */ +void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges) +{ + unsigned long flags; + + if (idx < 16) { + spin_lock_irqsave(&ucb->lock, flags); + + ucb1x00_enable(ucb); + if (edges & UCB_RISING) { + ucb->irq_ris_enbl &= ~(1 << idx); + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); + } + if (edges & UCB_FALLING) { + ucb->irq_fal_enbl &= ~(1 << idx); + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); + } + ucb1x00_disable(ucb); + spin_unlock_irqrestore(&ucb->lock, flags); + } +} + +/** + * ucb1x00_free_irq - disable and free the specified UCB1x00 interrupt + * @ucb: UCB1x00 structure describing chip + * @idx: interrupt index + * @devid: device id. + * + * Disable the interrupt source and remove the handler. devid must + * match the devid passed when hooking the interrupt. + * + * Returns zero on success, or one of the following errors: + * -EINVAL if the interrupt index is invalid + * -ENOENT if devid does not match + */ +int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid) +{ + struct ucb1x00_irq *irq; + int ret; + + if (idx >= 16) + goto bad; + + irq = ucb->irq_handler + idx; + ret = -ENOENT; + + spin_lock_irq(&ucb->lock); + if (irq->devid == devid) { + ucb->irq_ris_enbl &= ~(1 << idx); + ucb->irq_fal_enbl &= ~(1 << idx); + + ucb1x00_enable(ucb); + ucb1x00_reg_write(ucb, UCB_IE_RIS, ucb->irq_ris_enbl); + ucb1x00_reg_write(ucb, UCB_IE_FAL, ucb->irq_fal_enbl); + ucb1x00_disable(ucb); + + irq->fn = NULL; + irq->devid = NULL; + ret = 0; + } + spin_unlock_irq(&ucb->lock); + return ret; + +bad: + printk(KERN_ERR "Freeing bad UCB1x00 irq %d\n", idx); + return -EINVAL; +} + +/* + * Try to probe our interrupt, rather than relying on lots of + * hard-coded machine dependencies. For reference, the expected + * IRQ mappings are: + * + * Machine Default IRQ + * adsbitsy IRQ_GPCIN4 + * cerf IRQ_GPIO_UCB1200_IRQ + * flexanet IRQ_GPIO_GUI + * freebird IRQ_GPIO_FREEBIRD_UCB1300_IRQ + * graphicsclient ADS_EXT_IRQ(8) + * graphicsmaster ADS_EXT_IRQ(8) + * lart LART_IRQ_UCB1200 + * omnimeter IRQ_GPIO23 + * pfs168 IRQ_GPIO_UCB1300_IRQ + * simpad IRQ_GPIO_UCB1300_IRQ + * shannon SHANNON_IRQ_GPIO_IRQ_CODEC + * yopy IRQ_GPIO_UCB1200_IRQ + */ +static int __init ucb1x00_detect_irq(struct ucb1x00 *ucb) +{ + unsigned long mask; + + mask = probe_irq_on(); + if (!mask) + return NO_IRQ; + + /* + * Enable the ADC interrupt. + */ + ucb1x00_reg_write(ucb, UCB_IE_RIS, UCB_IE_ADC); + ucb1x00_reg_write(ucb, UCB_IE_FAL, UCB_IE_ADC); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0xffff); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); + + /* + * Cause an ADC interrupt. + */ + ucb1x00_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA); + ucb1x00_reg_write(ucb, UCB_ADC_CR, UCB_ADC_ENA | UCB_ADC_START); + + /* + * Wait for the conversion to complete. + */ + while ((ucb1x00_reg_read(ucb, UCB_ADC_DATA) & UCB_ADC_DAT_VAL) == 0); + ucb1x00_reg_write(ucb, UCB_ADC_CR, 0); + + /* + * Disable and clear interrupt. + */ + ucb1x00_reg_write(ucb, UCB_IE_RIS, 0); + ucb1x00_reg_write(ucb, UCB_IE_FAL, 0); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0xffff); + ucb1x00_reg_write(ucb, UCB_IE_CLEAR, 0); + + /* + * Read triggered interrupt. + */ + return probe_irq_off(mask); +} + +static char *ucb1x00_devnames[] __initdata = { + "Audio", + "Telecom", + "Touchscreen", +}; + +int ucb1x00_probe(struct mcp *mcp) +{ + struct ucb1x00 *ucb; + unsigned int id, i; + int ret = -ENODEV; + + mcp_enable(mcp); + id = mcp_reg_read(mcp, UCB_ID); + + if (id != UCB_ID_1200 && id != UCB_ID_1300) { + printk(KERN_WARNING "UCB1x00 ID not found: %04x\n", id); + goto err_disable; + } + + ucb = kmalloc(sizeof(struct ucb1x00), GFP_KERNEL); + ret = -ENOMEM; + if (!ucb) + goto err_disable; + + memset(ucb, 0, sizeof(struct ucb1x00)); + + spin_lock_init(&ucb->lock); + spin_lock_init(&ucb->io_lock); + sema_init(&ucb->adc_sem, 1); + + ucb->id = id; + ucb->mcp = mcp; + ucb->irq = ucb1x00_detect_irq(ucb); + if (ucb->irq == NO_IRQ) { + printk(KERN_ERR "UCB1x00: IRQ probe failed\n"); + ret = -ENODEV; + goto err_free; + } + + ret = request_irq(ucb->irq, ucb1x00_irq, 0, "UCB1x00", ucb); + if (ret) { + printk(KERN_ERR "ucb1x00: unable to grab irq%d: %d\n", + ucb->irq, ret); + goto err_free; + } + + set_irq_type(ucb->irq, IRQT_RISING); + mcp_set_drvdata(mcp, ucb); + + for (i = 0; i < NR_UCBDEVIDS; i++) { + ucb->dev[i].dev.parent = mcp->me; + ucb->dev[i].dev.bus = &ucb1x00_bus_type; + ucb->dev[i].dev.dma_mask = &ucb->dev[i].dma_mask; + ucb->dev[i].dma_mask = 0; + ucb->dev[i].id = i; + ucb->dev[i].ucb = ucb; + + snprintf(ucb->dev[i].dev.name, sizeof(ucb->dev[i].dev.name), + "Phillips UCB1x00 [%s]", ucb1x00_devnames[i]); + snprintf(ucb->dev[i].dev.bus_id, sizeof(ucb->dev[i].dev.bus_id), + "%s", ucb1x00_devnames[i]); + + ret = device_register(&ucb->dev[i].dev); + if (ret) + goto err_devs; + } + + mcp_disable(mcp); + return 0; + + err_devs: + while (i) { + i -= 1; + device_unregister(&ucb->dev[i].dev); + } + + free_irq(ucb->irq, ucb); + err_free: + kfree(ucb); + err_disable: + mcp_disable(mcp); + return ret; +} + +void ucb1x00_remove(struct mcp *mcp) +{ + struct ucb1x00 *ucb = mcp_get_drvdata(mcp); + int i; + + for (i = 0; i < NR_UCBDEVIDS; i++) + device_unregister(&ucb->dev[i].dev); + + free_irq(ucb->irq, ucb); + kfree(ucb); +} + +static int ucb1x00_match(struct device *_dev, struct device_driver *_drv) +{ + struct ucb1x00_device_driver *drv = ucb1x00_drv(_drv); + struct ucb1x00_device *dev = ucb1x00_dev(_dev); + unsigned int *ids = drv->ids; + + while (*ids != (unsigned int)-1) { + if (*ids == dev->id) + return 1; + ids++; + } + + return 0; +} + +struct bus_type ucb1x00_bus_type = { + .name = "ucb1x00", + .match = ucb1x00_match, +}; + +static int __init ucb1x00_init(void) +{ + return bus_register(&ucb1x00_bus_type); +} + +static void __exit ucb1x00_exit(void) +{ + bus_unregister(&ucb1x00_bus_type); +} + +module_init(ucb1x00_init); +module_exit(ucb1x00_exit); + +EXPORT_SYMBOL(ucb1x00_bus_type); + +EXPORT_SYMBOL(ucb1x00_io_set_dir); +EXPORT_SYMBOL(ucb1x00_io_write); +EXPORT_SYMBOL(ucb1x00_io_read); + +EXPORT_SYMBOL(ucb1x00_adc_enable); +EXPORT_SYMBOL(ucb1x00_adc_read); +EXPORT_SYMBOL(ucb1x00_adc_disable); + +EXPORT_SYMBOL(ucb1x00_hook_irq); +EXPORT_SYMBOL(ucb1x00_free_irq); +EXPORT_SYMBOL(ucb1x00_enable_irq); +EXPORT_SYMBOL(ucb1x00_disable_irq); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("UCB1x00 core driver"); +MODULE_LICENSE("GPL"); diff -urN orig/drivers/misc/ucb1x00-input.h linux/drivers/misc/ucb1x00-input.h --- orig/drivers/misc/ucb1x00-input.h Thu Jan 1 01:00:00 1970 +++ linux/drivers/misc/ucb1x00-input.h Sun Jun 16 15:04:42 2002 @@ -0,0 +1,233 @@ +/* + * linux/drivers/misc/ucb1x00.h + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + */ +#ifndef UCB1200_H +#define UCB1200_H + +#define UCB_IO_DATA 0x00 +#define UCB_IO_DIR 0x01 + +#define UCB_IO_0 (1 << 0) +#define UCB_IO_1 (1 << 1) +#define UCB_IO_2 (1 << 2) +#define UCB_IO_3 (1 << 3) +#define UCB_IO_4 (1 << 4) +#define UCB_IO_5 (1 << 5) +#define UCB_IO_6 (1 << 6) +#define UCB_IO_7 (1 << 7) +#define UCB_IO_8 (1 << 8) +#define UCB_IO_9 (1 << 9) + +#define UCB_IE_RIS 0x02 +#define UCB_IE_FAL 0x03 +#define UCB_IE_STATUS 0x04 +#define UCB_IE_CLEAR 0x04 +#define UCB_IE_ADC (1 << 11) +#define UCB_IE_TSPX (1 << 12) +#define UCB_IE_TSMX (1 << 13) +#define UCB_IE_TCLIP (1 << 14) +#define UCB_IE_ACLIP (1 << 15) + +#define UCB_IRQ_TSPX 12 + +#define UCB_TC_A 0x05 +#define UCB_TC_A_LOOP (1 << 7) /* UCB1200 */ +#define UCB_TC_A_AMPL (1 << 7) /* UCB1300 */ + +#define UCB_TC_B 0x06 +#define UCB_TC_B_VOICE_ENA (1 << 3) +#define UCB_TC_B_CLIP (1 << 4) +#define UCB_TC_B_ATT (1 << 6) +#define UCB_TC_B_SIDE_ENA (1 << 11) +#define UCB_TC_B_MUTE (1 << 13) +#define UCB_TC_B_IN_ENA (1 << 14) +#define UCB_TC_B_OUT_ENA (1 << 15) + +#define UCB_AC_A 0x07 +#define UCB_AC_B 0x08 +#define UCB_AC_B_LOOP (1 << 8) +#define UCB_AC_B_MUTE (1 << 13) +#define UCB_AC_B_IN_ENA (1 << 14) +#define UCB_AC_B_OUT_ENA (1 << 15) + +#define UCB_TS_CR 0x09 +#define UCB_TS_CR_TSMX_POW (1 << 0) +#define UCB_TS_CR_TSPX_POW (1 << 1) +#define UCB_TS_CR_TSMY_POW (1 << 2) +#define UCB_TS_CR_TSPY_POW (1 << 3) +#define UCB_TS_CR_TSMX_GND (1 << 4) +#define UCB_TS_CR_TSPX_GND (1 << 5) +#define UCB_TS_CR_TSMY_GND (1 << 6) +#define UCB_TS_CR_TSPY_GND (1 << 7) +#define UCB_TS_CR_MODE_INT (0 << 8) +#define UCB_TS_CR_MODE_PRES (1 << 8) +#define UCB_TS_CR_MODE_POS (2 << 8) +#define UCB_TS_CR_BIAS_ENA (1 << 11) +#define UCB_TS_CR_TSPX_LOW (1 << 12) +#define UCB_TS_CR_TSMX_LOW (1 << 13) + +#define UCB_ADC_CR 0x0a +#define UCB_ADC_SYNC_ENA (1 << 0) +#define UCB_ADC_VREFBYP_CON (1 << 1) +#define UCB_ADC_INP_TSPX (0 << 2) +#define UCB_ADC_INP_TSMX (1 << 2) +#define UCB_ADC_INP_TSPY (2 << 2) +#define UCB_ADC_INP_TSMY (3 << 2) +#define UCB_ADC_INP_AD0 (4 << 2) +#define UCB_ADC_INP_AD1 (5 << 2) +#define UCB_ADC_INP_AD2 (6 << 2) +#define UCB_ADC_INP_AD3 (7 << 2) +#define UCB_ADC_EXT_REF (1 << 5) +#define UCB_ADC_START (1 << 7) +#define UCB_ADC_ENA (1 << 15) + +#define UCB_ADC_DATA 0x0b +#define UCB_ADC_DAT_VAL (1 << 15) +#define UCB_ADC_DAT(x) (((x) & 0x7fe0) >> 5) + +#define UCB_ID 0x0c +#define UCB_ID_1200 0x1004 +#define UCB_ID_1300 0x1005 +#define UCB_ID_1400 0x4304 +#define UCB_ID_1400_BUGGY 0x4303 /* fake ID */ + +#define UCB_MODE 0x0d +#define UCB_MODE_DYN_VFLAG_ENA (1 << 12) +#define UCB_MODE_AUD_OFF_CAN (1 << 13) + +#include "mcp.h" + +struct ucb1x00; + +struct ucb1x00_irq { + void *devid; + void (*fn)(int, void *); +}; + +struct ucb1x00 { + spinlock_t lock; + struct mcp *mcp; + unsigned int irq; + struct semaphore adc_sem; + spinlock_t io_lock; + u16 id; + u16 io_dir; + u16 io_out; + u16 adc_cr; + u16 irq_fal_enbl; + u16 irq_ris_enbl; + struct ucb1x00_irq irq_handler[16]; +}; + +/** + * ucb1x00_clkrate - return the UCB1x00 SIB clock rate + * @ucb: UCB1x00 structure describing chip + * + * Return the SIB clock rate in Hz. + */ +static inline unsigned int ucb1x00_clkrate(struct ucb1x00 *ucb) +{ + return mcp_get_sclk_rate(ucb->mcp); +} + +/** + * ucb1x00_enable - enable the UCB1x00 SIB clock + * @ucb: UCB1x00 structure describing chip + * + * Enable the SIB clock. This can be called multiple times. + */ +static inline void ucb1x00_enable(struct ucb1x00 *ucb) +{ + mcp_enable(ucb->mcp); +} + +/** + * ucb1x00_disable - disable the UCB1x00 SIB clock + * @ucb: UCB1x00 structure describing chip + * + * Disable the SIB clock. The SIB clock will only be disabled + * when the number of ucb1x00_enable calls match the number of + * ucb1x00_disable calls. + */ +static inline void ucb1x00_disable(struct ucb1x00 *ucb) +{ + mcp_disable(ucb->mcp); +} + +/** + * ucb1x00_reg_write - write a UCB1x00 register + * @ucb: UCB1x00 structure describing chip + * @reg: UCB1x00 4-bit register index to write + * @val: UCB1x00 16-bit value to write + * + * Write the UCB1x00 register @reg with value @val. The SIB + * clock must be running for this function to return. + */ +static inline void ucb1x00_reg_write(struct ucb1x00 *ucb, unsigned int reg, unsigned int val) +{ + mcp_reg_write(ucb->mcp, reg, val); +} + +/** + * ucb1x00_reg_read - read a UCB1x00 register + * @ucb: UCB1x00 structure describing chip + * @reg: UCB1x00 4-bit register index to write + * + * Read the UCB1x00 register @reg and return its value. The SIB + * clock must be running for this function to return. + */ +static inline unsigned int ucb1x00_reg_read(struct ucb1x00 *ucb, unsigned int reg) +{ + return mcp_reg_read(ucb->mcp, reg); +} +/** + * ucb1x00_set_audio_divisor - + * @ucb: UCB1x00 structure describing chip + * @div: SIB clock divisor + */ +static inline void ucb1x00_set_audio_divisor(struct ucb1x00 *ucb, unsigned int div) +{ + mcp_set_audio_divisor(ucb->mcp, div); +} + +/** + * ucb1x00_set_telecom_divisor - + * @ucb: UCB1x00 structure describing chip + * @div: SIB clock divisor + */ +static inline void ucb1x00_set_telecom_divisor(struct ucb1x00 *ucb, unsigned int div) +{ + mcp_set_telecom_divisor(ucb->mcp, div); +} + +struct ucb1x00 *ucb1x00_get(void); + +void ucb1x00_io_set_dir(struct ucb1x00 *ucb, unsigned int, unsigned int); +void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int, unsigned int); +unsigned int ucb1x00_io_read(struct ucb1x00 *ucb); + +#define UCB_NOSYNC (0) +#define UCB_SYNC (1) + +unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync); +void ucb1x00_adc_enable(struct ucb1x00 *ucb); +void ucb1x00_adc_disable(struct ucb1x00 *ucb); + +/* + * Which edges of the IRQ do you want to control today? + */ +#define UCB_RISING (1 << 0) +#define UCB_FALLING (1 << 1) + +int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid); +void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges); +void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges); +int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid); + +#endif diff -urN orig/drivers/misc/ucb1x00-ts.c linux/drivers/misc/ucb1x00-ts.c --- orig/drivers/misc/ucb1x00-ts.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/misc/ucb1x00-ts.c Tue May 6 00:25:47 2003 @@ -0,0 +1,464 @@ +/* + * linux/drivers/misc/ucb1x00-ts.c + * + * Copyright (C) 2001 Russell King, 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. + * + * 21-Jan-2002 : + * + * Added support for synchronous A/D mode. This mode is useful to + * avoid noise induced in the touchpanel by the LCD, provided that + * the UCB1x00 has a valid LCD sync signal routed to its ADCSYNC pin. + * It is important to note that the signal connected to the ADCSYNC + * pin should provide pulses even when the LCD is blanked, otherwise + * a pen touch needed to unblank the LCD will never be read. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ucb1x00.h" + + +struct ucb1x00_ts { + struct input_dev idev; + struct ucb1x00 *ucb; + + wait_queue_head_t irq_wait; + struct semaphore sem; + struct completion init_exit; + struct task_struct *rtask; + int use_count; + u16 x_res; + u16 y_res; + + int restart:1; + int adcsync:1; +}; + +static int adcsync = UCB_NOSYNC; + +static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y) +{ + input_report_abs(&ts->idev, ABS_X, x); + input_report_abs(&ts->idev, ABS_Y, y); + input_report_abs(&ts->idev, ABS_PRESSURE, pressure); + input_sync(&ts->idev); +} + +static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts) +{ + input_report_abs(&ts->idev, ABS_PRESSURE, 0); + input_sync(&ts->idev); +} + +/* + * Switch to interrupt mode. + */ +static inline void ucb1x00_ts_mode_int(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_INT); +} + +/* + * Switch to pressure mode, and read pressure. We don't need to wait + * here, since both plates are being driven. + */ +static inline unsigned int ucb1x00_ts_read_pressure(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + + return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); +} + +/* + * Switch to X position mode and measure Y plate. We switch the plate + * configuration in pressure mode, then switch to position mode. This + * gives a faster response time. Even so, we need to wait about 55us + * for things to stabilise. + */ +static inline unsigned int ucb1x00_ts_read_xpos(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + + udelay(55); + + return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPY, ts->adcsync); +} + +/* + * Switch to Y position mode and measure X plate. We switch the plate + * configuration in pressure mode, then switch to position mode. This + * gives a faster response time. Even so, we need to wait about 55us + * for things to stabilise. + */ +static inline unsigned int ucb1x00_ts_read_ypos(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA); + + udelay(55); + + return ucb1x00_adc_read(ts->ucb, UCB_ADC_INP_TSPX, ts->adcsync); +} + +/* + * Switch to X plate resistance mode. Set MX to ground, PX to + * supply. Measure current. + */ +static inline unsigned int ucb1x00_ts_read_xres(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync); +} + +/* + * Switch to Y plate resistance mode. Set MY to ground, PY to + * supply. Measure current. + */ +static inline unsigned int ucb1x00_ts_read_yres(struct ucb1x00_ts *ts) +{ + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, + UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW | + UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA); + return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync); +} + +/* + * This is a RT kernel thread that handles the ADC accesses + * (mainly so we can use semaphores in the UCB1200 core code + * to serialise accesses to the ADC). + */ +static int ucb1x00_thread(void *_ts) +{ + struct ucb1x00_ts *ts = _ts; + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + int valid; + + ts->rtask = tsk; + + daemonize("ktsd"); + /* only want to receive SIGKILL */ + allow_signal(SIGKILL); + + /* + * We could run as a real-time thread. However, thus far + * this doesn't seem to be necessary. + */ +// tsk->policy = SCHED_FIFO; +// tsk->rt_priority = 1; + + complete(&ts->init_exit); + + valid = 0; + + add_wait_queue(&ts->irq_wait, &wait); + for (;;) { + unsigned int x, y, p, val; + signed long timeout; + + ts->restart = 0; + + ucb1x00_adc_enable(ts->ucb); + + x = ucb1x00_ts_read_xpos(ts); + y = ucb1x00_ts_read_ypos(ts); + p = ucb1x00_ts_read_pressure(ts); + + /* + * Switch back to interrupt mode. + */ + ucb1x00_ts_mode_int(ts); + ucb1x00_adc_disable(ts->ucb); + + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ / 100); + if (signal_pending(tsk)) + break; + + ucb1x00_enable(ts->ucb); + val = ucb1x00_reg_read(ts->ucb, UCB_TS_CR); + + if (val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW)) { + set_task_state(tsk, TASK_INTERRUPTIBLE); + + ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING); + ucb1x00_disable(ts->ucb); + + /* + * If we spat out a valid sample set last time, + * spit out a "pen off" sample here. + */ + if (valid) { + ucb1x00_ts_event_release(ts); + valid = 0; + } + + timeout = MAX_SCHEDULE_TIMEOUT; + } else { + ucb1x00_disable(ts->ucb); + + /* + * Filtering is policy. Policy belongs in user + * space. We therefore leave it to user space + * to do any filtering they please. + */ + if (!ts->restart) { + ucb1x00_ts_evt_add(ts, p, x, y); + valid = 1; + } + + set_task_state(tsk, TASK_INTERRUPTIBLE); + timeout = HZ / 100; + } + + schedule_timeout(timeout); + if (signal_pending(tsk)) + break; + } + + remove_wait_queue(&ts->irq_wait, &wait); + + ts->rtask = NULL; + complete_and_exit(&ts->init_exit, 0); +} + +/* + * We only detect touch screen _touches_ with this interrupt + * handler, and even then we just schedule our task. + */ +static void ucb1x00_ts_irq(int idx, void *id) +{ + struct ucb1x00_ts *ts = id; + ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING); + wake_up(&ts->irq_wait); +} + +static int ucb1x00_ts_open(struct input_dev *idev) +{ + struct ucb1x00_ts *ts = (struct ucb1x00_ts *)idev; + int ret = 0; + + if (down_interruptible(&ts->sem)) + return -EINTR; + + if (ts->use_count++ != 0) + goto out; + + if (ts->rtask) + panic("ucb1x00: rtask running?"); + + init_waitqueue_head(&ts->irq_wait); + ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts); + if (ret < 0) + goto out; + + /* + * If we do this at all, we should allow the user to + * measure and read the X and Y resistance at any time. + */ + ucb1x00_adc_enable(ts->ucb); + ts->x_res = ucb1x00_ts_read_xres(ts); + ts->y_res = ucb1x00_ts_read_yres(ts); + ucb1x00_adc_disable(ts->ucb); + + init_completion(&ts->init_exit); + ret = kernel_thread(ucb1x00_thread, ts, 0); + if (ret >= 0) { + wait_for_completion(&ts->init_exit); + ret = 0; + } else { + ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); + } + + out: + if (ret) + ts->use_count--; + up(&ts->sem); + return ret; +} + +/* + * Release touchscreen resources. Disable IRQs. + */ +static void ucb1x00_ts_close(struct input_dev *idev) +{ + struct ucb1x00_ts *ts = (struct ucb1x00_ts *)idev; + + down(&ts->sem); + if (--ts->use_count == 0) { + if (ts->rtask) { + send_sig(SIGKILL, ts->rtask, 1); + wait_for_completion(&ts->init_exit); + } + + ucb1x00_enable(ts->ucb); + ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts); + ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0); + ucb1x00_disable(ts->ucb); + } + up(&ts->sem); +} + +static int ucb1x00_ts_resume(struct device *_dev, u32 level) +{ + struct ucb1x00_device *dev = ucb1x00_dev(_dev); + struct ucb1x00_ts *ts = ucb1x00_get_drvdata(dev); + + if (level == RESUME_ENABLE && ts->rtask != NULL) { + /* + * Restart the TS thread to ensure the + * TS interrupt mode is set up again + * after sleep. + */ + ts->restart = 1; + wake_up(&ts->irq_wait); + } + return 0; +} + + +/* + * Initialisation. + */ +static int __init ucb1x00_ts_probe(struct device *_dev) +{ + struct ucb1x00_device *dev = ucb1x00_dev(_dev); + struct ucb1x00_ts *ts; + + ts = kmalloc(sizeof(struct ucb1x00_ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + memset(ts, 0, sizeof(struct ucb1x00_ts)); + + ts->ucb = dev->ucb; + ts->adcsync = adcsync; + init_MUTEX(&ts->sem); + + ts->idev.name = "Touchscreen panel"; + ts->idev.id.product = ts->ucb->id; + ts->idev.open = ucb1x00_ts_open; + ts->idev.close = ucb1x00_ts_close; + + __set_bit(EV_ABS, ts->idev.evbit); + __set_bit(ABS_X, ts->idev.absbit); + __set_bit(ABS_Y, ts->idev.absbit); + __set_bit(ABS_PRESSURE, ts->idev.absbit); + + input_register_device(&ts->idev); + + ucb1x00_set_drvdata(dev, ts); + + return 0; +} + +static int __exit ucb1x00_ts_remove(struct device *_dev) +{ + struct ucb1x00_device *dev = ucb1x00_dev(_dev); + struct ucb1x00_ts *ts = ucb1x00_get_drvdata(dev); + + input_unregister_device(&ts->idev); + + return 0; +} + +static unsigned int ucb1x00_ts_ids[] = { + UCBDEVID_TOUCHSCREEN, + UCBDEVID_LAST, +}; + +static struct ucb1x00_device_driver ucb1x00_ts_driver = { + .drv = { + .name = "ucb1x00-ts", + .bus = &ucb1x00_bus_type, + .probe = ucb1x00_ts_probe, + .remove = ucb1x00_ts_remove, + .resume = ucb1x00_ts_resume, + }, + .ids = ucb1x00_ts_ids, +}; + +static int __init ucb1x00_ts_init(void) +{ + return driver_register(&ucb1x00_ts_driver.drv); +} + +static void __exit ucb1x00_ts_exit(void) +{ + driver_unregister(&ucb1x00_ts_driver.drv); +} + +#ifndef MODULE + +/* + * Parse kernel command-line options. + * + * syntax : ucbts=[sync|nosync],... + */ +static int __init ucb1x00_ts_setup(char *str) +{ + char *p; + + while ((p = strsep(&str, ",")) != NULL) { + if (strcmp(p, "sync") == 0) + adcsync = UCB_SYNC; + } + + return 1; +} + +__setup("ucbts=", ucb1x00_ts_setup); + +#else + +MODULE_PARM(adcsync, "i"); +MODULE_PARM_DESC(adcsync, "Enable use of ADCSYNC signal"); + +#endif + +module_init(ucb1x00_ts_init); +module_exit(ucb1x00_ts_exit); + +MODULE_AUTHOR("Russell King "); +MODULE_DESCRIPTION("UCB1x00 touchscreen driver"); +MODULE_LICENSE("GPL"); diff -urN orig/drivers/misc/ucb1x00.h linux/drivers/misc/ucb1x00.h --- orig/drivers/misc/ucb1x00.h Thu Jan 1 01:00:00 1970 +++ linux/drivers/misc/ucb1x00.h Tue May 27 20:41:21 2003 @@ -0,0 +1,260 @@ +/* + * linux/drivers/misc/ucb1x00.h + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + */ +#ifndef UCB1200_H +#define UCB1200_H + +#define UCB_IO_DATA 0x00 +#define UCB_IO_DIR 0x01 + +#define UCB_IO_0 (1 << 0) +#define UCB_IO_1 (1 << 1) +#define UCB_IO_2 (1 << 2) +#define UCB_IO_3 (1 << 3) +#define UCB_IO_4 (1 << 4) +#define UCB_IO_5 (1 << 5) +#define UCB_IO_6 (1 << 6) +#define UCB_IO_7 (1 << 7) +#define UCB_IO_8 (1 << 8) +#define UCB_IO_9 (1 << 9) + +#define UCB_IE_RIS 0x02 +#define UCB_IE_FAL 0x03 +#define UCB_IE_STATUS 0x04 +#define UCB_IE_CLEAR 0x04 +#define UCB_IE_ADC (1 << 11) +#define UCB_IE_TSPX (1 << 12) +#define UCB_IE_TSMX (1 << 13) +#define UCB_IE_TCLIP (1 << 14) +#define UCB_IE_ACLIP (1 << 15) + +#define UCB_IRQ_TSPX 12 + +#define UCB_TC_A 0x05 +#define UCB_TC_A_LOOP (1 << 7) /* UCB1200 */ +#define UCB_TC_A_AMPL (1 << 7) /* UCB1300 */ + +#define UCB_TC_B 0x06 +#define UCB_TC_B_VOICE_ENA (1 << 3) +#define UCB_TC_B_CLIP (1 << 4) +#define UCB_TC_B_ATT (1 << 6) +#define UCB_TC_B_SIDE_ENA (1 << 11) +#define UCB_TC_B_MUTE (1 << 13) +#define UCB_TC_B_IN_ENA (1 << 14) +#define UCB_TC_B_OUT_ENA (1 << 15) + +#define UCB_AC_A 0x07 +#define UCB_AC_B 0x08 +#define UCB_AC_B_LOOP (1 << 8) +#define UCB_AC_B_MUTE (1 << 13) +#define UCB_AC_B_IN_ENA (1 << 14) +#define UCB_AC_B_OUT_ENA (1 << 15) + +#define UCB_TS_CR 0x09 +#define UCB_TS_CR_TSMX_POW (1 << 0) +#define UCB_TS_CR_TSPX_POW (1 << 1) +#define UCB_TS_CR_TSMY_POW (1 << 2) +#define UCB_TS_CR_TSPY_POW (1 << 3) +#define UCB_TS_CR_TSMX_GND (1 << 4) +#define UCB_TS_CR_TSPX_GND (1 << 5) +#define UCB_TS_CR_TSMY_GND (1 << 6) +#define UCB_TS_CR_TSPY_GND (1 << 7) +#define UCB_TS_CR_MODE_INT (0 << 8) +#define UCB_TS_CR_MODE_PRES (1 << 8) +#define UCB_TS_CR_MODE_POS (2 << 8) +#define UCB_TS_CR_BIAS_ENA (1 << 11) +#define UCB_TS_CR_TSPX_LOW (1 << 12) +#define UCB_TS_CR_TSMX_LOW (1 << 13) + +#define UCB_ADC_CR 0x0a +#define UCB_ADC_SYNC_ENA (1 << 0) +#define UCB_ADC_VREFBYP_CON (1 << 1) +#define UCB_ADC_INP_TSPX (0 << 2) +#define UCB_ADC_INP_TSMX (1 << 2) +#define UCB_ADC_INP_TSPY (2 << 2) +#define UCB_ADC_INP_TSMY (3 << 2) +#define UCB_ADC_INP_AD0 (4 << 2) +#define UCB_ADC_INP_AD1 (5 << 2) +#define UCB_ADC_INP_AD2 (6 << 2) +#define UCB_ADC_INP_AD3 (7 << 2) +#define UCB_ADC_EXT_REF (1 << 5) +#define UCB_ADC_START (1 << 7) +#define UCB_ADC_ENA (1 << 15) + +#define UCB_ADC_DATA 0x0b +#define UCB_ADC_DAT_VAL (1 << 15) +#define UCB_ADC_DAT(x) (((x) & 0x7fe0) >> 5) + +#define UCB_ID 0x0c +#define UCB_ID_1200 0x1004 +#define UCB_ID_1300 0x1005 + +#define UCB_MODE 0x0d +#define UCB_MODE_DYN_VFLAG_ENA (1 << 12) +#define UCB_MODE_AUD_OFF_CAN (1 << 13) + +#include "mcp.h" + +struct ucb1x00; + +struct ucb1x00_irq { + void *devid; + void (*fn)(int, void *); +}; + +#define UCBDEVID_AUDIO 0 +#define UCBDEVID_TELECOM 1 +#define UCBDEVID_TOUCHSCREEN 2 +#define UCBDEVID_LAST ((unsigned int)-1) + +#define NR_UCBDEVIDS 3 + +struct ucb1x00_device { + struct device dev; + unsigned int id; + u64 dma_mask; + struct ucb1x00 *ucb; +}; + +#define ucb1x00_dev(_d) container_of((_d), struct ucb1x00_device, dev) + +struct ucb1x00_device_driver { + struct device_driver drv; + unsigned int *ids; +}; + +#define ucb1x00_drv(_d) container_of((_d), struct ucb1x00_device_driver, drv) + +struct ucb1x00 { + struct ucb1x00_device dev[NR_UCBDEVIDS]; + spinlock_t lock; + struct mcp *mcp; + unsigned int irq; + struct semaphore adc_sem; + spinlock_t io_lock; + u16 id; + u16 io_dir; + u16 io_out; + u16 adc_cr; + u16 irq_fal_enbl; + u16 irq_ris_enbl; + struct ucb1x00_irq irq_handler[16]; +}; + +/** + * ucb1x00_clkrate - return the UCB1x00 SIB clock rate + * @ucb: UCB1x00 structure describing chip + * + * Return the SIB clock rate in Hz. + */ +static inline unsigned int ucb1x00_clkrate(struct ucb1x00 *ucb) +{ + return mcp_get_sclk_rate(ucb->mcp); +} + +/** + * ucb1x00_enable - enable the UCB1x00 SIB clock + * @ucb: UCB1x00 structure describing chip + * + * Enable the SIB clock. This can be called multiple times. + */ +static inline void ucb1x00_enable(struct ucb1x00 *ucb) +{ + mcp_enable(ucb->mcp); +} + +/** + * ucb1x00_disable - disable the UCB1x00 SIB clock + * @ucb: UCB1x00 structure describing chip + * + * Disable the SIB clock. The SIB clock will only be disabled + * when the number of ucb1x00_enable calls match the number of + * ucb1x00_disable calls. + */ +static inline void ucb1x00_disable(struct ucb1x00 *ucb) +{ + mcp_disable(ucb->mcp); +} + +/** + * ucb1x00_reg_write - write a UCB1x00 register + * @ucb: UCB1x00 structure describing chip + * @reg: UCB1x00 4-bit register index to write + * @val: UCB1x00 16-bit value to write + * + * Write the UCB1x00 register @reg with value @val. The SIB + * clock must be running for this function to return. + */ +static inline void ucb1x00_reg_write(struct ucb1x00 *ucb, unsigned int reg, unsigned int val) +{ + mcp_reg_write(ucb->mcp, reg, val); +} + +/** + * ucb1x00_reg_read - read a UCB1x00 register + * @ucb: UCB1x00 structure describing chip + * @reg: UCB1x00 4-bit register index to write + * + * Read the UCB1x00 register @reg and return its value. The SIB + * clock must be running for this function to return. + */ +static inline unsigned int ucb1x00_reg_read(struct ucb1x00 *ucb, unsigned int reg) +{ + return mcp_reg_read(ucb->mcp, reg); +} +/** + * ucb1x00_set_audio_divisor - + * @ucb: UCB1x00 structure describing chip + * @div: SIB clock divisor + */ +static inline void ucb1x00_set_audio_divisor(struct ucb1x00 *ucb, unsigned int div) +{ + mcp_set_audio_divisor(ucb->mcp, div); +} + +/** + * ucb1x00_set_telecom_divisor - + * @ucb: UCB1x00 structure describing chip + * @div: SIB clock divisor + */ +static inline void ucb1x00_set_telecom_divisor(struct ucb1x00 *ucb, unsigned int div) +{ + mcp_set_telecom_divisor(ucb->mcp, div); +} + +#define ucb1x00_get() NULL + +void ucb1x00_io_set_dir(struct ucb1x00 *ucb, unsigned int, unsigned int); +void ucb1x00_io_write(struct ucb1x00 *ucb, unsigned int, unsigned int); +unsigned int ucb1x00_io_read(struct ucb1x00 *ucb); + +#define UCB_NOSYNC (0) +#define UCB_SYNC (1) + +unsigned int ucb1x00_adc_read(struct ucb1x00 *ucb, int adc_channel, int sync); +void ucb1x00_adc_enable(struct ucb1x00 *ucb); +void ucb1x00_adc_disable(struct ucb1x00 *ucb); + +/* + * Which edges of the IRQ do you want to control today? + */ +#define UCB_RISING (1 << 0) +#define UCB_FALLING (1 << 1) + +int ucb1x00_hook_irq(struct ucb1x00 *ucb, unsigned int idx, void (*fn)(int, void *), void *devid); +void ucb1x00_enable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges); +void ucb1x00_disable_irq(struct ucb1x00 *ucb, unsigned int idx, int edges); +int ucb1x00_free_irq(struct ucb1x00 *ucb, unsigned int idx, void *devid); + +extern struct bus_type ucb1x00_bus_type; + +#define ucb1x00_get_drvdata(dev) (dev_get_drvdata(&(dev)->dev)) +#define ucb1x00_set_drvdata(dev,d) (dev_set_drvdata(&(dev)->dev, d)) + +#endif diff -urN orig/drivers/mtd/chips/amd_flash.c linux/drivers/mtd/chips/amd_flash.c --- orig/drivers/mtd/chips/amd_flash.c Wed Mar 5 19:45:22 2003 +++ linux/drivers/mtd/chips/amd_flash.c Wed Mar 5 10:17:31 2003 @@ -58,6 +58,7 @@ #define MANUFACTURER_TOSHIBA 0x0098 /* AMD */ +#define AM29F323CB 0x2253 #define AM29F800BB 0x2258 #define AM29F800BT 0x22D6 #define AM29LV800BB 0x225B @@ -425,15 +426,25 @@ const struct amd_flash_info table[] = { { .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29DL323CB, + .name = "AMD AM29DL323CB", + .size = 0x00400000, + .numeraseregions = 2, + .regions = { + { .offset = 0x000000, .erasesize = 0x02000, .numblocks = 8 }, + { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 63 }, + } + }, { + .mfr_id = MANUFACTURER_AMD, .dev_id = AM29LV160DT, .name = "AMD AM29LV160DT", .size = 0x00200000, .numeraseregions = 4, .regions = { { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, - { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, - { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } + { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } } }, { .mfr_id = MANUFACTURER_AMD, @@ -442,9 +453,9 @@ .size = 0x00200000, .numeraseregions = 4, .regions = { - { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, - { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, + { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } } }, { @@ -455,9 +466,9 @@ .numeraseregions = 4, .regions = { { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, - { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, - { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } + { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } } }, { .mfr_id = MANUFACTURER_FUJITSU, @@ -467,9 +478,9 @@ .numeraseregions = 4, .regions = { { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, - { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, - { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } + { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } } }, { .mfr_id = MANUFACTURER_TOSHIBA, @@ -478,9 +489,9 @@ .size = 0x00200000, .numeraseregions = 4, .regions = { - { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, - { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, + { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } } }, { @@ -490,9 +501,9 @@ .size = 0x00200000, .numeraseregions = 4, .regions = { - { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, - { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, + { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } } }, { @@ -502,9 +513,9 @@ .size = 0x00100000, .numeraseregions = 4, .regions = { - { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, - { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, + { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 } } }, { @@ -514,9 +525,9 @@ .size = 0x00100000, .numeraseregions = 4, .regions = { - { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, - { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, + { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 } } }, { @@ -527,9 +538,9 @@ .numeraseregions = 4, .regions = { { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 }, - { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, - { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } + { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } } }, { .mfr_id = MANUFACTURER_AMD, @@ -539,9 +550,9 @@ .numeraseregions = 4, .regions = { { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 }, - { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, - { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } + { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } } }, { .mfr_id = MANUFACTURER_AMD, @@ -551,9 +562,9 @@ .numeraseregions = 4, .regions = { { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 }, - { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, - { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } + { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } } }, { .mfr_id = MANUFACTURER_ST, @@ -563,9 +574,9 @@ .numeraseregions = 4, .regions = { { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 }, - { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, - { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } + { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } } }, { .mfr_id = MANUFACTURER_ST, @@ -575,9 +586,9 @@ .numeraseregions = 4, .regions = { { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, - { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, - { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } + { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } } }, { .mfr_id = MANUFACTURER_ST, @@ -586,9 +597,9 @@ .size = 0x00200000, .numeraseregions = 4, .regions = { - { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, - { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, + { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } } }, { @@ -600,7 +611,7 @@ .regions = { { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 48 }, { .offset = 0x300000, .erasesize = 0x10000, .numblocks = 15 }, - { .offset = 0x3f0000, .erasesize = 0x02000, .numblocks = 8 }, + { .offset = 0x3f0000, .erasesize = 0x02000, .numblocks = 8 }, } }, { .mfr_id = MANUFACTURER_AMD, @@ -611,7 +622,7 @@ .regions = { { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 96 }, { .offset = 0x600000, .erasesize = 0x10000, .numblocks = 31 }, - { .offset = 0x7f0000, .erasesize = 0x02000, .numblocks = 8 }, + { .offset = 0x7f0000, .erasesize = 0x02000, .numblocks = 8 }, } } }; diff -urN orig/drivers/mtd/chips/cfi_cmdset_0001.c linux/drivers/mtd/chips/cfi_cmdset_0001.c --- orig/drivers/mtd/chips/cfi_cmdset_0001.c Tue May 27 10:04:42 2003 +++ linux/drivers/mtd/chips/cfi_cmdset_0001.c Tue May 27 10:11:36 2003 @@ -4,7 +4,7 @@ * * (C) 2000 Red Hat. GPL'd * - * $Id: cfi_cmdset_0001.c,v 1.87 2001/10/02 15:05:11 dwmw2 Exp $ + * $Id: cfi_cmdset_0001.c,v 1.90 2001/12/29 11:52:11 dwmw2 Exp $ * * * 10/10/2000 Nicolas Pitre @@ -31,6 +31,8 @@ #include static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static int cfi_intelext_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *); @@ -151,15 +153,16 @@ /* Do some byteswapping if necessary */ extp->FeatureSupport = cfi32_to_cpu(extp->FeatureSupport); extp->BlkStatusRegMask = cfi32_to_cpu(extp->BlkStatusRegMask); - + extp->ProtRegAddr = cfi32_to_cpu(extp->ProtRegAddr); + #ifdef DEBUG_CFI_FEATURES /* Tell the user about it in lots of lovely detail */ cfi_tell_features(extp); #endif /* Install our own private info structure */ - cfi->cmdset_priv = extp; - } + cfi->cmdset_priv = extp; + } for (i=0; i< cfi->numchips; i++) { cfi->chips[i].word_write_time = 128; @@ -249,6 +252,8 @@ //printk(KERN_INFO "Using word write method\n" ); mtd->write = cfi_intelext_write_words; } + mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg; + mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg; mtd->sync = cfi_intelext_sync; mtd->lock = cfi_intelext_lock; mtd->unlock = cfi_intelext_unlock; @@ -435,6 +440,115 @@ return ret; } +static int cfi_intelext_read_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, int base_offst, int reg_sz) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *extp=cfi->cmdset_priv; + int ofs_factor = cfi->interleave * cfi->device_type; + int count=len; + struct flchip *chip; + int chip_num,offst; + unsigned long timeo; + DECLARE_WAITQUEUE(wait, current); + + /* Calculate which chip & protection register offset we need */ + chip_num=((unsigned int)from/reg_sz); + offst=from-(reg_sz*chip_num)+base_offst; + + while(count){ + + if(chip_num>=cfi->numchips) + goto out; + + /* Make sure that the chip is in the right state */ + + timeo = jiffies + HZ; + chip=&cfi->chips[chip_num]; + retry: + spin_lock_bh(chip->mutex); + + switch (chip->state) { + case FL_READY: + case FL_STATUS: + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: + break; + + default: + /* Stick ourselves on a wait queue to be woken when + someone changes the status */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + spin_unlock_bh(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); + timeo = jiffies + HZ; + goto retry; + } + + /* Now read the data required from this flash */ + + cfi_send_gen_cmd(0x90, 0x55,chip->start, map, cfi, cfi->device_type, NULL); + while(count && ((offst-base_offst)read8(map,(chip->start+(extp->ProtRegAddr*ofs_factor)+offst)); + buf++; + offst++; + count--; + } + + chip->state=FL_CFI_QUERY; + spin_unlock_bh(chip->mutex); + /* Move on to the next chip */ + chip_num++; + offst=base_offst; + + } + + out: + wake_up(&chip->wq); + return len-count; +} + +static int cfi_intelext_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *extp=cfi->cmdset_priv; + int base_offst,reg_sz; + + /* Check that we actually have some protection registers */ + if(!(extp->FeatureSupport&64)){ + printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name); + return 0; + } + + base_offst=(1<FactProtRegSize); + reg_sz=(1<UserProtRegSize); + + return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz); +} + +static int cfi_intelext_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *extp=cfi->cmdset_priv; + int base_offst,reg_sz; + + /* Check that we actually have some protection registers */ + if(!(extp->FeatureSupport&64)){ + printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name); + return 0; + } + + base_offst=0; + reg_sz=(1<FactProtRegSize); + + return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz); +} + + static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, __u32 datum) { struct cfi_private *cfi = map->fldrv_priv; @@ -708,8 +822,6 @@ */ switch (chip->state) { case FL_READY: - break; - case FL_CFI_QUERY: case FL_JEDEC_QUERY: cfi_write(map, CMD(0x70), cmd_adr); @@ -742,7 +854,16 @@ timeo = jiffies + HZ; goto retry; } - + /* We know we're now in FL_STATUS mode, and 'status' is current */ + /* §4.8 of the 28FxxxJ3A datasheet says "Any time SR.4 and/or SR.5 is set + [...], the device will not accept any more Write to Buffer commands". + So we must check here and reset those bits if they're set. Otherwise + we're just pissing in the wind */ + if (status & CMD(0x30)) { + printk(KERN_WARNING "SR.4 or SR.5 bits set in buffer write (status %x). Clearing.\n", status); + cfi_write(map, CMD(0x50), cmd_adr); + cfi_write(map, CMD(0x70), cmd_adr); + } ENABLE_VPP(map); cfi_write(map, CMD(0xe8), cmd_adr); chip->state = FL_WRITING_TO_BUFFER; @@ -762,8 +883,11 @@ cfi_write(map, CMD(0x70), cmd_adr); chip->state = FL_STATUS; DISABLE_VPP(map); - spin_unlock_bh(chip->mutex); printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %x, status = %x\n", status, cfi_read(map, cmd_adr)); + /* Odd. Clear status bits */ + cfi_write(map, CMD(0x50), cmd_adr); + cfi_write(map, CMD(0x70), cmd_adr); + spin_unlock_bh(chip->mutex); return -EIO; } } @@ -992,7 +1116,8 @@ cfi_write(map, CMD(0x20), adr); cfi_write(map, CMD(0xD0), adr); chip->state = FL_ERASING; - + chip->oldstate = 0; + spin_unlock_bh(chip->mutex); schedule_timeout(HZ); spin_lock_bh(chip->mutex); @@ -1009,10 +1134,15 @@ spin_unlock_bh(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - timeo = jiffies + (HZ*20); /* FIXME */ spin_lock_bh(chip->mutex); continue; } + if (chip->oldstate) { + /* This erase was suspended and resumed. + Adjust the timeout */ + timeo = jiffies + (HZ*20); /* FIXME */ + chip->oldstate = 0; + } status = cfi_read(map, adr); if ((status & status_OK) == status_OK) @@ -1022,7 +1152,10 @@ if (time_after(jiffies, timeo)) { cfi_write(map, CMD(0x70), adr); chip->state = FL_STATUS; - printk(KERN_ERR "waiting for erase to complete timed out. Xstatus = %x, status = %x.\n", status, cfi_read(map, adr)); + printk(KERN_ERR "waiting for erase at %08lx to complete timed out. Xstatus = %x, status = %x.\n", adr, status, cfi_read(map, adr)); + /* Clear status bits */ + cfi_write(map, CMD(0x50), adr); + cfi_write(map, CMD(0x70), adr); DISABLE_VPP(map); spin_unlock_bh(chip->mutex); return -EIO; diff -urN orig/drivers/mtd/chips/cfi_cmdset_0002.c linux/drivers/mtd/chips/cfi_cmdset_0002.c --- orig/drivers/mtd/chips/cfi_cmdset_0002.c Tue May 27 10:04:42 2003 +++ linux/drivers/mtd/chips/cfi_cmdset_0002.c Tue May 27 10:11:36 2003 @@ -8,7 +8,7 @@ * * This code is GPL * - * $Id: cfi_cmdset_0002.c,v 1.52 2001/10/24 09:37:30 dwmw2 Exp $ + * $Id: cfi_cmdset_0002.c,v 1.53 2001/12/21 08:52:25 dwmw2 Exp $ * */ @@ -460,10 +460,12 @@ } } - /* Go into unlock bypass mode */ - cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x20, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); + if (cfi->fast_prog) { + /* Go into unlock bypass mode */ + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x20, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); + } /* We are now aligned, write as much as possible */ while(len >= CFIDEV_BUSWIDTH) { diff -urN orig/drivers/mtd/chips/jedec_probe.c linux/drivers/mtd/chips/jedec_probe.c --- orig/drivers/mtd/chips/jedec_probe.c Wed Mar 5 19:45:22 2003 +++ linux/drivers/mtd/chips/jedec_probe.c Wed Mar 5 10:22:31 2003 @@ -1,7 +1,7 @@ /* Common Flash Interface probe code. (C) 2000 Red Hat. GPL'd. - $Id: jedec_probe.c,v 1.3 2001/10/02 15:05:12 dwmw2 Exp $ + $Id: jedec_probe.c,v 1.6 2001/10/22 10:16:08 dwmw2 Exp $ */ #include @@ -24,8 +24,10 @@ #define MANUFACTURER_FUJITSU 0x0004 #define MANUFACTURER_ATMEL 0x001f #define MANUFACTURER_ST 0x0020 -#define MANUFACTURER_SST 0x00BF +#define MANUFACTURER_INTEL 0x0089 +#define MANUFACTURER_MICRON 0x0089 #define MANUFACTURER_TOSHIBA 0x0098 +#define MANUFACTURER_SST 0x00BF /* AMD */ #define AM29F800BB 0x2258 @@ -43,6 +45,31 @@ #define MBM29LV160TE 0x22C4 #define MBM29LV160BE 0x2249 +/* Intel */ +#define I28F004B3T 0x00d4 +#define I28F004B3B 0x00d5 +#define I28F400B3T 0x8894 +#define I28F400B3B 0x8895 +#define I28F008SA 0x00a2 +#define I28F008B3T 0x00d2 +#define I28F008B3B 0x00d3 +#define I28F800B3T 0x8892 +#define I28F800B3B 0x8893 +#define I28F016B3T 0x00d0 +#define I28F016B3B 0x00d1 +#define I28F160B3T 0x8890 +#define I28F160B3B 0x8891 +#define I28F320B3T 0x8896 +#define I28F320B3B 0x8897 +#define I28F640B3T 0x8898 +#define I28F640B3B 0x8899 + +/* Micron - www.micron.com */ +#define MT28F008B3T 0x0098 +#define MT28F008B3B 0x0099 +#define MT28F800B3T 0x889C +#define MT28F800B3B 0x889D + /* ST - www.st.com */ #define M29W800T 0x00D7 #define M29W160DT 0x22C4 @@ -64,14 +91,17 @@ const int DevSize; const int InterfaceDesc; const int NumEraseRegions; + const int CmdSet; const ulong regions[4]; }; #define ERASEINFO(size,blocks) (size<<8)|(blocks-1) -#define SIZE_1MiB 20 -#define SIZE_2MiB 21 -#define SIZE_4MiB 22 +#define SIZE_512KiB 19 +#define SIZE_1MiB 20 +#define SIZE_2MiB 21 +#define SIZE_4MiB 22 +#define SIZE_8MiB 23 static const struct amd_flash_info jedec_table[] = { { @@ -79,6 +109,7 @@ .dev_id = AM29LV160DT, .name = "AMD AM29LV160DT", .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, .NumEraseRegions = 4, .regions = {ERASEINFO(0x10000,31), ERASEINFO(0x08000,1), @@ -90,6 +121,7 @@ .dev_id = AM29LV160DB, .name = "AMD AM29LV160DB", .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, .NumEraseRegions = 4, .regions = {ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), @@ -101,6 +133,7 @@ .dev_id = TC58FVT160, .name = "Toshiba TC58FVT160", .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, .NumEraseRegions = 4, .regions = {ERASEINFO(0x10000,31), ERASEINFO(0x08000,1), @@ -112,6 +145,7 @@ .dev_id = MBM29LV160TE, .name = "Fujitsu MBM29LV160TE", .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, .NumEraseRegions = 4, .regions = {ERASEINFO(0x10000,31), ERASEINFO(0x08000,1), @@ -123,6 +157,7 @@ .dev_id = TC58FVB160, .name = "Toshiba TC58FVB160", .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, .NumEraseRegions = 4, .regions = {ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), @@ -134,6 +169,7 @@ .dev_id = MBM29LV160BE, .name = "Fujitsu MBM29LV160BE", .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, .NumEraseRegions = 4, .regions = {ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), @@ -145,6 +181,7 @@ .dev_id = AM29LV800BB, .name = "AMD AM29LV800BB", .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, .NumEraseRegions = 4, .regions = {ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), @@ -156,6 +193,7 @@ .dev_id = AM29F800BB, .name = "AMD AM29F800BB", .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, .NumEraseRegions = 4, .regions = {ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), @@ -167,6 +205,7 @@ .dev_id = AM29LV800BT, .name = "AMD AM29LV800BT", .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, .NumEraseRegions = 4, .regions = {ERASEINFO(0x10000,15), ERASEINFO(0x08000,1), @@ -178,6 +217,7 @@ .dev_id = AM29F800BT, .name = "AMD AM29F800BT", .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, .NumEraseRegions = 4, .regions = {ERASEINFO(0x10000,15), ERASEINFO(0x08000,1), @@ -189,6 +229,7 @@ .dev_id = AM29LV800BB, .name = "AMD AM29LV800BB", .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, .NumEraseRegions = 4, .regions = {ERASEINFO(0x10000,15), ERASEINFO(0x08000,1), @@ -196,10 +237,245 @@ ERASEINFO(0x04000,1) } }, { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F004B3B, + .name = "Intel 28F004B3B", + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions = 2, + .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 7), + } + }, { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F004B3T, + .name = "Intel 28F004B3T", + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions = 2, + .regions = { + ERASEINFO(0x10000, 7), + ERASEINFO(0x02000, 8), + } + }, { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F400B3B, + .name = "Intel 28F400B3B", + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions = 2, + .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 7), + } + }, { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F400B3T, + .name = "Intel 28F400B3T", + .DevSize = SIZE_512KiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions = 2, + .regions = { + ERASEINFO(0x10000, 7), + ERASEINFO(0x02000, 8), + } + }, { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F008B3B, + .name = "Intel 28F008B3B", + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions = 2, + .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 15), + } + }, { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F008B3T, + .name = "Intel 28F008B3T", + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions = 2, + .regions = { + ERASEINFO(0x10000, 15), + ERASEINFO(0x02000, 8), + } + }, { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F008SA, + .name = "Intel 28F008SA", + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions = 1, + .regions = { + ERASEINFO(0x10000, 16), + } + }, { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F800B3B, + .name = "Intel 28F800B3B", + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions = 2, + .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 15), + } + }, { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F800B3T, + .name = "Intel 28F800B3T", + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions = 2, + .regions = { + ERASEINFO(0x10000, 15), + ERASEINFO(0x02000, 8), + } + }, { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F016B3B, + .name = "Intel 28F016B3B", + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions = 2, + .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 31), + } + }, { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F016B3T, + .name = "Intel 28F016B3T", + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions = 2, + .regions = { + ERASEINFO(0x10000, 31), + ERASEINFO(0x02000, 8), + } + }, { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F160B3B, + .name = "Intel 28F160B3B", + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions = 2, + .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 31), + } + }, { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F160B3T, + .name = "Intel 28F160B3T", + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions = 2, + .regions = { + ERASEINFO(0x10000, 31), + ERASEINFO(0x02000, 8), + } + }, { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F320B3B, + .name = "Intel 28F320B3B", + .DevSize = SIZE_4MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions = 2, + .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 63), + } + }, { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F320B3T, + .name = "Intel 28F320B3T", + .DevSize = SIZE_4MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions = 2, + .regions = { + ERASEINFO(0x10000, 63), + ERASEINFO(0x02000, 8), + } + }, { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F640B3B, + .name = "Intel 28F640B3B", + .DevSize = SIZE_8MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions = 2, + .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 127), + } + }, { + .mfr_id = MANUFACTURER_INTEL, + .dev_id = I28F640B3T, + .name = "Intel 28F640B3T", + .DevSize = SIZE_8MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions = 2, + .regions = { + ERASEINFO(0x10000, 127), + ERASEINFO(0x02000, 8), + } + }, { + .mfr_id = MANUFACTURER_MICRON, + .dev_id = MT28F008B3B, + .name = "Micron 28F008B3B", + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions = 4, + .regions = {ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x18000,1), + ERASEINFO(0x20000,7), + } + }, { + .mfr_id = MANUFACTURER_MICRON, + .dev_id = MT28F008B3T, + .name = "Micron 28F008B3T", + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions = 4, + .regions = {ERASEINFO(0x20000,7), + ERASEINFO(0x18000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { + .mfr_id = MANUFACTURER_MICRON, + .dev_id = MT28F800B3B, + .name = "Micron 28F800B3B", + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions = 4, + .regions = {ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x18000,1), + ERASEINFO(0x20000,7), + } + }, { + .mfr_id = MANUFACTURER_MICRON, + .dev_id = MT28F800B3T, + .name = "Micron 28F800B3T", + .DevSize = SIZE_1MiB, + .CmdSet = P_ID_INTEL_STD, + .NumEraseRegions = 4, + .regions = {ERASEINFO(0x20000,7), + ERASEINFO(0x18000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { .mfr_id = MANUFACTURER_ST, .dev_id = M29W800T, .name = "ST M29W800T", .DevSize = SIZE_1MiB, + .CmdSet = P_ID_AMD_STD, .NumEraseRegions = 4, .regions = {ERASEINFO(0x10000,15), ERASEINFO(0x08000,1), @@ -211,6 +487,7 @@ .dev_id = M29W160DT, .name = "ST M29W160DT", .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, .NumEraseRegions = 4, .regions = {ERASEINFO(0x10000,31), ERASEINFO(0x08000,1), @@ -222,6 +499,7 @@ .dev_id = M29W160DB, .name = "ST M29W160DB", .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, .NumEraseRegions = 4, .regions = {ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), @@ -233,6 +511,7 @@ .dev_id = AT49BV16X4, .name = "Atmel AT49BV16X4", .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, .NumEraseRegions = 3, .regions = {ERASEINFO(0x02000,8), ERASEINFO(0x08000,2), @@ -243,6 +522,7 @@ .dev_id = AT49BV16X4T, .name = "Atmel AT49BV16X4T", .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, .NumEraseRegions = 3, .regions = {ERASEINFO(0x10000,30), ERASEINFO(0x08000,2), @@ -276,14 +556,28 @@ } memset(p_cfi->cfiq,0,sizeof(struct cfi_ident)); - - p_cfi->cfiq->P_ID = P_ID_AMD_STD; + + p_cfi->cfiq->P_ID = jedec_table[index].CmdSet; p_cfi->cfiq->NumEraseRegions = jedec_table[index].NumEraseRegions; p_cfi->cfiq->DevSize = jedec_table[index].DevSize; for (i=0; icfiq->EraseRegionInfo[i] = jedec_table[index].regions[i]; - } + } + + if (p_cfi->cfiq->P_ID == P_ID_INTEL_STD || + p_cfi->cfiq->P_ID == P_ID_INTEL_EXT) { + struct cfi_pri_intelext *extp; + + extp = kmalloc(sizeof(*extp), GFP_KERNEL); + if (!extp) { + kfree(p_cfi->cfiq); + return 0; + } + + memset(extp, 0, sizeof(*extp)); + p_cfi->cmdset_priv = extp; + } return 1; /* ok */ } @@ -323,22 +617,32 @@ /* Reset */ cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + /* Ensure we're in read mode - Intel uses 0xff for this, AMD uses 0xff for NOP */ + cfi_send_gen_cmd(0xff, 0, base, map, cfi, cfi->device_type, NULL); + /* Autoselect Mode */ cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL); cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); if (!cfi->numchips) { + __u32 mfr, id; /* This is the first time we're called. Set up the CFI stuff accordingly and return */ - cfi->mfr = jedec_read_mfr(map, base, osf); - cfi->id = jedec_read_id(map, base, osf); + cfi->mfr = mfr = jedec_read_mfr(map, base, osf); + cfi->id = id = jedec_read_id(map, base, osf); + + mfr &= (1 << (cfi->device_type * 8)) - 1; + id &= (1 << (cfi->device_type * 8)) - 1; for (i=0; imfr == jedec_table[i].mfr_id && - cfi->id == jedec_table[i].dev_id) - return cfi_jedec_setup(cfi, i); + if (mfr == jedec_table[i].mfr_id && + id == jedec_table[i].dev_id) { + if (!cfi_jedec_setup(cfi, i)) + return 0; + goto ok_out; + } } if (!retried++) { /* Deal with whichever strange chips these were */ @@ -353,12 +657,13 @@ for (i=0; inumchips; i++) { /* This chip should be in read mode if it's one we've already touched. */ - if (jedec_read_mfr(map, base, osf) == cfi->mfr && - jedec_read_id(map, base, osf) == cfi->id) { + if (jedec_read_mfr(map, chips[i].start, osf) == cfi->mfr && + jedec_read_id(map, chips[i].start, osf) == cfi->id) { /* Eep. This chip also looks like it's in autoselect mode. Is it an alias for the new one? */ cfi_send_gen_cmd(0xF0, 0, chips[i].start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xff, 0, chips[i].start, map, cfi, cfi->device_type, NULL); /* If the device IDs go away, it's an alias */ if (jedec_read_mfr(map, base, osf) != cfi->mfr || jedec_read_id(map, base, osf) != cfi->id) { @@ -372,6 +677,7 @@ * too and if it's the same, assume it's an alias. */ /* FIXME: Use other modes to do a proper check */ cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xff, 0, base, map, cfi, cfi->device_type, NULL); if (jedec_read_mfr(map, base, osf) == cfi->mfr && jedec_read_id(map, base, osf) == cfi->id) { printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", @@ -392,8 +698,10 @@ chips[cfi->numchips].state = FL_READY; cfi->numchips++; +ok_out: /* Put it back into Read Mode */ cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xff, 0, base, map, cfi, cfi->device_type, NULL); printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit mode\n", map->name, cfi->interleave, cfi->device_type*8, base, diff -urN orig/drivers/mtd/devices/doc1000.c linux/drivers/mtd/devices/doc1000.c --- orig/drivers/mtd/devices/doc1000.c Sun Dec 30 20:35:46 2001 +++ linux/drivers/mtd/devices/doc1000.c Fri Jan 4 10:38:32 2002 @@ -1,6 +1,6 @@ /*====================================================================== - $Id: doc1000.c,v 1.15 2001/10/02 15:05:13 dwmw2 Exp $ + $Id: doc1000.c,v 1.16 2001/12/28 22:45:17 dwmw2 Exp $ ======================================================================*/ @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -482,7 +483,7 @@ else priv->devstat[erase->dev] = erase->state = MTD_ERASE_PENDING; } - else if (erase->time + erase_timeout < jiffies) + else if (time_after(jiffies, erase->time + erase_timeout)) { printk("Flash erase timed out. The world is broken.\n"); diff -urN orig/drivers/mtd/devices/mtdram.c linux/drivers/mtd/devices/mtdram.c --- orig/drivers/mtd/devices/mtdram.c Sun Oct 14 20:53:09 2001 +++ linux/drivers/mtd/devices/mtdram.c Tue Jul 30 22:24:11 2002 @@ -1,6 +1,6 @@ /* * mtdram - a test mtd device - * $Id: mtdram.c,v 1.25 2001/10/02 15:05:13 dwmw2 Exp $ + * $Id: mtdram.c,v 1.27 2002/03/28 15:05:19 dwmw2 Exp $ * Author: Alexander Larsson * * Copyright (c) 1999 Alexander Larsson @@ -28,7 +28,9 @@ static unsigned long total_size = CONFIG_MTDRAM_TOTAL_SIZE; static unsigned long erase_size = CONFIG_MTDRAM_ERASE_SIZE; MODULE_PARM(total_size,"l"); +MODULE_PARM_DESC(total_size, "Total device size in KiB"); MODULE_PARM(erase_size,"l"); +MODULE_PARM_DESC(erase_size, "Device erase block size in KiB"); #define MTDRAM_TOTAL_SIZE (total_size * 1024) #define MTDRAM_ERASE_SIZE (erase_size * 1024) #else @@ -123,7 +125,7 @@ // Allocate some memory mtd_info = (struct mtd_info *)kmalloc(sizeof(struct mtd_info), GFP_KERNEL); if (!mtd_info) - return 0; + return -ENOMEM; memset(mtd_info, 0, sizeof(*mtd_info)); diff -urN orig/drivers/mtd/devices/pmc551.c linux/drivers/mtd/devices/pmc551.c --- orig/drivers/mtd/devices/pmc551.c Wed Mar 5 19:45:22 2003 +++ linux/drivers/mtd/devices/pmc551.c Wed Mar 5 09:57:05 2003 @@ -1,5 +1,5 @@ /* - * $Id: pmc551.c,v 1.19 2001/10/02 15:05:13 dwmw2 Exp $ + * $Id: pmc551.c,v 1.20 2002/03/05 13:47:08 dwmw2 Exp $ * * PMC551 PCI Mezzanine Ram Device * @@ -98,6 +98,7 @@ #include #include #include +#include #include #include diff -urN orig/drivers/mtd/devices/slram.c linux/drivers/mtd/devices/slram.c --- orig/drivers/mtd/devices/slram.c Sun Dec 30 20:35:46 2001 +++ linux/drivers/mtd/devices/slram.c Tue Jul 30 22:24:33 2002 @@ -1,6 +1,32 @@ /*====================================================================== - $Id: slram.c,v 1.25 2001/10/02 15:05:13 dwmw2 Exp $ + $Id: slram.c,v 1.26 2002/05/17 07:07:39 jochen Exp $ + + This driver provides a method to access memory not used by the kernel + itself (i.e. if the kernel commandline mem=xxx is used). To actually + use slram at least mtdblock or mtdchar is required (for block or + character device access). + + Usage: + + if compiled as loadable module: + modprobe slram map=,, + if statically linked into the kernel use the following kernel cmd.line + slram=,, + + : name of the device that will be listed in /proc/mtd + : start of the memory region, decimal or hex (0xabcdef) + : end of the memory region. It's possible to use +0x1234 + to specify the offset instead of the absolute address + + NOTE: + With slram it's only possible to map a contigous memory region. Therfore + if there's a device mapped somewhere in the region specified slram will + fail to load (see kernel log if modprobe fails). + + - + + Jochen Schaeuble ======================================================================*/ @@ -20,6 +46,7 @@ #include #include #include +#include #include #include @@ -176,7 +203,7 @@ (*curmtd)->mtdinfo->write = slram_write; (*curmtd)->mtdinfo->module = THIS_MODULE; (*curmtd)->mtdinfo->type = MTD_RAM; - (*curmtd)->mtdinfo->erasesize = 0x10000; + (*curmtd)->mtdinfo->erasesize = 0x0; if (add_mtd_device((*curmtd)->mtdinfo)) { E("slram: Failed to register new device\n"); diff -urN orig/drivers/mtd/maps/Kconfig linux/drivers/mtd/maps/Kconfig --- orig/drivers/mtd/maps/Kconfig Sun Apr 20 16:31:59 2003 +++ linux/drivers/mtd/maps/Kconfig Thu Apr 17 21:58:14 2003 @@ -267,6 +267,10 @@ the SA1100 and SA1110, including the Assabet and the Compaq iPAQ. If you have such a board, say 'Y'. +config MTD_2PARTS_IPAQ + bool "Support 2-partitions layout (bootldr + root) for iPAQ" + depends CONFIG_MTD_SA1100 + config MTD_DC21285 tristate "CFI Flash device mapped on DC21285 Footbridge" depends on ARM && MTD_CFI && ARCH_FOOTBRIDGE diff -urN orig/drivers/mtd/maps/Makefile linux/drivers/mtd/maps/Makefile --- orig/drivers/mtd/maps/Makefile Sun Apr 20 16:31:59 2003 +++ linux/drivers/mtd/maps/Makefile Sun Apr 20 15:35:54 2003 @@ -1,7 +1,7 @@ # # linux/drivers/maps/Makefile # -# $Id: Makefile,v 1.13 2001/08/16 15:16:58 rmk Exp $ +# $Id: Makefile,v 1.17 2001/11/29 18:43:29 ppopov Exp $ # Chip mappings obj-$(CONFIG_MTD_CDB89712) += cdb89712.o @@ -14,6 +14,7 @@ obj-$(CONFIG_MTD_IQ80310) += iq80310.o obj-$(CONFIG_MTD_IQ80321) += iq80321.o obj-$(CONFIG_MTD_L440GX) += l440gx.o +obj-$(CONFIG_MTD_MBX860) += mbx860.o obj-$(CONFIG_MTD_NORA) += nora.o obj-$(CONFIG_MTD_CEIVA) += ceiva.o obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o @@ -38,3 +39,4 @@ obj-$(CONFIG_MTD_EDB7312) += edb7312.o obj-$(CONFIG_MTD_IMPA7) += impa7.o obj-$(CONFIG_MTD_FORTUNET) += fortunet.o +obj-$(CONFIG_MTD_PB1000) += pb1xxx-flash.o diff -urN orig/drivers/mtd/maps/mbx860.c linux/drivers/mtd/maps/mbx860.c --- orig/drivers/mtd/maps/mbx860.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/mtd/maps/mbx860.c Fri Jan 4 10:38:32 2002 @@ -0,0 +1,144 @@ +/* + * $Id: mbx860.c,v 1.1 2001/11/18 19:43:09 dwmw2 Exp $ + * + * Handle mapping of the flash on MBX860 boards + * + * Author: Anton Todorov + * Copyright: (C) 2001 Emness Technology + * + * 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 + + +#define WINDOW_ADDR 0xfe000000 +#define WINDOW_SIZE 0x00200000 + +/* Flash / Partition sizing */ +#define MAX_SIZE_KiB 8192 +#define BOOT_PARTITION_SIZE_KiB 512 +#define KERNEL_PARTITION_SIZE_KiB 5632 +#define APP_PARTITION_SIZE_KiB 2048 + +#define NUM_PARTITIONS 3 + +/* partition_info gives details on the logical partitions that the split the + * single flash device into. If the size if zero we use up to the end of the + * device. */ +static struct mtd_partition partition_info[]={ + { name: "MBX flash BOOT partition", + offset: 0, + size: BOOT_PARTITION_SIZE_KiB*1024 }, + { name: "MBX flash DATA partition", + offset: BOOT_PARTITION_SIZE_KiB*1024, + size: (KERNEL_PARTITION_SIZE_KiB)*1024 }, + { name: "MBX flash APPLICATION partition", + offset: (BOOT_PARTITION_SIZE_KiB+KERNEL_PARTITION_SIZE_KiB)*1024 } +}; + + +static struct mtd_info *mymtd; + +__u8 mbx_read8(struct map_info *map, unsigned long ofs) +{ + return readb(map->map_priv_1 + ofs); +} + +__u16 mbx_read16(struct map_info *map, unsigned long ofs) +{ + return readw(map->map_priv_1 + ofs); +} + +__u32 mbx_read32(struct map_info *map, unsigned long ofs) +{ + return readl(map->map_priv_1 + ofs); +} + +void mbx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); +} + +void mbx_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + writeb(d, map->map_priv_1 + adr); +} + +void mbx_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + writew(d, map->map_priv_1 + adr); +} + +void mbx_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + writel(d, map->map_priv_1 + adr); +} + +void mbx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + memcpy_toio((void *)(map->map_priv_1 + to), from, len); +} + +struct map_info mbx_map = { + name: "MBX flash", + size: WINDOW_SIZE, + buswidth: 4, + read8: mbx_read8, + read16: mbx_read16, + read32: mbx_read32, + copy_from: mbx_copy_from, + write8: mbx_write8, + write16: mbx_write16, + write32: mbx_write32, + copy_to: mbx_copy_to +}; + +int __init init_mbx(void) +{ + printk(KERN_NOTICE "Motorola MBX flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR); + mbx_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); + + if (!mbx_map.map_priv_1) { + printk("Failed to ioremap\n"); + return -EIO; + } + mymtd = do_map_probe("jedec_probe", &mbx_map); + if (mymtd) { + mymtd->module = THIS_MODULE; + add_mtd_device(mymtd); + add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS); + return 0; + } + + iounmap((void *)mbx_map.map_priv_1); + return -ENXIO; +} + +static void __exit cleanup_mbx(void) +{ + if (mymtd) { + del_mtd_device(mymtd); + map_destroy(mymtd); + } + if (mbx_map.map_priv_1) { + iounmap((void *)mbx_map.map_priv_1); + mbx_map.map_priv_1 = 0; + } +} + +module_init(init_mbx); +module_exit(cleanup_mbx); + +MODULE_AUTHOR("Anton Todorov "); +MODULE_DESCRIPTION("MTD map driver for Motorola MBX860 board"); +MODULE_LICENSE("GPL"); diff -urN orig/drivers/mtd/maps/pb1xxx-flash.c linux/drivers/mtd/maps/pb1xxx-flash.c --- orig/drivers/mtd/maps/pb1xxx-flash.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/mtd/maps/pb1xxx-flash.c Fri Jan 4 10:38:32 2002 @@ -0,0 +1,169 @@ +/* + * Flash memory access on Alchemy Pb1xxx boards + * + * (C) 2001 Pete Popov + * + * $Id: pb1xxx-flash.c,v 1.1 2001/11/29 18:43:29 ppopov Exp $ + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#define WINDOW_ADDR 0x1F800000 +#define WINDOW_SIZE 0x800000 + +__u8 physmap_read8(struct map_info *map, unsigned long ofs) +{ + return __raw_readb(map->map_priv_1 + ofs); +} + +__u16 physmap_read16(struct map_info *map, unsigned long ofs) +{ + return __raw_readw(map->map_priv_1 + ofs); +} + +__u32 physmap_read32(struct map_info *map, unsigned long ofs) +{ + return __raw_readl(map->map_priv_1 + ofs); +} + +void physmap_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy_fromio(to, map->map_priv_1 + from, len); +} + +void physmap_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + __raw_writeb(d, map->map_priv_1 + adr); + mb(); +} + +void physmap_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + __raw_writew(d, map->map_priv_1 + adr); + mb(); +} + +void physmap_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + __raw_writel(d, map->map_priv_1 + adr); + mb(); +} + +void physmap_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + memcpy_toio(map->map_priv_1 + to, from, len); +} + + + +static struct map_info pb1xxx_map = { + name: "Pb1xxx flash", + size: 0x800000, + buswidth: 4, + read8: physmap_read8, + read16: physmap_read16, + read32: physmap_read32, + copy_from: physmap_copy_from, + write8: physmap_write8, + write16: physmap_write16, + write32: physmap_write32, + copy_to: physmap_copy_to, +}; + + +#ifdef CONFIG_MIPS_PB1000 + +static unsigned long pb1000_max_flash_size = 0x00800000; +static struct mtd_partition pb1000_partitions[] = { + { + name: "yamon env", + size: 0x00020000, + offset: 0, + mask_flags: MTD_WRITEABLE + },{ + name: "jffs/2", + size: 0x003e0000, + offset: 0x20000, + },{ + name: "boot code", + size: 0x100000, + offset: 0x400000, + mask_flags: MTD_WRITEABLE + },{ + name: "raw/kernel", + size: 0x300000, + offset: 0x500000 + } +}; +#else +#error Unsupported board +#endif + + +#define NB_OF(x) (sizeof(x)/sizeof(x[0])) + +static struct mtd_partition *parsed_parts; +static struct mtd_info *mymtd; + +int __init pb1xxx_mtd_init(void) +{ + struct mtd_partition *parts; + int nb_parts = 0; + int parsed_nr_parts = 0; + char *part_type; + + /* Default flash buswidth */ + pb1xxx_map.buswidth = 4; + + /* + * Static partition definition selection + */ + part_type = "static"; +#ifdef CONFIG_MIPS_PB1000 + parts = pb1000_partitions; + nb_parts = NB_OF(pb1000_partitions); +#endif + pb1xxx_map.size = 4; + + /* + * Now let's probe for the actual flash. Do it here since + * specific machine settings might have been set above. + */ + printk(KERN_NOTICE "Pb1xxx flash: probing %d-bit flash bus\n", + pb1xxx_map.buswidth*8); + pb1xxx_map.map_priv_1 = + (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); + mymtd = do_map_probe("cfi_probe", &pb1xxx_map); + if (!mymtd) + return -ENXIO; + mymtd->module = THIS_MODULE; + + add_mtd_partitions(mymtd, parts, nb_parts); + return 0; +} + +static void __exit pb1xxx_mtd_cleanup(void) +{ + if (mymtd) { + del_mtd_partitions(mymtd); + map_destroy(mymtd); + if (parsed_parts) + kfree(parsed_parts); + } +} + +module_init(pb1xxx_mtd_init); +module_exit(pb1xxx_mtd_cleanup); + +MODULE_AUTHOR("Pete Popov"); +MODULE_DESCRIPTION("Pb1xxx CFI map driver"); +MODULE_LICENSE("GPL"); diff -urN orig/drivers/mtd/maps/pci.c linux/drivers/mtd/maps/pci.c --- orig/drivers/mtd/maps/pci.c Wed Mar 5 19:45:26 2003 +++ linux/drivers/mtd/maps/pci.c Sat May 31 16:31:28 2003 @@ -22,6 +22,8 @@ #include #include +#include + struct map_pci_info; struct mtd_pci_info { diff -urN orig/drivers/mtd/maps/pcmciamtd.c linux/drivers/mtd/maps/pcmciamtd.c --- orig/drivers/mtd/maps/pcmciamtd.c Mon May 5 17:39:13 2003 +++ linux/drivers/mtd/maps/pcmciamtd.c Sat May 17 22:53:23 2003 @@ -25,8 +25,8 @@ #include -#ifdef CONFIG_MTD_DEBUG -static int debug = CONFIG_MTD_DEBUG_VERBOSE; +#if 1 //def CONFIG_MTD_DEBUG +static int debug = 5; //CONFIG_MTD_DEBUG_VERBOSE; MODULE_PARM(debug, "i"); MODULE_PARM_DESC(debug, "Set Debug Level 0=quiet, 5=noisy"); #undef DEBUG @@ -87,7 +87,7 @@ static int setvpp; /* Force card to be treated as FLASH, ROM or RAM */ -static int mem_type; +static int mem_type = 1; MODULE_LICENSE("GPL"); MODULE_AUTHOR("Simon Evans "); @@ -836,17 +836,18 @@ return link; } +static struct pcmcia_driver pcmciamtd_driver = { + .owner = THIS_MODULE, + .drv = { + .name = "pcmciamtd", + }, + .attach = pcmciamtd_attach, + .detach = pcmciamtd_detach, +}; static int __init init_pcmciamtd(void) { - servinfo_t serv; - info(DRIVER_DESC " " DRIVER_VERSION); - CardServices(GetCardServicesInfo, &serv); - if (serv.Revision != CS_RELEASE_CODE) { - err("Card Services release does not match!"); - return -1; - } if(buswidth && buswidth != 1 && buswidth != 2) { info("bad buswidth (%d), using default", buswidth); @@ -860,8 +861,8 @@ info("bad mem_type (%d), using default", mem_type); mem_type = 0; } - register_pccard_driver(&dev_info, &pcmciamtd_attach, &pcmciamtd_detach); - return 0; + + return pcmcia_register_driver(&pcmciamtd_driver); } @@ -870,7 +871,10 @@ struct list_head *temp1, *temp2; DEBUG(1, DRIVER_DESC " unloading"); - unregister_pccard_driver(&dev_info); + + pcmcia_unregister_driver(&pcmciamtd_driver); + + /* XXX: this really needs to move into generic code.. */ list_for_each_safe(temp1, temp2, &dev_list) { dev_link_t *link = &list_entry(temp1, struct pcmciamtd_dev, list)->link; if (link && (link->state & DEV_CONFIG)) { diff -urN orig/drivers/mtd/maps/sa1100-flash.c linux/drivers/mtd/maps/sa1100-flash.c --- orig/drivers/mtd/maps/sa1100-flash.c Tue May 27 10:04:42 2003 +++ linux/drivers/mtd/maps/sa1100-flash.c Tue May 27 10:11:36 2003 @@ -1105,10 +1105,69 @@ kfree(sa[0].map); } +/* + * A Thought: can we automatically detect the flash? + * - Check to see if the region is busy (yes -> failure) + * - Is the MSC setup for flash (no -> failure) + * - Probe for flash + */ +static void sa1100_probe_one_cs(unsigned int msc, unsigned long phys) +{ + struct mtd_info *mtd; + + printk(KERN_INFO "* Probing 0x%08lx: MSC = 0x%04x %d bit ", + phys, msc & 0xffff, msc & MSC_RBW ? 16 : 32); + + if (check_mem_region(phys, 0x08000000)) { + printk("busy\n"); + return; + } + + if ((msc & 3) == 1) { + printk("wrong type\n"); + return; + } + + sa1100_map.buswidth = msc & MSC_RBW ? 2 : 4; + sa1100_map.size = SZ_1M; + sa1100_map.map_priv_1 = (unsigned long)ioremap(phys, SZ_1M); + if (sa1100_map.map_priv_1 == 0) + goto fail; + + /* Shame cfi_probe blurts out kernel messages... */ + mtd = do_map_probe("cfi_probe", &sa1100_map); + if (mtd) + map_destroy(mtd); + iounmap((void *)sa1100_map.map_priv_1); + + if (!mtd) + goto fail; + + printk("pass\n"); + return; + + fail: + printk("failed\n"); +} + +static void sa1100_probe_flash(void) +{ + printk(KERN_INFO "-- SA11xx Flash probe. Please report results.\n"); + sa1100_probe_one_cs(MSC0, SA1100_CS0_PHYS); + sa1100_probe_one_cs(MSC0 >> 16, SA1100_CS1_PHYS); + sa1100_probe_one_cs(MSC1, SA1100_CS2_PHYS); + sa1100_probe_one_cs(MSC1 >> 16, SA1100_CS3_PHYS); + sa1100_probe_one_cs(MSC2, SA1100_CS4_PHYS); + sa1100_probe_one_cs(MSC2 >> 16, SA1100_CS5_PHYS); + printk(KERN_INFO "-- SA11xx Flash probe complete.\n"); +} + static int __init sa1100_locate_flash(void) { int i, nr = -ENODEV; + sa1100_probe_flash(); + if (machine_is_adsbitsy()) { info[0].base = SA1100_CS1_PHYS; info[0].size = SZ_32M; diff -urN orig/drivers/mtd/maps/solutionengine.c linux/drivers/mtd/maps/solutionengine.c --- orig/drivers/mtd/maps/solutionengine.c Wed Mar 5 19:45:27 2003 +++ linux/drivers/mtd/maps/solutionengine.c Wed Mar 5 10:24:06 2003 @@ -1,5 +1,5 @@ /* - * $Id: solutionengine.c,v 1.3 2001/10/02 15:05:14 dwmw2 Exp $ + * $Id: solutionengine.c,v 1.4 2001/11/07 01:20:59 jsiegel Exp $ * * Flash and EPROM on Hitachi Solution Engine and similar boards. * @@ -15,6 +15,7 @@ #include #include #include +#include extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); @@ -57,20 +58,38 @@ .write32 = soleng_write32, }; +#ifdef CONFIG_MTD_SUPERH_RESERVE +static struct mtd_partition superh_se_partitions[] = { + /* Reserved for boot code, read-only */ + { + name: "flash_boot", + offset: 0x00000000, + size: CONFIG_MTD_SUPERH_RESERVE, + mask_flags: MTD_WRITEABLE, + }, + /* All else is writable (e.g. JFFS) */ + { + name: "Flash FS", + offset: MTDPART_OFS_NXTBLK, + size: MTDPART_SIZ_FULL, + } +}; +#endif /* CONFIG_MTD_SUPERH_RESERVE */ + static int __init init_soleng_maps(void) { - int nr_parts; + int nr_parts = 0; /* First probe at offset 0 */ soleng_flash_map.map_priv_1 = P2SEGADDR(0); - soleng_eprom_map.map_priv_1 = P1SEGADDR(0x400000); + soleng_eprom_map.map_priv_1 = P1SEGADDR(0x01000000); - printk(KERN_NOTICE "Probing for flash chips at 0x000000:\n"); + printk(KERN_NOTICE "Probing for flash chips at 0x00000000:\n"); flash_mtd = do_map_probe("cfi_probe", &soleng_flash_map); if (!flash_mtd) { /* Not there. Try swapping */ - printk(KERN_NOTICE "Probing for flash chips at 0x400000:\n"); - soleng_flash_map.map_priv_1 = P2SEGADDR(0x400000); + printk(KERN_NOTICE "Probing for flash chips at 0x01000000:\n"); + soleng_flash_map.map_priv_1 = P2SEGADDR(0x01000000); soleng_eprom_map.map_priv_1 = P1SEGADDR(0); flash_mtd = do_map_probe("cfi_probe", &soleng_flash_map); if (!flash_mtd) { @@ -90,9 +109,23 @@ add_mtd_device(eprom_mtd); } +#ifdef CONFIG_MTD_REDBOOT_PARTS nr_parts = parse_redboot_partitions(flash_mtd, &parsed_parts); + if (nr_parts > 0) + printk(KERN_NOTICE "Found RedBoot partition table.\n"); + else if (nr_parts < 0) + printk(KERN_NOTICE "Error looking for RedBoot partitions.\n"); +#endif /* CONFIG_MTD_REDBOOT_PARTS */ +#if CONFIG_MTD_SUPERH_RESERVE + if (nr_parts == 0) { + printk(KERN_NOTICE "Using configured partition at 0x%08x.\n", + CONFIG_MTD_SUPERH_RESERVE); + parsed_parts = superh_se_partitions; + nr_parts = sizeof(superh_se_partitions)/sizeof(*parsed_parts); + } +#endif /* CONFIG_MTD_SUPERH_RESERVE */ - if (nr_parts) + if (nr_parts > 0) add_mtd_partitions(flash_mtd, parsed_parts, nr_parts); else add_mtd_device(flash_mtd); diff -urN orig/drivers/mtd/mtdcore.c linux/drivers/mtd/mtdcore.c --- orig/drivers/mtd/mtdcore.c Sun Oct 14 20:53:09 2001 +++ linux/drivers/mtd/mtdcore.c Tue Jul 30 22:19:19 2002 @@ -1,5 +1,5 @@ /* - * $Id: mtdcore.c,v 1.31 2001/10/02 15:05:11 dwmw2 Exp $ + * $Id: mtdcore.c,v 1.32 2002/03/07 18:38:10 joern Exp $ * * Core registration and callback routines for MTD * drivers and users. @@ -296,7 +296,7 @@ up(&mtd_table_mutex); if (off >= len+begin) return 0; - *start = page + (begin-off); + *start = page + (off-begin); return ((count < begin+len-off) ? count : begin+len-off); } diff -urN orig/drivers/mtd/mtdpart.c linux/drivers/mtd/mtdpart.c --- orig/drivers/mtd/mtdpart.c Sun Oct 14 20:53:09 2001 +++ linux/drivers/mtd/mtdpart.c Tue Jul 30 22:19:59 2002 @@ -5,8 +5,11 @@ * * This code is GPL * - * $Id: mtdpart.c,v 1.23 2001/10/02 15:05:11 dwmw2 Exp $ - */ + * $Id: mtdpart.c,v 1.27 2002/03/08 16:34:35 rkaiser Exp $ + * + * 02-21-2002 Thomas Gleixner + * added support for read_oob, write_oob + */ #include #include @@ -28,6 +31,7 @@ u_int32_t offset; int index; struct list_head list; + int registered; }; /* @@ -54,6 +58,18 @@ len, retlen, buf); } +static int part_read_oob (struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct mtd_part *part = PART(mtd); + if (from >= mtd->size) + len = 0; + else if (from + len > mtd->size) + len = mtd->size - from; + return part->master->read_oob (part->master, from + part->offset, + len, retlen, buf); +} + static int part_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { @@ -68,6 +84,20 @@ len, retlen, buf); } +static int part_write_oob (struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct mtd_part *part = PART(mtd); + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + if (to >= mtd->size) + len = 0; + else if (to + len > mtd->size) + len = mtd->size - to; + return part->master->write_oob (part->master, to + part->offset, + len, retlen, buf); +} + static int part_writev (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen) { @@ -148,7 +178,8 @@ if (slave->master == master) { struct list_head *prev = node->prev; __list_del(prev, node->next); - del_mtd_device(&slave->mtd); + if(slave->registered) + del_mtd_device(&slave->mtd); kfree(slave); node = prev; } @@ -195,21 +226,27 @@ slave->mtd.oobsize = master->oobsize; slave->mtd.ecctype = master->ecctype; slave->mtd.eccsize = master->eccsize; + slave->mtd.read_user_prot_reg = master->read_user_prot_reg; + slave->mtd.read_fact_prot_reg = master->read_fact_prot_reg; + slave->mtd.write_user_prot_reg = master->write_user_prot_reg; slave->mtd.name = parts[i].name; slave->mtd.bank_size = master->bank_size; - slave->mtd.module = master->module; slave->mtd.read = part_read; slave->mtd.write = part_write; + + if (master->read_oob) + slave->mtd.read_oob = part_read_oob; + if (master->write_oob) + slave->mtd.write_oob = part_write_oob; if (master->sync) slave->mtd.sync = part_sync; if (!i && master->suspend && master->resume) { slave->mtd.suspend = part_suspend; slave->mtd.resume = part_resume; } - if (master->writev) slave->mtd.writev = part_writev; if (master->readv) @@ -225,6 +262,15 @@ if (slave->offset == MTDPART_OFS_APPEND) slave->offset = cur_offset; + if (slave->offset == MTDPART_OFS_NXTBLK) { + u_int32_t emask = master->erasesize-1; + slave->offset = (cur_offset + emask) & ~emask; + if (slave->offset != cur_offset) { + printk(KERN_NOTICE "Moving partition %d: " + "0x%08x -> 0x%08x\n", i, + cur_offset, slave->offset); + } + } if (slave->mtd.size == MTDPART_SIZ_FULL) slave->mtd.size = master->size - slave->offset; cur_offset = slave->offset + slave->mtd.size; @@ -279,8 +325,17 @@ parts[i].name); } - /* register our partition */ - add_mtd_device(&slave->mtd); + if(parts[i].mtdp) + { /* store the object pointer (caller may or may not register it */ + *parts[i].mtdp = &slave->mtd; + slave->registered = 0; + } + else + { + /* register our partition */ + add_mtd_device(&slave->mtd); + slave->registered = 1; + } } return 0; diff -urN orig/drivers/net/cs89x0.c linux/drivers/net/cs89x0.c --- orig/drivers/net/cs89x0.c Tue May 27 10:04:44 2003 +++ linux/drivers/net/cs89x0.c Tue May 27 10:11:41 2003 @@ -115,6 +115,7 @@ */ +#include #include #include #include @@ -425,18 +426,18 @@ /* if they give us an odd I/O address, then do ONE write to the address port, to get it back to address zero, where we expect to find the EISA signature word. An IO with a base of 0x3 - will skip the test for the ADD_PORT. */ + will skip the test for the ADD_PORT. */ if (ioaddr & 1) { if (net_debug > 1) printk(KERN_INFO "%s: odd ioaddr 0x%x\n", dev->name, ioaddr); - if ((ioaddr & 2) != 2) + if ((ioaddr & 2) != 2) if ((inw((ioaddr & ~3)+ ADD_PORT) & ADD_MASK) != ADD_SIG) { printk(KERN_ERR "%s: bad signature 0x%x\n", dev->name, inw((ioaddr & ~3)+ ADD_PORT)); retval = -ENODEV; goto out2; } - ioaddr &= ~3; + ioaddr &= ~3; outw(PP_ChipID, ioaddr + ADD_PORT); } printk("PP_addr=0x%x\n", inw(ioaddr + ADD_PORT)); @@ -444,7 +445,7 @@ if (inw(ioaddr + DATA_PORT) != CHIP_EISA_ID_SIG) { printk(KERN_ERR "%s: incorrect signature 0x%x\n", dev->name, inw(ioaddr + DATA_PORT)); - retval = -ENODEV; + retval = -ENODEV; goto out2; } @@ -475,7 +476,7 @@ dev->base_addr); reset_chip(dev); - + /* Here we read the current configuration of the chip. If there is no Extended EEPROM then the idea is to not disturb the chip configuration, it should have been correctly setup by automatic diff -urN orig/drivers/net/irda/irtty.c linux/drivers/net/irda/irtty.c --- orig/drivers/net/irda/irtty.c Mon May 5 17:39:16 2003 +++ linux/drivers/net/irda/irtty.c Mon May 5 21:08:01 2003 @@ -788,45 +788,24 @@ */ static int irtty_set_dtr_rts(struct net_device *dev, int dtr, int rts) { - struct irtty_cb *self; - struct tty_struct *tty; - //unsigned long flags; - mm_segment_t fs; - int arg = 0; + struct irtty_cb *self = (struct irtty_cb *) dev->priv; + int set = 0, clear = 0; - self = (struct irtty_cb *) dev->priv; - tty = self->tty; - - /* Was protected in ioctl handler, but the serial driver doesn't - * like it. This may need to change. - Jean II */ - //spin_lock_irqsave(&self->lock, flags); - -#ifdef TIOCM_OUT2 /* Not defined for ARM */ - arg = TIOCM_OUT2; -#endif if (rts) - arg |= TIOCM_RTS; + set |= TIOCM_RTS; + else + clear |= TIOCM_RTS; if (dtr) - arg |= TIOCM_DTR; + set |= TIOCM_DTR; + else + clear |= TIOCM_DTR; /* - * The ioctl() function, or actually set_modem_info() in serial.c - * expects a pointer to the argument in user space. To hack us - * around this, we use the set_fs() function to fool the routines - * that check if they are called from user space. We also need - * to send a pointer to the argument so get_user() gets happy. DB. + * This function is not yet defined for all tty driver, so + * let's be careful... Jean II */ - - fs = get_fs(); - set_fs(get_ds()); - - /* This is probably unsafe, but currently under discussion - Jean II */ - if (tty->driver->ioctl(tty, NULL, TIOCMSET, (unsigned long) &arg)) { - IRDA_DEBUG(2, "%s(), error doing ioctl!\n", __FUNCTION__); - } - set_fs(fs); - - //spin_unlock_irqrestore(&self->lock, flags); + ASSERT(self->tty->driver->tiocmset != NULL, return -1;); + self->tty->driver->tiocmset(self->tty, NULL, set, clear); return 0; } diff -urN orig/drivers/net/irda/sa1100_ir.c linux/drivers/net/irda/sa1100_ir.c --- orig/drivers/net/irda/sa1100_ir.c Tue May 27 10:04:48 2003 +++ linux/drivers/net/irda/sa1100_ir.c Wed Jun 11 17:15:35 2003 @@ -57,6 +57,8 @@ static int max_rate = 4000000; struct sa1100_irda { + struct net_device netdev; + unsigned char hscr0; unsigned char utcr4; unsigned char power; @@ -357,15 +359,14 @@ */ static int sa1100_irda_suspend(struct device *_dev, u32 state, u32 level) { - struct net_device *dev = dev_get_drvdata(_dev); - struct sa1100_irda *si = dev->priv; + struct sa1100_irda *si = dev_get_drvdata(_dev); if (si && si->open && level == SUSPEND_DISABLE) { /* * Stop the transmit queue */ - netif_device_detach(dev); - disable_irq(dev->irq); + netif_device_detach(&si->netdev); + disable_irq(si->netdev.irq); sa1100_irda_shutdown(si); __sa1100_irda_set_power(si, 0); } @@ -378,8 +379,7 @@ */ static int sa1100_irda_resume(struct device *_dev, u32 level) { - struct net_device *dev = dev_get_drvdata(_dev); - struct sa1100_irda *si = dev->priv; + struct sa1100_irda *si = dev_get_drvdata(_dev); if (si && si->open && level == RESUME_ENABLE) { /* @@ -396,12 +396,12 @@ sa1100_irda_startup(si); __sa1100_irda_set_power(si, si->power); - enable_irq(dev->irq); + enable_irq(si->netdev.irq); /* * This automatically wakes up the queue */ - netif_device_attach(dev); + netif_device_attach(&si->netdev); } return 0; @@ -947,56 +947,53 @@ return io->head ? 0 : -ENOMEM; } -static struct device_driver sa1100ir_driver = { - .name = "sa1100ir", - .bus = &system_bus_type, - .suspend = sa1100_irda_suspend, - .resume = sa1100_irda_resume, -}; - -static struct sys_device sa1100ir_device = { - .name = "sa1100ir", - .id = 0, - .root = NULL, - .dev = { - .name = "Intel Corporation SA11x0 [IrDA]", - .bus_id = "0", - .driver = &sa1100ir_driver, - }, -}; - -static int sa1100_irda_net_init(struct net_device *dev) +static int sa1100_irda_probe(struct device *_dev) { - struct sa1100_irda *si = dev->priv; + struct platform_device *dev = to_platform_device(_dev); + struct sa1100_irda *si; unsigned int baudrate_mask; - int err = -ENOMEM; + int err; + + err = request_mem_region(__PREG(Ser2UTCR0), 0x24, "IrDA") ? 0 : -EBUSY; + if (err) + goto err_mem_1; + err = request_mem_region(__PREG(Ser2HSCR0), 0x1c, "IrDA") ? 0 : -EBUSY; + if (err) + goto err_mem_2; + err = request_mem_region(__PREG(Ser2HSCR2), 0x04, "IrDA") ? 0 : -EBUSY; + if (err) + goto err_mem_3; si = kmalloc(sizeof(struct sa1100_irda), GFP_KERNEL); if (!si) - goto out; - - memset(si, 0, sizeof(*si)); + goto err_mem_4; - si->dev = &sa1100ir_device.dev; + memset(si, 0, sizeof(struct sa1100_irda)); + si->dev = &dev->dev; /* * Initialise the HP-SIR buffers */ err = sa1100_irda_init_iobuf(&si->rx_buff, 14384); if (err) - goto out; + goto err_mem_4; err = sa1100_irda_init_iobuf(&si->tx_buff, 4000); if (err) - goto out_free_rx; + goto err_mem_4; + + SET_MODULE_OWNER(&si->netdev); + SET_NETDEV_DEV(&si->netdev, &dev->dev); - dev->priv = si; - dev->hard_start_xmit = sa1100_irda_hard_xmit; - dev->open = sa1100_irda_start; - dev->stop = sa1100_irda_stop; - dev->do_ioctl = sa1100_irda_ioctl; - dev->get_stats = sa1100_irda_stats; + strcpy(si->netdev.name, "irda%d"); + si->netdev.priv = si; + si->netdev.hard_start_xmit = sa1100_irda_hard_xmit; + si->netdev.open = sa1100_irda_start; + si->netdev.stop = sa1100_irda_stop; + si->netdev.do_ioctl = sa1100_irda_ioctl; + si->netdev.get_stats = sa1100_irda_stats; + si->netdev.irq = IRQ_Ser2ICP; - irda_device_setup(dev); + irda_device_setup(&si->netdev); irda_init_max_qos_capabilies(&si->qos); /* @@ -1006,11 +1003,11 @@ baudrate_mask = IR_9600; switch (max_rate) { - case 4000000: baudrate_mask |= IR_4000000 << 8; - case 115200: baudrate_mask |= IR_115200; - case 57600: baudrate_mask |= IR_57600; - case 38400: baudrate_mask |= IR_38400; - case 19200: baudrate_mask |= IR_19200; + case 4000000: baudrate_mask |= IR_4000000 << 8; + case 115200: baudrate_mask |= IR_115200; + case 57600: baudrate_mask |= IR_57600; + case 38400: baudrate_mask |= IR_38400; + case 19200: baudrate_mask |= IR_19200; } si->qos.baud_rate.bits &= baudrate_mask; @@ -1030,42 +1027,70 @@ Ser2UTCR4 = si->utcr4; Ser2HSCR0 = HSCR0_UART; - return 0; - - kfree(si->tx_buff.head); -out_free_rx: - kfree(si->rx_buff.head); -out: - kfree(si); + err = register_netdev(&si->netdev); + if (err == 0) + dev_set_drvdata(&dev->dev, si); + if (err) { + err_mem_4: + if (si) { + kfree(si->tx_buff.head); + kfree(si->rx_buff.head); + kfree(si); + } + release_mem_region(__PREG(Ser2HSCR2), 0x04); + err_mem_3: + release_mem_region(__PREG(Ser2HSCR0), 0x1c); + err_mem_2: + release_mem_region(__PREG(Ser2UTCR0), 0x24); + } + err_mem_1: return err; } -/* - * Remove all traces of this driver module from the kernel, so we can't be - * called. Note that the device has already been stopped, so we don't have - * to worry about interrupts or dma. - */ -static void sa1100_irda_net_uninit(struct net_device *dev) +static int sa1100_irda_remove(struct device *_dev) { - struct sa1100_irda *si = dev->priv; + struct sa1100_irda *si = dev_get_drvdata(_dev); + + if (si) { + unregister_netdev(&si->netdev); + kfree(si->tx_buff.head); + kfree(si->rx_buff.head); + kfree(si); + } + + release_mem_region(__PREG(Ser2HSCR2), 0x04); + release_mem_region(__PREG(Ser2HSCR0), 0x1c); + release_mem_region(__PREG(Ser2UTCR0), 0x24); - dev->hard_start_xmit = NULL; - dev->open = NULL; - dev->stop = NULL; - dev->do_ioctl = NULL; - dev->get_stats = NULL; - dev->priv = NULL; - - kfree(si->tx_buff.head); - kfree(si->rx_buff.head); - kfree(si); + /* + * We now know that the netdevice is no longer in use, and all + * references to our driver have been removed. The only structure + * which may still be present is the netdevice, which will get + * cleaned up by net/core/dev.c + */ } +static struct device_driver sa1100ir_driver = { + .name = "sa11x0-ir", + .bus = &platform_bus_type, + .probe = sa1100_irda_probe, + .remove = sa1100_irda_remove, + .suspend = sa1100_irda_suspend, + .resume = sa1100_irda_resume, +}; + +static struct platform_device sa1100ir_device = { + .name = "sa11x0-ir", + .id = 0, + .dev = { + .name = "Intel Corporation SA11x0 [IrDA]", + }, +}; + static int __init sa1100_irda_init(void) { - struct net_device *dev; - int err; + int ret; /* * Limit power level a sensible range. @@ -1075,72 +1100,19 @@ if (power_level > 3) power_level = 3; - err = request_mem_region(__PREG(Ser2UTCR0), 0x24, "IrDA") ? 0 : -EBUSY; - if (err) - goto err_mem_1; - err = request_mem_region(__PREG(Ser2HSCR0), 0x1c, "IrDA") ? 0 : -EBUSY; - if (err) - goto err_mem_2; - err = request_mem_region(__PREG(Ser2HSCR2), 0x04, "IrDA") ? 0 : -EBUSY; - if (err) - goto err_mem_3; - - driver_register(&sa1100ir_driver); - sys_device_register(&sa1100ir_device); - - rtnl_lock(); - dev = dev_alloc("irda%d", &err); - if (dev) { - dev->irq = IRQ_Ser2ICP; - dev->init = sa1100_irda_net_init; - dev->uninit = sa1100_irda_net_uninit; - - err = register_netdevice(dev); - - if (err) - kfree(dev); - else - dev_set_drvdata(&sa1100ir_device.dev, dev); - } - rtnl_unlock(); - - if (err) { - sys_device_unregister(&sa1100ir_device); - driver_unregister(&sa1100ir_driver); - - release_mem_region(__PREG(Ser2HSCR2), 0x04); -err_mem_3: - release_mem_region(__PREG(Ser2HSCR0), 0x1c); -err_mem_2: - release_mem_region(__PREG(Ser2UTCR0), 0x24); + ret = driver_register(&sa1100ir_driver); + if (ret == 0) { + ret = platform_device_register(&sa1100ir_device); + if (ret) + driver_unregister(&sa1100ir_driver); } -err_mem_1: - return err; + return ret; } static void __exit sa1100_irda_exit(void) { - struct net_device *dev = dev_get_drvdata(&sa1100ir_device.dev); - - if (dev) { - rtnl_lock(); - unregister_netdevice(dev); - rtnl_unlock(); - } - - sys_device_unregister(&sa1100ir_device); driver_unregister(&sa1100ir_driver); - - release_mem_region(__PREG(Ser2HSCR2), 0x04); - release_mem_region(__PREG(Ser2HSCR0), 0x1c); - release_mem_region(__PREG(Ser2UTCR0), 0x24); - - /* - * We now know that the netdevice is no longer in use, and all - * references to our driver have been removed. The only structure - * which may still be present is the netdevice, which will get - * cleaned up by net/core/dev.c - */ + platform_device_unregister(&sa1100ir_device); } static int __init sa1100ir_setup(char *line) diff -urN orig/drivers/net/irda/w83977af_ir.c linux/drivers/net/irda/w83977af_ir.c --- orig/drivers/net/irda/w83977af_ir.c Tue May 27 10:04:48 2003 +++ linux/drivers/net/irda/w83977af_ir.c Tue May 27 10:11:55 2003 @@ -196,7 +196,7 @@ /* FIXME: The HP HDLS-1100 does not support 1152000! */ self->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600| - IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8); + IR_115200/*|IR_576000|IR_1152000|(IR_4000000 << 8)*/; /* The HP HDLS-1100 needs 1 ms according to the specs */ self->qos.min_turn_time.bits = qos_mtt_bits; @@ -902,7 +902,7 @@ skb = dev_alloc_skb(len+1); if (skb == NULL) { - printk(KERN_INFO + printk(KERN_INFO "%s(), memory squeeze, dropping frame.\n", __FUNCTION__); /* Restore set register */ outb(set, iobase+SSR); @@ -1345,12 +1345,12 @@ IRDA_DEBUG(2, "%s(), %s, (cmd=0x%X)\n", __FUNCTION__ , dev->name, cmd); spin_lock_irqsave(&self->lock, flags); - + switch (cmd) { case SIOCSBANDWIDTH: /* Set bandwidth */ if (!capable(CAP_NET_ADMIN)) { ret = -EPERM; - goto out; + break; } w83977af_change_speed(self, irq->ifr_baudrate); break; diff -urN orig/drivers/net/pcmcia/Kconfig linux/drivers/net/pcmcia/Kconfig --- orig/drivers/net/pcmcia/Kconfig Mon May 5 17:39:17 2003 +++ linux/drivers/net/pcmcia/Kconfig Mon May 5 17:42:36 2003 @@ -153,5 +153,9 @@ The module will be called ibmtr_cs. If you want to compile it as a module, say M here and read . +config CONFIG_PCMCIA_WVLAN + tristate "AT&T/Lucent Wavelan IEEE 802.11 wireless support" + depends on NET_PCMCIA_RADIO && PCMCIA + endmenu diff -urN orig/drivers/net/pcmcia/Makefile linux/drivers/net/pcmcia/Makefile --- orig/drivers/net/pcmcia/Makefile Wed Mar 5 19:45:28 2003 +++ linux/drivers/net/pcmcia/Makefile Wed Mar 5 10:10:17 2003 @@ -13,4 +13,11 @@ obj-$(CONFIG_ARCNET_COM20020_CS)+= com20020_cs.o obj-$(CONFIG_PCMCIA_AXNET) += axnet_cs.o +# 16-bit wireless client drivers +obj-$(CONFIG_PCMCIA_WVLAN) += wvlan_cs.o + +wvlan_cs-objs = wvlan.o wvlan_hcf.o wvlan_hcfio.o + +CFLAGS_wvlan.o = -DPCMCIA + obj-$(CONFIG_PCMCIA_IBMTR) += ibmtr_cs.o diff -urN orig/drivers/net/pcmcia/pcnet_cs.c linux/drivers/net/pcmcia/pcnet_cs.c --- orig/drivers/net/pcmcia/pcnet_cs.c Mon May 5 17:39:20 2003 +++ linux/drivers/net/pcmcia/pcnet_cs.c Sat May 17 22:53:23 2003 @@ -1620,16 +1620,7 @@ static int __init init_pcnet_cs(void) { - servinfo_t serv; - DEBUG(0, "%s\n", version); - CardServices(GetCardServicesInfo, &serv); - if (serv.Revision != CS_RELEASE_CODE) { - printk(KERN_NOTICE "pcnet_cs: Card Services release " - "does not match!\n"); - return -EINVAL; - } - pcmcia_register_driver(&pcnet_driver); - return 0; + return pcmcia_register_driver(&pcnet_driver); } static void __exit exit_pcnet_cs(void) diff -urN orig/drivers/net/pcmcia/wvlan.c linux/drivers/net/pcmcia/wvlan.c --- orig/drivers/net/pcmcia/wvlan.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/net/pcmcia/wvlan.c Wed Oct 23 11:18:26 2002 @@ -0,0 +1,3390 @@ +/******************************************************************** + * WaveLAN/IEEE PCMCIA Linux network device driver + * + * by Andreas Neuhaus + * http://www.fasta.fh-dortmund.de/users/andy/wvlan/ + * + * This driver is free software; you can redistribute and/or + * modify it under the terms of the GNU General Public License; + * either version 2 of the license, or (at your option) any + * later version. + * Please send comments/bugfixes/patches to the above address. + * + * Based on the Linux PCMCIA Programmer's Guide + * and Lucent Technologies' HCF-Light library (wvlan47). + * + * See 'man 4 wvlan_cs' for more information. + * + * TODO + * We should use multiple Tx buffers to gain performance. + * Have a closer look to the endianess (PPC problems). + * + * HISTORY + * v1.0.7 11/01/2000 - Jean II and Anton Blanchard + * Dont hold spinlock during copy_{to,from}_user, we might + * sleep. (brought to my attention by Dave Gibson) + * New Ad-Hoc mode accepts ESSID=any (Jean II) + * Support new wireless extension - Transmit Power (Jean II) + * Added support for 3.3V cards, fixed IPv6 (David) + * + * v1.0.6 7/12/2000 - David Hinds, Anton Blanchard, Jean II and others + * Endianess fix for PPC users, first try (David) + * Fix some ranges (power management, key size, ...) (me) + * Auto rate up to a maximum (for ex. up to 5.5) (me) + * Remove error on IW_ENCODE_RESTRICTED (me) + * Better error messages to catch stupid users (me) + * --- + * Oups ! Firmware 6.06 *do* support port3 (me) + * Oups ! ibss mode is not fully functional in firmware 6.04 (me) + * Match Windows driver for selecting Ad-Hoc mode (me) + * Get MAC address earlier, so that if module parameters are + * invalid, we can still use wireless.opts... (me) + * Use ethX and not wvlanX by default (me) + * Minimal support for some PrismII cards (me) + * Check out of bound in getratelist (Paul Mackerras) + * Finish up the endianess fix for PPC and test it + * (Paul Mackerras and Hugh Blemings) + * Check Cabletron Firmware 4.32 support (Anton) + * + * v1.0.5 19/10/2000 - David Hinds, Jean II and others + * Support for 6.00 firmware (remove fragmentation - ? + me) + * Add Microwave Oven Robustness support (me) + * Fix a bug preventing RARP from working (?) + * --- + * Fix SMP support (fix all those spinlocks - me) + * Add IBSS support, to enable 802.11 ad-hoc mode (Ross Finlayson) + * Integrate IBSS support with Wireless Extensions (me) + * Clean-up Wireless Extensions (#define and other stuff - me) + * Multi-card support for Wireless Extensions (me) + * Firmware madness support - Arghhhhh !!!!!! (me) + * --- + * Proper firmware detection routines (me) + * Aggregate configuration change when closed (me) + * wireless.opts now works on old firmware (me) + * Integrate MWO robust to frag setting (me) + * copy_to/from in ioctl with irq on (me, requested by Alan) + * Add a few "static" and "inline" there and there (me) + * Update to new module init/cleanup procedures (me) + * + * v1.0.4 2000/02/26 + * Some changes to fit into kernel 2.3.x. + * Some changes to better fit into the new net API. + * Use of spinlocks for disabling interrupts. + * Conditional to allow ignoring tx timeouts. + * Disable interrupts during config/shutdown/reset. + * Credits go to Jean Tourrilhes for all the following: + * Promiscuous mode (tcpdump now work correctly). + * Set multicast Rx list (RTP now work correctly). + * Hook up watchdog timer in new net API. + * Update frag/rts to new W-Ext. + * Operating mode support + W-Ext (Neat...). + * Power Saving support + W-Ext (useless...). + * WEP (Privacy - Silver+Gold) support + W-Ext (yeah !!!). + * Disable interupts during reading wireless stats. + * (New quality indicator not included, need more work) + * Plus a few cleanups, comments and fixes... + * + * v1.0.3 Skipped to not confuse with kernel 2.3.x driver + * + * v1.0.2 2000/01/07 + * Merged driver into the PCMCIA Card Services package + * (thanks to David Hinds). + * Removed README, added man page (man 4 wvlan_cs). + * + * v1.0.1 1999/09/02 + * Interrupts are now disabled during ioctl to prevent being + * disturbed during our fiddling with the NIC (occurred + * when using wireless tools while heavy loaded link). + * Fixed a problem with more than 6 spy addresses (thanks to + * Thomas Ekstrom). + * Hopefully fixed problems with bigger packet sizes than 1500. + * When you changed parameters that were specified at module + * load time later with wireless_tools and the card was + * reset afterward (e.g. by a Tx timeout), all changes + * were lost. Changes will stay now after a reset. + * Rewrote some parts of this README, added card LEDs description. + * Added medium_reservation, ap_density, frag_threshold and + * transmit_rate to module parameters. + * Applying the patch now also modifies the files SUPPORTED.CARDS + * and MAINTAINERS. + * Signal/noise levels are now reported in dBm (-102..-10). + * Added support for the new wireless extension (get wireless_ + * tools 19). Credits go to Jean Tourrilhes for all + * the following: + * Setting channel by giving frequency value is now available. + * Setting/getting ESSID/BSSID/station-name is now possible + * via iwconfig. + * Support to set/get the desired/current bitrate. + * Support to set/get RTS threshold. + * Support to set/get fragmentation threshold. + * Support to set/get AP density. + * Support to set/get port type. + * Fixed a problem with ioctl calls when setting station/network + * name, where the new name string wasn't in kernel space + * (thanks to Danton Nunes). + * Driver sucessful tested on AlphaLinux (thanks to Peter Olson). + * Driver sucessful tested with WaveLAN Turbo cards. + * + * v0.2.7 1999/07/20 + * Changed release/detach code to fix hotswapping with 2.2/2.3 + * kernels (thanks to Derrick J Brashear). + * Fixed wrong adjustment of receive buffer length. This was only + * a problem when a higher level protocol relies on correct + * length information, so it never occurred with IPv4 + * (thanks to Henrik Gulbrandsen). + * + * v0.2.6 1999/05/04 + * Added wireless spy and histogram support. Signal levels + * are now reported in ad-hoc mode correctly, but you + * need to use iwspy for it, because we can 'hear' more + * than one remote host in ad-hoc mode (thanks + * to Albert K T Hui for the code and to Richard van + * Leeuwen for the technical details). + * Fixed a bug with wrong tx_bytes count. + * Added GPL file wvlan.COPYING. + * + * v0.2.5 1999/03/12 + * Hopefully fixed problems with the Makefile patch. + * Changed the interrupt service routine to do never lock up + * in an endless loop (if this ever would happen...). + * Missed a conditional which made the driver unable to compile + * on 2.0.x kernels (thanks to Glenn D. Golden). + * + * v0.2.4 1999/03/10 + * Tested in ad-hoc mode and with access point (many thanks + * to Frank Bruegmann, who made some hardware available + * to me so that I can now test it myself). + * Change the interrupt service routine to repeat on frame + * reception and deallocate the NICs receiving frame + * buffer correctly (thanks to Glenn D. Golden). + * Fixed a problem with checksums where under some circumstances + * an incorrect packet wasn't recognized. Switched + * on the kernel checksum checking (thanks to Glenn D. Golden). + * Setting the channel value is now checked against valid channel + * values which are read from the card. + * Added private ioctl (iwconfig priv) station_name, network_name + * and current_network. It needs an iwconfig capable of + * setting and gettings strings (thanks to Justin Seger). + * Ioctl (iwconfig) should now return the real current used channel. + * Setting the channel value is now only valid using ad-hoc mode. + * It's useless when using an access points. + * Renamed the ssid parameter to network_name and made it work + * correctly for all port_types. It should work now + * in ad-hoc networks as well as with access points. + * Added entries for the NCR WaveLAN/IEEE and the Cabletron + * RoamAbout 802.11 DS card (thanks to Richard van Leeuwen) + * Support to count the received and transmitted bytes + * if kernel version >2.1.25. + * Changed the reset method in case of Tx-timeouts. + * PM suspend/resume should work now (thanks to Dave Kristol). + * Changed installation and driver package. Read INSTALL in this + * file for information how it works now. + * + * v0.2.3 1999/02/25 + * Added support to set the own SSID + * Changed standard channel setting to 3 so that it works + * with Windows without specifying a channel (the + * Windows driver seem to default to channel 3). + * Fixed two problems with the Ethernet-II frame de/encapsulation. + * + * v0.2.2 1999/02/07 + * First public beta release. + * Added support to get link quality via iwconfig. + * Added support to change channel via iwconfig. + * Added changeable MTU setting (thanks to Tomasz Motylewski). + * Added Ethernet-II frame de/encapsulation, because + * HCF-Light doesn't support it. + * + * v0.2.1 1999/02/03 + * Added channel parameter. + * Rewrote the driver with information made public + * in Lucent's HCF-Light library. The HCF was + * slightly modified to get rid of the compiler + * warnings. The filenames were prefixed with + * wvlan_ to better fit into the pcmcia package. + * + * v0.1d 1998/12/21 + * Fixed a problem where the NIC was crashing during heavy + * loaded transmissions. Interrupts are now disabled + * during wvlan_tx() function. Seems to work fine now. + * + * v0.1c 1998/12/20 + * Driver works fine with ad-hoc network. + * + * v0.1b 1998/12/19 + * First successful send-tests. + * + * v0.1a 1998/12/18 + * First tests with card functions. + */ + +#include +#include +#ifndef KERNEL_VERSION +#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) +#endif + +#ifdef __IN_PCMCIA_PACKAGE__ +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + + +#include +/* Note : v6 is included in : 2.0.37+ and 2.2.4+, adds ESSID */ +/* Note : v8 is included in : 2.2.11+ and 2.3.14+, adds frag/rts/rate/nick */ +/* Note : v9 is included in : 2.2.14+ and 2.3.25+, adds mode/ps/wep */ +#if WIRELESS_EXT < 6 +#warning "Wireless extension v8 or newer required - please upgrade your kernel" +#undef WIRELESS_EXT +#endif +#if WIRELESS_EXT < 9 +#warning "Wireless extension v9 or newer prefered - please upgrade your kernel" +#endif +#define WIRELESS_SPY // enable iwspy support +#undef HISTOGRAM // disable histogram of signal levels + +// This is needed for station_name, but we may not compile WIRELESS_EXT +#ifndef IW_ESSID_MAX_SIZE +#define IW_ESSID_MAX_SIZE 32 +#endif /* IW_ESSID_MAX_SIZE */ + +#include "wvlan_hcf.h" + +/* #define PCMCIA_DEBUG 1 // For developer only :-) */ + +// Undefine this if you want to ignore Tx timeouts +// (i.e. card will not be reset on Tx timeouts) +#define WVLAN_RESET_ON_TX_TIMEOUT + + +/******************************************************************** + * DEBUG + */ +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +MODULE_PARM(pc_debug, "i"); +#define DEBUG(n, args...) if (pc_debug>=(n)) printk(KERN_DEBUG args); +#else +#define DEBUG(n, args...) {} +#endif +#define DEBUG_INFO 1 +#define DEBUG_NOISY 2 +#define DEBUG_TXRX 3 +#define DEBUG_CALLTRACE 4 +#define DEBUG_INTERRUPT 5 + +/******************************************************************** + * MISC + */ +static char *version = "1.0.6"; +static dev_info_t dev_info = "wvlan_cs"; +static dev_link_t *dev_list = NULL; + +// Module parameters +static u_int irq_mask = 0xdeb8; // Interrupt mask +static int irq_list[4] = { -1 }; // Interrupt list (alternative) +static int eth = 1; // use ethX devname +static int mtu = 1500; +// Note : the following parameters can be also modified through Wireless +// Extension, and additional parameters are also available this way... +static int port_type = 1; // Port-type [1] +static int allow_ibss = 0; // Allow a IBSS [0] +static char network_name[IW_ESSID_MAX_SIZE+1] = "\0"; // Name of network [] +static int channel = 3; // Channel [3] +MODULE_PARM(irq_mask, "i"); +MODULE_PARM(irq_list, "1-4i"); +MODULE_PARM(eth, "i"); +MODULE_PARM(mtu, "i"); +MODULE_PARM(port_type, "i"); +MODULE_PARM(allow_ibss, "i"); +MODULE_PARM(network_name, "c" __MODULE_STRING(IW_ESSID_MAX_SIZE)); +MODULE_PARM(channel, "i"); +// Backward compatibility - This one is obsolete and will be removed soon +static char station_name[IW_ESSID_MAX_SIZE+1] = "\0"; // Name of station [] +MODULE_PARM(station_name, "c" __MODULE_STRING(IW_ESSID_MAX_SIZE)); + +// Useful macros we have in pcmcia-cs but not in the kernel +#ifndef __IN_PCMCIA_PACKAGE__ +#define DEV_KFREE_SKB(skb) dev_kfree_skb(skb); +#define skb_tx_check(dev, skb) +#define add_rx_bytes(stats, n) (stats)->rx_bytes += n; +#define add_tx_bytes(stats, n) (stats)->tx_bytes += n; +#endif + +// Ethernet timeout is ((400*HZ)/1000), but we should use a higher +// value, because wireless transmissions are much slower +#define TX_TIMEOUT ((4000*HZ)/1000) + +// Ethernet-II snap header +static char snap_header[] __attribute__((aligned(2))) = + { 0x00, 0x00, 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; + +// Valid MTU values (HCF_MAX_MSG (2304) is the max hardware frame size) +#define WVLAN_MIN_MTU 256 +#define WVLAN_MAX_MTU (HCF_MAX_MSG - sizeof(snap_header)) + +// Max number of multicast addresses that the filter can accept +#define WVLAN_MAX_MULTICAST GROUP_ADDR_SIZE/6 + +// Frequency list (map channels to frequencies) +const long frequency_list[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442, + 2447, 2452, 2457, 2462, 2467, 2472, 2484 }; + +// Bit-rate list in 1/2 Mb/s (first is dummy - not for original turbo) +static const int rate_list[] = { 0, 2, 4, -22, 11, 22, -4, -11, 0, 0, 0, 0 }; + +// A few details needed for WEP (Wireless Equivalent Privacy) +#define MAX_KEY_SIZE 13 // 128/104 (?) bits +#define MIN_KEY_SIZE 5 // 40 bits RC4 - WEP +#define MAX_KEYS 4 // 4 different keys + +// Keep track of wvlanX devices +#define MAX_WVLAN_CARDS 16 +static struct net_device *wvlandev_index[MAX_WVLAN_CARDS]; + +// Local data for netdevice +struct net_local { + dev_node_t node; + struct net_device *dev; // backtrack device + dev_link_t *link; // backtrack link + spinlock_t slock; // spinlock + int interrupt; // interrupt + IFB_STRCT ifb; // WaveLAN HCF structure + struct net_device_stats stats; // device stats + u_char promiscuous; // Promiscuous mode + u_char allmulticast; // All multicast mode + int mc_count; // Number of multicast addrs + int need_commit; // Need to set config + /* Capabilities : what the firmware do support */ + int has_port3; // Ad-Hoc demo mode + int has_ibssid; // IBSS Ad-Hoc mode + int has_mwo; // MWO robust support + int has_wep; // Lucent WEP support + int has_pwep; // Prism WEP support + int has_pm; // Power Management support + /* Configuration : what is the current state of the hardware */ + int port_type; // Port-type [1] + int allow_ibss; // Allow a IBSS [0] + char network_name[IW_ESSID_MAX_SIZE+1]; // Name of network [] + int channel; // Channel [3] +#ifdef WIRELESS_EXT + char station_name[IW_ESSID_MAX_SIZE+1]; // Name of station [] + int ap_density; // AP density [1] + int medium_reservation; // RTS threshold [2347] + int frag_threshold; // Frag. threshold [2346] + int mwo_robust; // MWO robustness [0] + int transmit_rate; // Transmit rate [3] + int wep_on; // WEP enabled + int transmit_key; // Key used for transmissions + KEY_STRCT key[MAX_KEYS]; // WEP keys & size + int pm_on; // Power Management enabled + int pm_multi; // Receive multicasts + int pm_period; // Power Management period +#ifdef WIRELESS_SPY + int spy_number; + u_char spy_address[IW_MAX_SPY][MAC_ADDR_SIZE]; + struct iw_quality spy_stat[IW_MAX_SPY]; +#endif +#ifdef HISTOGRAM + int his_number; + u_char his_range[16]; + u_long his_sum[16]; +#endif + struct iw_statistics wstats; // wireless stats +#endif /* WIRELESS_EXT */ +}; + +// Shortcuts +#ifdef WIRELESS_EXT +typedef struct iw_statistics iw_stats; +typedef struct iw_quality iw_qual; +typedef struct iw_freq iw_freq; +#endif /* WIRELESS_EXT */ + +// Show CardServices error message (syslog) +static void cs_error (client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); +} + + +/******************************************************************** + * FUNCTION PROTOTYPES + */ +static int wvlan_hw_setmaxdatalen (IFBP ifbp, int maxlen); +static int wvlan_hw_getmacaddr (IFBP ifbp, char *mac, int len); +static int wvlan_hw_getchannellist (IFBP ifbp); +static int wvlan_hw_setporttype (IFBP ifbp, int ptype); +static int wvlan_hw_getporttype (IFBP ifbp); +static int wvlan_hw_setallowibssflag (IFBP ifbp, int flag); +static int wvlan_hw_getallowibssflag (IFBP ifbp); +static int wvlan_hw_setstationname (IFBP ifbp, char *name); +static int wvlan_hw_getstationname (IFBP ifbp, char *name, int len); +static int wvlan_hw_setssid (IFBP ifbp, char *name, int ptype); +static int wvlan_hw_getssid (IFBP ifbp, char *name, int len, int cur, int ptype); +static int wvlan_hw_getbssid (IFBP ifbp, char *mac, int len); +static int wvlan_hw_setchannel (IFBP ifbp, int channel); +static int wvlan_hw_getchannel (IFBP ifbp); +static int wvlan_hw_getcurrentchannel (IFBP ifbp); +static int wvlan_hw_setthreshold (IFBP ifbp, int thrh, int cmd); +static int wvlan_hw_getthreshold (IFBP ifbp, int cmd); +static int wvlan_hw_setbitrate (IFBP ifbp, int brate); +static int wvlan_hw_getbitrate (IFBP ifbp, int cur); +static int wvlan_hw_getratelist (IFBP ifbp, char *brlist, int len); +#ifdef WIRELESS_EXT +static int wvlan_hw_getfrequencylist (IFBP ifbp, iw_freq *list, int max); +static int wvlan_getbitratelist (IFBP ifbp, __s32 *list, int max); +static int wvlan_hw_setpower (IFBP ifbp, int enabled, int cmd); +static int wvlan_hw_getpower (IFBP ifbp, int cmd); +static int wvlan_hw_setpmsleep (IFBP ifbp, int duration); +static int wvlan_hw_getpmsleep (IFBP ifbp); +static int wvlan_hw_getprivacy (IFBP ifbp); +static int wvlan_hw_setprivacy (IFBP ifbp, int mode, int transmit, KEY_STRCT *keys); +#endif /* WIRELESS_EXT */ +static int wvlan_hw_setpromisc (IFBP ifbp, int promisc); +static int wvlan_hw_getfirmware (IFBP ifbp, int *first, int *major, int *minor); + +static int wvlan_hw_config (struct net_device *dev); +static int wvlan_hw_shutdown (struct net_device *dev); +static int wvlan_hw_reset (struct net_device *dev); + +struct net_device_stats *wvlan_get_stats (struct net_device *dev); +#ifdef WIRELESS_EXT +int wvlan_ioctl (struct net_device *dev, struct ifreq *rq, int cmd); +struct iw_statistics *wvlan_get_wireless_stats (struct net_device *dev); +#ifdef WIRELESS_SPY +static inline void wvlan_spy_gather (struct net_device *dev, u_char *mac, u_char *stats); +#endif +#ifdef HISTOGRAM +static inline void wvlan_his_gather (struct net_device *dev, u_char *stats); +#endif +#endif /* WIRELESS_EXT */ +int wvlan_change_mtu (struct net_device *dev, int new_mtu); +static void wvlan_set_multicast_list (struct net_device *dev); + +static void wvlan_watchdog (struct net_device *dev); +int wvlan_tx (struct sk_buff *skb, struct net_device *dev); +void wvlan_rx (struct net_device *dev, int len); + +static int wvlan_open (struct net_device *dev); +static int wvlan_close (struct net_device *dev); + +static void wvlan_interrupt (int irq, void *dev_id, struct pt_regs *regs); + +static int wvlan_config (dev_link_t *link); +static void wvlan_release (u_long arg); + +static dev_link_t *wvlan_attach (void); +static void wvlan_detach (dev_link_t *link); + +static int wvlan_event (event_t event, int priority, event_callback_args_t *args); + +extern int init_wvlan_cs (void); +extern void exit_wvlan_cs (void); + + +/******************************************************************** + * HARDWARE SETTINGS + */ +/* Note : most function below are called once in the code, so I added + * the "inline" modifier. If a function is used more than once, please + * remove the "inline"... + * Jean II */ +// Stupid constants helping clarity +#define WVLAN_CURRENT 1 +#define WVLAN_DESIRED 0 + +static inline int wvlan_hw_setmaxdatalen (IFBP ifbp, int maxlen) +{ + CFG_ID_STRCT ltv; + int rc; + + ltv.len = 2; + ltv.typ = CFG_CNF_MAX_DATA_LEN; + ltv.id[0] = cpu_to_le16(maxlen); + rc = hcf_put_info(ifbp, (LTVP) <v); + DEBUG(DEBUG_NOISY, "%s: hcf_put_info(CFG_CNF_MAX_DATA_LEN:0x%x) returned 0x%x\n", dev_info, maxlen, rc); + return rc; +} + +static inline int wvlan_hw_getmacaddr (IFBP ifbp, char *mac, int len) +{ + CFG_MAC_ADDR_STRCT ltv; + int rc, l; + + ltv.len = 4; + ltv.typ = CFG_CNF_OWN_MAC_ADDR; + rc = hcf_get_info(ifbp, (LTVP) <v); + DEBUG(DEBUG_NOISY, "%s: hcf_get_info(CFG_CNF_OWN_MAC_ADDR) returned 0x%x\n", dev_info, rc); + if (rc) + return rc; + l = min(len, ltv.len*2); + memcpy(mac, (char *)ltv.mac_addr, l); + return 0; +} + +static int wvlan_hw_getchannellist (IFBP ifbp) +{ + CFG_ID_STRCT ltv; + int rc, chlist; + + ltv.len = 2; + ltv.typ = CFG_CHANNEL_LIST; + rc = hcf_get_info(ifbp, (LTVP) <v); + chlist = le16_to_cpup(<v.id[0]); + DEBUG(DEBUG_NOISY, "%s: hcf_get_info(CFG_CHANNEL_LIST):0x%x returned 0x%x\n", dev_info, chlist, rc); + return rc ? 0 : chlist; +} + +static inline int wvlan_hw_setporttype (IFBP ifbp, int ptype) +{ + CFG_ID_STRCT ltv; + int rc; + + ltv.len = 2; + ltv.typ = CFG_CNF_PORT_TYPE; + ltv.id[0] = cpu_to_le16(ptype); + rc = hcf_put_info(ifbp, (LTVP) <v); + DEBUG(DEBUG_NOISY, "%s: hcf_put_info(CFG_CNF_PORT_TYPE:0x%x) returned 0x%x\n", dev_info, ptype, rc); + return rc; +} + +static inline int wvlan_hw_getporttype (IFBP ifbp) +{ + CFG_ID_STRCT ltv; + int rc, ptype; + + ltv.len = 2; + ltv.typ = CFG_CNF_PORT_TYPE; + rc = hcf_get_info(ifbp, (LTVP) <v); + ptype = le16_to_cpup(<v.id[0]); + DEBUG(DEBUG_NOISY, "%s: hcf_get_info(CFG_CNF_PORT_TYPE):0x%x returned 0x%x\n", dev_info, ptype, rc); + return rc ? 0 : ptype; +} + +static inline int wvlan_hw_setallowibssflag (IFBP ifbp, int flag) +{ + CFG_ID_STRCT ltv; + int rc; + + ltv.len = 2; + ltv.typ = CFG_CREATE_IBSS; + ltv.id[0] = cpu_to_le16(flag); + rc = hcf_put_info(ifbp, (LTVP) <v); + DEBUG(DEBUG_NOISY, "%s: hcf_put_info(CFG_CREATE_IBSS:0x%x) returned 0x%x\n", dev_info, flag, rc); + return rc; +} + +static inline int wvlan_hw_getallowibssflag (IFBP ifbp) +{ + CFG_ID_STRCT ltv; + int rc, flag; + + ltv.len = 2; + ltv.typ = CFG_CREATE_IBSS; + rc = hcf_get_info(ifbp, (LTVP) <v); + flag = le16_to_cpup(<v.id[0]); + DEBUG(DEBUG_NOISY, "%s: hcf_get_info(CFG_CREATE_IBSS):0x%x returned 0x%x\n", dev_info, flag, rc); + return rc ? 0 : flag; +} + +static inline int wvlan_hw_setstationname (IFBP ifbp, char *name) +{ + CFG_ID_STRCT ltv; + int rc, l; + + ltv.len = 18; + ltv.typ = CFG_CNF_OWN_NAME; + l = min_t(int, strlen(name), ltv.len*2); + ltv.id[0] = cpu_to_le16(l); + memcpy((char *) <v.id[1], name, l); + rc = hcf_put_info(ifbp, (LTVP) <v); + DEBUG(DEBUG_NOISY, "%s: hcf_put_info(CFG_CNF_OWN_NAME:'%s') returned 0x%x\n", dev_info, name, rc); + return rc; +} + +static inline int wvlan_hw_getstationname (IFBP ifbp, char *name, int len) +{ + CFG_ID_STRCT ltv; + int rc, l; + + ltv.len = 18; + ltv.typ = CFG_CNF_OWN_NAME; + rc = hcf_get_info(ifbp, (LTVP) <v); + DEBUG(DEBUG_NOISY, "%s: hcf_get_info(CFG_CNF_OWN_NAME) returned 0x%x\n", dev_info, rc); + if (rc) + return rc; + l = le16_to_cpup(<v.id[0]); + if (l) + l = min(len, l); + else + l = min(len, ltv.len*2); /* It's a feature */ + memcpy(name, (char *) <v.id[1], l); + name[l] = 0; + DEBUG(DEBUG_NOISY, "%s: hcf_get_info(CFG_CNF_OWN_NAME):'%s'\n", dev_info, name); + return 0; +} + +static inline int wvlan_hw_setssid (IFBP ifbp, char *name, int ptype) +{ + CFG_ID_STRCT ltv; + int rc, l; + + ltv.len = 18; + if (ptype == 3) + ltv.typ = CFG_CNF_OWN_SSID; + else + ltv.typ = CFG_CNF_DESIRED_SSID; + l = min_t(int, strlen(name), ltv.len*2); + ltv.id[0] = cpu_to_le16(l); + memcpy((char *) <v.id[1], name, l); + rc = hcf_put_info(ifbp, (LTVP) <v); + DEBUG(DEBUG_NOISY, "%s: hcf_put_info(CFG_CNF_OWN/DESIRED_SSID:'%s') returned 0x%x\n", dev_info, name, rc); + return rc; +} + +static int wvlan_hw_getssid (IFBP ifbp, char *name, int len, int cur, int ptype) +{ + CFG_ID_STRCT ltv; + int rc, l; + + ltv.len = 18; + if (cur) + ltv.typ = CFG_CURRENT_SSID; + else + if (ptype == 3) + ltv.typ = CFG_CNF_OWN_SSID; + else + ltv.typ = CFG_CNF_DESIRED_SSID; + rc = hcf_get_info(ifbp, (LTVP) <v); + DEBUG(DEBUG_NOISY, "%s: hcf_get_info(CFG_CNF_OWN/DESIRED/CURRENT_SSID) returned 0x%x\n", dev_info, rc); + if (rc) + return rc; + l = le16_to_cpup(<v.id[0]); + if (l) + { + l = min(len, l); + memcpy(name, (char *) <v.id[1], l); + } + name[l] = '\0'; + DEBUG(DEBUG_NOISY, "%s: hcf_get_info(CFG_CNF_OWN/DESIRED/CURRENT_SSID):'%s'\n", dev_info, name); + return 0; +} + +static inline int wvlan_hw_getbssid (IFBP ifbp, char *mac, int len) +{ + CFG_MAC_ADDR_STRCT ltv; + int rc, l; + + ltv.len = 4; + ltv.typ = CFG_CURRENT_BSSID; + rc = hcf_get_info(ifbp, (LTVP) <v); + DEBUG(DEBUG_NOISY, "%s: hcf_get_info(CFG_CURRENT_BSSID) returned 0x%x\n", dev_info, rc); + if (rc) + return rc; + l = min(len, ltv.len*2); + memcpy(mac, (char *)ltv.mac_addr, l); + return 0; +} + +static inline int wvlan_hw_setchannel (IFBP ifbp, int channel) +{ + CFG_ID_STRCT ltv; + int rc; + + ltv.len = 2; + ltv.typ = CFG_CNF_OWN_CHANNEL; + ltv.id[0] = cpu_to_le16(channel); + rc = hcf_put_info(ifbp, (LTVP) <v); + DEBUG(DEBUG_NOISY, "%s: hcf_put_info(CFG_CNF_OWN_CHANNEL:0x%x) returned 0x%x\n", dev_info, channel, rc); + return rc; +} + +/* Unused ??? */ +static int wvlan_hw_getchannel (IFBP ifbp) +{ + CFG_ID_STRCT ltv; + int rc, channel; + + ltv.len = 2; + ltv.typ = CFG_CNF_OWN_CHANNEL; + rc = hcf_get_info(ifbp, (LTVP) <v); + channel = le16_to_cpup(<v.id[0]); + DEBUG(DEBUG_NOISY, "%s: hcf_get_info(CFG_CNF_OWN_CHANNEL):0x%x returned 0x%x\n", dev_info, channel, rc); + return rc ? 0 : channel; +} + +static int wvlan_hw_getcurrentchannel (IFBP ifbp) +{ + CFG_ID_STRCT ltv; + int rc, channel; + + ltv.len = 2; + ltv.typ = CFG_CURRENT_CHANNEL; + rc = hcf_get_info(ifbp, (LTVP) <v); + channel = le16_to_cpup(<v.id[0]); + DEBUG(DEBUG_NOISY, "%s: hcf_get_info(CFG_CURRENT_CHANNEL):0x%x returned 0x%x\n", dev_info, channel, rc); + return rc ? 0 : channel; +} + +static int wvlan_hw_setthreshold (IFBP ifbp, int thrh, int cmd) +{ + CFG_ID_STRCT ltv; + int rc; + + ltv.len = 2; + ltv.typ = cmd; + ltv.id[0] = cpu_to_le16(thrh); + rc = hcf_put_info(ifbp, (LTVP) <v); + DEBUG(DEBUG_NOISY, "%s: hcf_put_info(0x%x:0x%x) returned 0x%x\n", dev_info, cmd, thrh, rc); + return rc; +} + +static int wvlan_hw_getthreshold (IFBP ifbp, int cmd) +{ + CFG_ID_STRCT ltv; + int rc, thrh; + + ltv.len = 2; + ltv.typ = cmd; + rc = hcf_get_info(ifbp, (LTVP) <v); + thrh = le16_to_cpup(<v.id[0]); + DEBUG(DEBUG_NOISY, "%s: hcf_get_info(0x%x):0x%x returned 0x%x\n", dev_info, cmd, thrh, rc); + return rc ? 0 : thrh; +} + +/* Unused ? */ +static int wvlan_hw_setbitrate (IFBP ifbp, int brate) +{ + CFG_ID_STRCT ltv; + int rc; + + ltv.len = 2; + ltv.typ = CFG_TX_RATE_CONTROL; + ltv.id[0] = cpu_to_le16(brate); + rc = hcf_put_info(ifbp, (LTVP) <v); + DEBUG(DEBUG_NOISY, "%s: hcf_put_info(CFG_TX_RATE_CONTROL:0x%x) returned 0x%x\n", dev_info, brate, rc); + return rc; +} + +static int wvlan_hw_getbitrate (IFBP ifbp, int cur) +{ + CFG_ID_STRCT ltv; + int rc, brate; + + ltv.len = 2; + ltv.typ = cur ? CFG_CURRENT_TX_RATE : CFG_TX_RATE_CONTROL; + rc = hcf_get_info(ifbp, (LTVP) <v); + brate = le16_to_cpup(<v.id[0]); + DEBUG(DEBUG_NOISY, "%s: hcf_get_info(CFG_TX_RATE_CONTROL):0x%x returned 0x%x\n", dev_info, brate, rc); + return rc ? 0 : brate; +} + +static int wvlan_hw_getratelist(IFBP ifbp, char *brlist, int brmaxlen) +{ + CFG_ID_STRCT ltv; + int rc, brnum; + + ltv.len = 10; + ltv.typ = CFG_SUPPORTED_DATA_RATES; + rc = hcf_get_info(ifbp, (LTVP) <v); + brnum = le16_to_cpup(<v.id[0]); + if (brnum > brmaxlen) + brnum = brmaxlen; + memcpy(brlist, (char *) <v.id[1], brnum); + DEBUG(DEBUG_NOISY, "%s: hcf_get_info(CFG_CHANNEL_LIST):0x%x returned 0x%x\n", dev_info, brnum, rc); + return rc ? 0 : brnum; +} + +#ifdef WIRELESS_EXT +static inline int wvlan_hw_getfrequencylist(IFBP ifbp, iw_freq *list, int max) +{ + int chlist = wvlan_hw_getchannellist(ifbp); + int i, k = 0; + + /* Compute maximum number of freq to scan */ + if(max > 15) + max = 15; + + /* Check availability */ + for(i = 0; i < max; i++) + if((1 << i) & chlist) + { +#if WIRELESS_EXT > 7 + list[k].i = i + 1; /* Set the list index */ +#endif /* WIRELESS_EXT */ + list[k].m = frequency_list[i] * 100000; + list[k++].e = 1; /* Values in table in MHz -> * 10^5 * 10 */ + } + + return k; +} + +static inline int wvlan_getbitratelist(IFBP ifbp, __s32 *list, int max) +{ + char brlist[9]; + int brnum = wvlan_hw_getratelist(ifbp, brlist, sizeof(brlist)); + int i; + + /* Compute maximum number of freq to scan */ + if(brnum > max) + brnum = max; + + /* Convert to Mb/s */ + for(i = 0; i < max; i++) + list[i] = (brlist[i] & 0x7F) * 500000; + + return brnum; +} + +static int wvlan_hw_setpower (IFBP ifbp, int enabled, int cmd) +{ + CFG_ID_STRCT ltv; + int rc; + + ltv.len = 2; + ltv.typ = cmd; + ltv.id[0] = cpu_to_le16(enabled); + rc = hcf_put_info(ifbp, (LTVP) <v); + DEBUG(DEBUG_NOISY, "%s: hcf_put_info(0x%x:0x%x) returned 0x%x\n", dev_info, cmd, enabled, rc); + return rc; +} + +static int wvlan_hw_getpower (IFBP ifbp, int cmd) +{ + CFG_ID_STRCT ltv; + int rc, enabled; + + ltv.len = 2; + ltv.typ = cmd; + rc = hcf_get_info(ifbp, (LTVP) <v); + enabled = le16_to_cpup(<v.id[0]); + DEBUG(DEBUG_NOISY, "%s: hcf_get_info(0x%x):0x%x returned 0x%x\n", dev_info, cmd, enabled, rc); + return rc ? 0 : enabled; +} + +static inline int wvlan_hw_setpmsleep (IFBP ifbp, int duration) +{ + CFG_ID_STRCT ltv; + int rc; + + ltv.len = 2; + ltv.typ = CFG_CNF_MAX_SLEEP_DURATION; + ltv.id[0] = cpu_to_le16(duration); + rc = hcf_put_info(ifbp, (LTVP) <v); + DEBUG(DEBUG_NOISY, "%s: hcf_put_info(CNF_MAX_SLEEP_DURATION:0x%x) returned 0x%x\n", dev_info, duration, rc); + return rc; +} + +static inline int wvlan_hw_getpmsleep (IFBP ifbp) +{ + CFG_ID_STRCT ltv; + int rc, duration; + + ltv.len = 2; + ltv.typ = CFG_CNF_MAX_SLEEP_DURATION; + rc = hcf_get_info(ifbp, (LTVP) <v); + duration = le16_to_cpup(<v.id[0]); + DEBUG(DEBUG_NOISY, "%s: hcf_get_info(CNF_MAX_SLEEP_DURATION):0x%x returned 0x%x\n", dev_info, duration, rc); + return rc ? 0 : duration; +} + +static int wvlan_hw_getprivacy (IFBP ifbp) +{ + CFG_ID_STRCT ltv; + int rc, privacy; + + // This function allow to distiguish bronze cards from other + // types, to know if WEP exist... + // This is stupid, we have no way to distinguish the silver + // and gold cards, because the call below return 1 in all + // cases. Yuk... + ltv.len = 2; + ltv.typ = CFG_PRIVACY_OPTION_IMPLEMENTED; + rc = hcf_get_info(ifbp, (LTVP) <v); + privacy = le16_to_cpup(<v.id[0]); + DEBUG(DEBUG_NOISY, "%s: hcf_get_info(CFG_PRIVACY_OPTION_IMPLEMENTED):0x%x returned 0x%x\n", dev_info, privacy, rc); + return rc ? 0 : privacy; +} + +static int wvlan_hw_setprivacy (IFBP ifbp, int mode, int transmit, KEY_STRCT *keys) +{ + CFG_ID_STRCT ltv; + CFG_CNF_DEFAULT_KEYS_STRCT ltv_key; + int rc; + int i; + + if (mode) + { + // Set the index of the key used for transmission + ltv.len = 2; + ltv.typ = CFG_CNF_TX_KEY_ID; + ltv.id[0] = cpu_to_le16(transmit); + rc = hcf_put_info(ifbp, (LTVP) <v); + DEBUG(DEBUG_NOISY, "%s: hcf_put_info(CFG_CNF_TX_KEY_ID:0x%x) returned 0x%x\n", dev_info, mode, rc); + if (rc) + return rc; + + // Set the keys themselves (all in on go !) + ltv_key.len = sizeof(KEY_STRCT)*MAX_KEYS/2 + 1; + ltv_key.typ = CFG_CNF_DEFAULT_KEYS; + memcpy((char *) <v_key.key, (char *) keys, sizeof(KEY_STRCT)*MAX_KEYS); + for (i = 0; i < MAX_KEYS; ++i) + cpu_to_le16s(<v_key.key[i].len); + rc = hcf_put_info(ifbp, (LTVP) <v_key); + DEBUG(DEBUG_NOISY, "%s: hcf_put_info(CFG_CNF_TX_KEY_ID:0x%x) returned 0x%x\n", dev_info, mode, rc); + if (rc) + return rc; + } + // enable/disable encryption + ltv.len = 2; + ltv.typ = CFG_CNF_ENCRYPTION; + ltv.id[0] = cpu_to_le16(mode); + rc = hcf_put_info(ifbp, (LTVP) <v); + DEBUG(DEBUG_NOISY, "%s: hcf_put_info(CFG_CNF_ENCRYPTION:0x%x) returned 0x%x\n", dev_info, mode, rc); + return rc; +} +#endif /* WIRELESS_EXT */ + +static int wvlan_hw_setpromisc (IFBP ifbp, int promisc) +{ + CFG_ID_STRCT ltv; + int rc; + + ltv.len = 2; + ltv.typ = CFG_PROMISCUOUS_MODE; + ltv.id[0] = cpu_to_le16(promisc); + rc = hcf_put_info(ifbp, (LTVP) <v); + DEBUG(DEBUG_NOISY, "%s: hcf_put_info(CFG_PROMISCUOUS_MODE:0x%x) returned 0x%x\n", dev_info, promisc, rc); + return rc; +} + +static inline int wvlan_hw_getfirmware (IFBP ifbp, int *vendor, int *major, int *minor) +{ + CFG_ID_STRCT ltv; + int rc; + + ltv.len = 32; + ltv.typ = CFG_STA_IDENTITY; + rc = hcf_get_info(ifbp, (LTVP) <v); + DEBUG(DEBUG_NOISY, "%s: hcf_get_info(CFG_STA_IDENTITY) returned 0x%x\n", dev_info, rc); + if (rc) + return rc; + + /* Get the data we need (note : 16 bits operations) */ + *vendor = le16_to_cpup(<v.id[1]); + *major = le16_to_cpup(<v.id[2]); + *minor = le16_to_cpup(<v.id[3]); + /* There is more data after that, but I can't guess its use */ + + DEBUG(DEBUG_NOISY, "%s: hcf_get_info(CFG_STA_IDENTITY):%d-%d.%d\n", dev_info, *vendor, *major, *minor); + + return 0; +} + + +/******************************************************************** + * HARDWARE CONFIG / SHUTDOWN / RESET + */ + +/*------------------------------------------------------------------*/ +/* + * Hardware configuration of the Wavelan + * The caller *must* disable IRQs by himself before comming here. + */ +static int wvlan_hw_config (struct net_device *dev) +{ + struct net_local *local = (struct net_local *) dev->priv; + int rc, i, chlist; + int vendor, major, minor; /* Firmware revision */ + int firmware; + + DEBUG(DEBUG_CALLTRACE, "-> wvlan_hw_config(%s)\n", dev->name); + + // Init the HCF library + hcf_connect(&local->ifb, dev->base_addr); + + // Init hardware and turn on interrupts + rc = hcf_action(&local->ifb, HCF_ACT_CARD_IN); + DEBUG(DEBUG_NOISY, "%s: hcf_action(HCF_ACT_CARD_IN) returned 0x%x\n", dev_info, rc); +#if defined(PCMCIA_DEBUG) && (PCMCIA_DEBUG>=DEBUG_INTERRUPT) + local->ifb.IFB_IntEnMask |= HREG_EV_TICK; +#endif + rc = hcf_action(&local->ifb, HCF_ACT_INT_ON); + DEBUG(DEBUG_NOISY, "%s: hcf_action(HCF_ACT_INT_ON) returned 0x%x\n", dev_info, rc); + + /* Get MAC address (before we get a chance to fail) */ + if (!rc) { + rc = wvlan_hw_getmacaddr(&local->ifb, dev->dev_addr, ETH_ALEN); + printk(KERN_INFO "%s: MAC address on %s is ", dev_info, dev->name); + for (i=0; idev_addr[i]); + printk("\n"); + } + + /* Get firmware revision of the card */ + if (!rc) + rc = wvlan_hw_getfirmware(&local->ifb, &vendor, &major, &minor); + + /* Process firmware info to know what it supports */ + firmware = (major << 16) + minor; + if(!rc) { + switch(vendor) { + case 0x1: + /* This is a Lucent card : Wavelan IEEE, Orinoco, + * Cabletron/Enterasys Roamabout or ELSA cards. + * This is what we mainly support... + * Note : this will work at least for Lucent + * firmwares */ + local->has_port3 = 1; + local->has_ibssid = ((firmware >= 0x60006) + + (firmware >= 0x60010)); + local->has_mwo = (firmware >= 0x60000); + local->has_wep = (firmware >= 0x40020); + local->has_pwep = 0; + local->has_pm = (firmware >= 0x40020); + /* Note : I've tested the following firmwares : + * 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.06 and 6.16 + * Jean II */ + /* Tested CableTron 4.32 - Anton */ + break; + case 0x2: + case 0x6: + /* This is a PrismII card. It is is *very* similar + * to the Lucent, and the driver work 95%, + * therefore, we attempt to support it... */ + printk(KERN_NOTICE "%s: This is a PrismII card, not a Wavelan IEEE card :-(\n\ +You may want report firmare revision (0x%X) and what the card support.\n\ +I will try to make it work, but you should look for a better driver.\n", dev_info, firmware); + local->has_port3 = 1; + local->has_ibssid = 0; + local->has_mwo = 0; + local->has_wep = 0; + local->has_pwep = 1; + local->has_pm = 1; + /* Would need to reverse engineer encryption support, + * somebody with a card should do that... */ + /* Transmit rate also seem to be different. */ + /* Note : currently untested... Jean II */ + break; + default: + printk(KERN_NOTICE "%s: Unrecognised card, card return vendor = 0x%04X, please report...\n", dev_info, vendor); + break; + } + } + + printk(KERN_INFO "%s: Found firmware 0x%X (vendor %d) - Firmware capabilities : %d-%d-%d-%d-%d\n", + dev_info, firmware, vendor, local->has_port3, local->has_ibssid, + local->has_mwo, local->has_wep, local->has_pm); + + if(!rc) { + /* Check for a few user mistakes... Cut down on support ;-) */ + if((!local->has_port3) && (local->port_type == 3)) { + printk(KERN_NOTICE "%s: This firmware doesn't support ``port_type=3'', please use iwconfig.\n", dev_info); + rc = 255; + } + if((!local->has_ibssid) && (local->allow_ibss)) { + printk(KERN_NOTICE "%s: This firmware doesn't support ``allow_ibss=1'', please update it.\n", dev_info); + rc = 255; + } + if((local->allow_ibss) && (local->has_ibssid == 1) && + (local->network_name[0] == '\0')) { + printk(KERN_NOTICE "%s: This firmware require an ESSID in Ad-Hoc mode, please use iwconfig.\n", dev_info); + rc = 255; + } + if((local->has_ibssid) && (local->port_type == 3)) { + printk(KERN_NOTICE "%s: Warning, you are using the old proprietary Ad-Hoc mode (not the IBSS Ad-Hoc mode).\n", dev_info); + } + } + + // Set hardware parameters + if (!rc) + rc = wvlan_hw_setmaxdatalen(&local->ifb, HCF_MAX_MSG); + if (!rc) + rc = wvlan_hw_setporttype(&local->ifb, local->port_type); + if (!rc && *(local->network_name)) + rc = wvlan_hw_setssid(&local->ifb, local->network_name, + local->port_type); + /* Firmware 4.08 doesn't like that at all :-( */ + if (!rc && (local->has_ibssid)) + rc = wvlan_hw_setallowibssflag(&local->ifb, local->allow_ibss); + +#ifdef WIRELESS_EXT + // Set other hardware parameters + if (!rc && *(local->station_name)) + rc = wvlan_hw_setstationname(&local->ifb, local->station_name); + if (!rc) + rc = wvlan_hw_setthreshold(&local->ifb, local->ap_density, CFG_CNF_SYSTEM_SCALE); + if (!rc) + rc = wvlan_hw_setthreshold(&local->ifb, local->transmit_rate, CFG_TX_RATE_CONTROL); + if (!rc) + rc = wvlan_hw_setthreshold(&local->ifb, local->medium_reservation, CFG_RTS_THRH); + /* Normal fragmentation for v4 and earlier */ + if (!rc && (!local->has_mwo)) + rc = wvlan_hw_setthreshold(&local->ifb, local->frag_threshold, CFG_FRAGMENTATION_THRH); + /* MWO robustness for v6 and later */ + if (!rc && (local->has_mwo)) + rc = wvlan_hw_setthreshold(&local->ifb, local->mwo_robust, CFG_CNF_MICRO_WAVE); + /* Firmware 4.08 doesn't like those at all :-( */ + if (!rc && (local->has_wep)) + rc = wvlan_hw_setprivacy(&local->ifb, local->wep_on, local->transmit_key, local->key); + if (!rc && (local->has_pm)) + rc = wvlan_hw_setpower(&local->ifb, local->pm_on, CFG_CNF_PM_ENABLED); + if (!rc && (local->has_pm) && (local->pm_on)) + rc = wvlan_hw_setpower(&local->ifb, local->pm_multi, CFG_CNF_MCAST_RX); + if (!rc && (local->has_pm) && (local->pm_on)) + rc = wvlan_hw_setpmsleep(&local->ifb, local->pm_period); +#endif /* WIRELESS_EXT */ + + // Check valid channel settings + if (!rc && ((local->port_type == 3) || (local->allow_ibss))) { + chlist = wvlan_hw_getchannellist(&local->ifb); + printk(KERN_INFO "%s: Valid channels: ", dev_info); + for (i=1; i<17; i++) + if (1<<(i-1) & chlist) + printk("%d ", i); + printk("\n"); + if (local->channel < 1 || local->channel > 16 + || !(1 << (local->channel - 1) & chlist)) + printk(KERN_WARNING "%s: Channel value of %d is invalid!\n", dev_info, local->channel); + else + rc = wvlan_hw_setchannel(&local->ifb, local->channel); + } + + // Enable hardware + if (!rc) + { + rc = hcf_enable(&local->ifb, 0); + DEBUG(DEBUG_NOISY, "%s: hcf_enable(0) returned 0x%x\n", dev_info, rc); + } + + // Report error if any + if (rc) + printk(KERN_WARNING "%s: Initialization failed!\n", dev_info); + + DEBUG(DEBUG_CALLTRACE, "<- wvlan_hw_config()\n"); + return rc; +} + +/*------------------------------------------------------------------*/ +/* + * Wrapper for calling wvlan_hw_config() with irq disabled + */ +static inline int wvlan_hw_config_locked (struct net_device *dev) +{ + struct net_local *local = (struct net_local *) dev->priv; + unsigned long flags; + int ret; + + spin_lock_irqsave(&local->slock,flags); + ret = wvlan_hw_config(dev); + spin_unlock_irqrestore(&local->slock,flags); + + return ret; +} + +/*------------------------------------------------------------------*/ +/* + * Hardware de-configuration of the Wavelan (switch off the device) + * The caller *must* disable IRQs by himself before comming here. + */ +static int wvlan_hw_shutdown (struct net_device *dev) +{ + struct net_local *local = (struct net_local *) dev->priv; + int rc; + + DEBUG(DEBUG_CALLTRACE, "-> wvlan_hw_shutdown(%s)\n", dev->name); + + // Disable and shutdown hardware + rc = hcf_disable(&local->ifb, 0); + DEBUG(DEBUG_NOISY, "%s: hcf_disable(0) returned 0x%x\n", dev_info, rc); + rc = hcf_action(&local->ifb, HCF_ACT_INT_OFF); + DEBUG(DEBUG_NOISY, "%s: hcf_action(HCF_ACT_INT_OFF) returned 0x%x\n", dev_info, rc); + rc = hcf_action(&local->ifb, HCF_ACT_CARD_OUT); + DEBUG(DEBUG_NOISY, "%s: hcf_action(HCF_ACT_CARD_OUT) returned 0x%x\n", dev_info, rc); + + // Release HCF library + hcf_disconnect(&local->ifb); + + DEBUG(DEBUG_CALLTRACE, "<- wvlan_hw_shutdown()\n"); + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * "light" hardware reset of the Wavelan + * The caller *must* disable IRQs by himself before comming here. + */ +static int wvlan_hw_reset (struct net_device *dev) +{ + struct net_local *local = (struct net_local *) dev->priv; + int rc; + + DEBUG(DEBUG_CALLTRACE, "-> wvlan_hw_reset(%s)\n", dev->name); + + // Disable hardware + rc = hcf_disable(&local->ifb, 0); + DEBUG(DEBUG_NOISY, "%s: hcf_disable(0) returned 0x%x\n", dev_info, rc); + rc = hcf_action(&local->ifb, HCF_ACT_INT_OFF); + DEBUG(DEBUG_NOISY, "%s: hcf_action(HCF_ACT_INT_OFF) returned 0x%x\n", dev_info, rc); + + // Re-Enable hardware + rc = hcf_action(&local->ifb, HCF_ACT_INT_ON); + DEBUG(DEBUG_NOISY, "%s: hcf_action(HCF_ACT_INT_ON) returned 0x%x\n", dev_info, rc); + rc = hcf_enable(&local->ifb, 0); + DEBUG(DEBUG_NOISY, "%s: hcf_enable(0) returned 0x%x\n", dev_info, rc); + + DEBUG(DEBUG_CALLTRACE, "<- wvlan_hw_reset()\n"); + return rc; +} + + +/******************************************************************** + * NET STATS / IOCTL + */ +struct net_device_stats *wvlan_get_stats (struct net_device *dev) +{ + DEBUG(DEBUG_CALLTRACE, "<> wvlan_get_stats(%s)\n", dev->name); + return(&((struct net_local *) dev->priv)->stats); +} + +#ifdef WIRELESS_EXT +int wvlan_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct net_local *local = (struct net_local *) dev->priv; + struct iwreq *wrq = (struct iwreq *) rq; + unsigned long flags; + int rc = 0; + + DEBUG(DEBUG_CALLTRACE, "-> wvlan_ioctl(%s, cmd=0x%x)\n", dev->name, cmd); + + // Disable interrupts + spin_lock_irqsave(&local->slock,flags); + + switch (cmd) + { + // Get name + case SIOCGIWNAME: + strcpy(wrq->u.name, "IEEE 802.11-DS"); + break; + + // Set frequency/channel + case SIOCSIWFREQ: + // If setting by frequency, convert to a channel + if((wrq->u.freq.e == 1) && + (wrq->u.freq.m >= (int) 2.412e8) && + (wrq->u.freq.m <= (int) 2.487e8)) + { + int f = wrq->u.freq.m / 100000; + int c = 0; + while((c < 14) && (f != frequency_list[c])) + c++; + // Hack to fall through... + wrq->u.freq.e = 0; + wrq->u.freq.m = c + 1; + } + // Setting by channel number + if (((local->port_type != 3) && (!local->allow_ibss)) + || (wrq->u.freq.m > 1000) || (wrq->u.freq.e > 0)) + rc = -EOPNOTSUPP; + else + { + int channel = wrq->u.freq.m; + int chlist = wvlan_hw_getchannellist(&local->ifb); + if (channel<1 || channel>16 || !(1<<(channel-1) & chlist)) + { + DEBUG(DEBUG_INFO, "%s: New channel value of %d for %s is invalid!\n", dev_info, wrq->u.freq.m, dev->name); + rc = -EINVAL; + } + else + { + local->channel = wrq->u.freq.m; + local->need_commit = 1; + } + } + break; + + // Get frequency/channel + case SIOCGIWFREQ: +#ifdef WEXT_USECHANNELS + wrq->u.freq.m = wvlan_hw_getcurrentchannel(&local->ifb); + wrq->u.freq.e = 0; +#else + { + int f = wvlan_hw_getcurrentchannel(&local->ifb); + wrq->u.freq.m = frequency_list[f-1] * 100000; + wrq->u.freq.e = 1; + } +#endif + break; + + // Set desired network name (ESSID) + case SIOCSIWESSID: + if (wrq->u.data.pointer) + { + char essid[IW_ESSID_MAX_SIZE + 1]; + + /* Check if we asked for `any' */ + if(wrq->u.data.flags == 0) + { + essid[0] = '\0'; + } + else + { + /* Check the size of the string */ + if(wrq->u.data.length > + IW_ESSID_MAX_SIZE + 1) + { + rc = -E2BIG; + break; + } + spin_unlock_irqrestore(&local->slock,flags); + rc = copy_from_user(essid, + wrq->u.data.pointer, + wrq->u.data.length); + spin_lock_irqsave(&local->slock,flags); + if (rc) { + rc = -EFAULT; + break; + } + essid[IW_ESSID_MAX_SIZE] = '\0'; + } + strncpy(local->network_name, essid, sizeof(local->network_name)-1); + local->need_commit = 1; + } + break; + + // Get current network name (ESSID) + case SIOCGIWESSID: + if (wrq->u.data.pointer) + { + char essid[IW_ESSID_MAX_SIZE + 1]; + /* Get the essid that was set */ + wvlan_hw_getssid(&local->ifb, essid, + IW_ESSID_MAX_SIZE, + WVLAN_DESIRED, + local->port_type); + /* If it was set to any, get the current one */ + if(strlen(essid) == 0) + wvlan_hw_getssid(&local->ifb, essid, + IW_ESSID_MAX_SIZE, + WVLAN_CURRENT, + local->port_type); + + /* Push it out ! */ + wrq->u.data.length = strlen(essid) + 1; + wrq->u.data.flags = 1; /* active */ + spin_unlock_irqrestore(&local->slock,flags); + rc = copy_to_user(wrq->u.data.pointer, essid, sizeof(essid)); + spin_lock_irqsave(&local->slock,flags); + if (rc) + rc = -EFAULT; + } + break; + + // Get current Access Point (BSSID) + case SIOCGIWAP: + wvlan_hw_getbssid(&local->ifb, wrq->u.ap_addr.sa_data, ETH_ALEN); + wrq->u.ap_addr.sa_family = ARPHRD_ETHER; + break; + +#if WIRELESS_EXT > 7 + // Set desired station name + case SIOCSIWNICKN: + if (wrq->u.data.pointer) + { + char name[IW_ESSID_MAX_SIZE + 1]; + + /* Check the size of the string */ + if(wrq->u.data.length > IW_ESSID_MAX_SIZE + 1) + { + rc = -E2BIG; + break; + } + spin_unlock_irqrestore(&local->slock,flags); + rc = copy_from_user(name, wrq->u.data.pointer, wrq->u.data.length); + spin_lock_irqsave(&local->slock,flags); + if (rc) { + rc = -EFAULT; + break; + } + name[IW_ESSID_MAX_SIZE] = '\0'; + strncpy(local->station_name, name, sizeof(local->station_name)-1); + local->need_commit = 1; + } + break; + + // Get current station name + case SIOCGIWNICKN: + if (wrq->u.data.pointer) + { + char name[IW_ESSID_MAX_SIZE + 1]; + wvlan_hw_getstationname(&local->ifb, name, IW_ESSID_MAX_SIZE); + wrq->u.data.length = strlen(name) + 1; + spin_unlock_irqrestore(&local->slock,flags); + rc = copy_to_user(wrq->u.data.pointer, name, sizeof(name)); + spin_lock_irqsave(&local->slock,flags); + if (rc) + rc = -EFAULT; + } + break; + + // Set the desired bit-rate + case SIOCSIWRATE: + { + // Start the magic... + char brlist[9]; + int brnum = wvlan_hw_getratelist(&local->ifb, brlist, sizeof(brlist)); + int brate = wrq->u.bitrate.value/500000; + int wvrate = 0; + + // Auto or fixed ? + if(wrq->u.bitrate.fixed == 0) { + // Is there a valid value ? + if(wrq->u.bitrate.value == -1) + wvrate = 3; + else { + // Setting by rate value + // Find index in magic table + while((rate_list[wvrate] != -brate) && + (wvrate < (brnum * 2))) + wvrate++; + } + } else + if((wrq->u.bitrate.value <= (brnum * 2 - 1)) && + (wrq->u.bitrate.value > 0)) + { + // Setting by rate index + wvrate = wrq->u.bitrate.value; + } else { + // Setting by rate value + // Find index in magic table + while((rate_list[wvrate] != brate) && + (wvrate < (brnum * 2))) + wvrate++; + } + + // Check if in range + if((wvrate < 1) || (wvrate >= (brnum * 2))) + { + rc = -EINVAL; + break; + } + local->transmit_rate = wvrate; + local->need_commit = 1; + break; + } + + // Get the current bit-rate + case SIOCGIWRATE: + { + int wvrate = wvlan_hw_getbitrate(&local->ifb, WVLAN_DESIRED); + int brate = rate_list[wvrate]; + + // Auto ? + if (brate < 0) + { + wrq->u.bitrate.fixed = 0; + wvrate = wvlan_hw_getbitrate(&local->ifb, WVLAN_CURRENT); + brate = 2 * wvrate; + // Mandatory kludge! + if (wvrate == 6) + brate = 11; + } + else + wrq->u.bitrate.fixed = 1; + + wrq->u.bitrate.value = brate * 500000; +#if WIRELESS_EXT > 8 + wrq->u.bitrate.disabled = 0; +#endif + } + break; + + // Set the desired AP density + case SIOCSIWSENS: + { + int dens = wrq->u.sens.value; + if((dens < 1) || (dens > 3)) + { + rc = -EINVAL; + break; + } + local->ap_density = dens; + local->need_commit = 1; + } + break; + + // Get the current AP density + case SIOCGIWSENS: + wrq->u.sens.value = wvlan_hw_getthreshold(&local->ifb, CFG_CNF_SYSTEM_SCALE); + wrq->u.sens.fixed = 0; /* auto */ + break; +#endif /* WIRELESS_EXT > 7 */ + +#if WIRELESS_EXT > 8 + // Set the desired RTS threshold + case SIOCSIWRTS: + { + int rthr = wrq->u.rts.value; + // if(wrq->u.rts.fixed == 0) we should complain + if(wrq->u.rts.disabled) + rthr = 2347; + if((rthr < 0) || (rthr > 2347)) + { + rc = -EINVAL; + break; + } + local->medium_reservation = rthr; + local->need_commit = 1; + } + break; + + // Get the current RTS threshold + case SIOCGIWRTS: + wrq->u.rts.value = wvlan_hw_getthreshold(&local->ifb, CFG_RTS_THRH); + wrq->u.rts.disabled = (wrq->u.rts.value == 2347); + wrq->u.rts.fixed = 1; + break; + + // Set the desired fragmentation threshold + case SIOCSIWFRAG: + /* Check if firmware v4 or v6 */ + if(local->has_mwo) { + int fthr = wrq->u.frag.value; + /* v6 : fragmentation is now controlled by + * MWO robust setting */ + // if(wrq->u.frag.fixed == 1) should complain + if(wrq->u.frag.disabled) + fthr = 0; + if((fthr < 0) || (fthr > 2347)) { + rc = -EINVAL; + } else { + local->mwo_robust = (fthr > 0); + local->need_commit = 1; + } + } else { + int fthr = wrq->u.frag.value; + /* v4 : we can set frag threshold */ + // if(wrq->u.frag.fixed == 0) should complain + if(wrq->u.frag.disabled) + fthr = 2346; + if((fthr < 256) || (fthr > 2346)) { + rc = -EINVAL; + } else { + fthr &= ~0x1; // Get an even value + local->frag_threshold = fthr; + local->need_commit = 1; + } + } + break; + + // Get the current fragmentation threshold + case SIOCGIWFRAG: + /* Check if firmware v4 or v6 */ + if(local->has_mwo) { + if(wvlan_hw_getthreshold(&local->ifb, CFG_CNF_MICRO_WAVE)) + wrq->u.frag.value = 2347; + else + wrq->u.frag.value = 0; + wrq->u.frag.disabled = !(wrq->u.frag.value); + wrq->u.frag.fixed = 0; + } else { + wrq->u.frag.value = wvlan_hw_getthreshold(&local->ifb, CFG_FRAGMENTATION_THRH); + wrq->u.frag.disabled = (wrq->u.frag.value >= 2346); + wrq->u.frag.fixed = 1; + } + break; + + // Set port type + case SIOCSIWMODE: + /* Big firmware trouble here ! + * In v4 and v6.04, the ad-hoc mode supported is the + * Lucent proprietary Ad-Hoc demo mode. + * Starting with v6.06, the ad-hoc mode supported is + * the standard 802.11 IBSS Ad-Hoc mode. + * Jean II + */ + if(local->has_ibssid) { + /* v6 : set the IBSS flag */ + char ibss = 0; + + /* Paranoia */ + if(local->port_type != 1) + local->port_type = 1; + + switch (wrq->u.mode) + { + case IW_MODE_ADHOC: + ibss = 1; + // Fall through + case IW_MODE_INFRA: + local->allow_ibss = ibss; + local->need_commit = 1; + break; + default: + rc = -EINVAL; + } + } else { + /* v4 : set the correct port type */ + char ptype = 1; + + /* Note : this now works properly with + * all firmware ;-) */ + + /* Paranoia */ + if(local->allow_ibss) + local->allow_ibss = 0; + + switch (wrq->u.mode) + { + case IW_MODE_ADHOC: + ptype = 3; + // Fall through + case IW_MODE_INFRA: + local->port_type = ptype; + local->need_commit = 1; + break; + default: + rc = -EINVAL; + } + } + break; + + // Get port type + case SIOCGIWMODE: + /* Check for proprietary Ad-Hoc demo mode */ + if (wvlan_hw_getporttype(&local->ifb) == 1) + wrq->u.mode = IW_MODE_INFRA; + else + wrq->u.mode = IW_MODE_ADHOC; + /* Check for compliant 802.11 IBSS Ad-Hoc mode */ + if ((local->has_ibssid) && + (wvlan_hw_getallowibssflag(&local->ifb) == 1)) + wrq->u.mode = IW_MODE_ADHOC; + break; + + // Set the desired Power Management mode + case SIOCSIWPOWER: + // Disable it ? + if(wrq->u.power.disabled) { + local->pm_on = 0; + local->need_commit = 1; + } else { + // Check mode + switch(wrq->u.power.flags & IW_POWER_MODE) + { + case IW_POWER_UNICAST_R: + local->pm_multi = 0; + local->need_commit = 1; + break; + case IW_POWER_ALL_R: + local->pm_multi = 1; + local->need_commit = 1; + break; + case IW_POWER_ON: // None = ok + break; + default: // Invalid + rc = -EINVAL; + } + // Set period + if (wrq->u.power.flags & IW_POWER_PERIOD) + { + // Activate PM + local->pm_on = 1; + // Hum: check max/min values ? + local->pm_period = wrq->u.power.value/1000; + local->need_commit = 1; + } + if (wrq->u.power.flags & IW_POWER_TIMEOUT) + rc = -EINVAL; // Invalid + } + break; + + // Get the power management settings + case SIOCGIWPOWER: + wrq->u.power.disabled = !wvlan_hw_getpower(&local->ifb, CFG_CNF_PM_ENABLED); + wrq->u.power.flags = IW_POWER_PERIOD; + wrq->u.power.value = wvlan_hw_getpmsleep (&local->ifb) * 1000; + if (wvlan_hw_getpower(&local->ifb, CFG_CNF_MCAST_RX)) + wrq->u.power.flags |= IW_POWER_ALL_R; + else + wrq->u.power.flags |= IW_POWER_UNICAST_R; + break; + + // Set WEP keys and mode + case SIOCSIWENCODE: + // Is it supported? + if (!wvlan_hw_getprivacy(&local->ifb)) + { + rc = -EOPNOTSUPP; + break; + } + // Basic checking: do we have a key to set? + if (wrq->u.encoding.pointer != (caddr_t) 0) + { + int index = (wrq->u.encoding.flags & IW_ENCODE_INDEX) - 1; + // Check the size of the key + if (wrq->u.encoding.length > MAX_KEY_SIZE) + { + rc = -EINVAL; + break; + } + // Check the index + if ((index < 0) || (index >= MAX_KEYS)) + index = local->transmit_key; + // Cleanup + memset(local->key[index].key, 0, MAX_KEY_SIZE); + // Copy the key in the driver + spin_unlock_irqrestore(&local->slock,flags); + rc = copy_from_user(local->key[index].key, wrq->u.encoding.pointer, wrq->u.encoding.length); + spin_lock_irqsave(&local->slock,flags); + if (rc) { + rc = -EFAULT; + local->key[index].len = 0; + break; + } + // Set the length + if (wrq->u.encoding.length > MIN_KEY_SIZE) + local->key[index].len = MAX_KEY_SIZE; + else + if (wrq->u.encoding.length > 0) + local->key[index].len = MIN_KEY_SIZE; + else + local->key[index].len = 0; + // Enable WEP (if possible) + if ((index == local->transmit_key) && (local->key[local->transmit_key].len > 0)) + local->wep_on = 1; + } + else + { + int index = (wrq->u.encoding.flags & IW_ENCODE_INDEX) - 1; + // Do we want to just set the current transmit key? + if ((index >= 0) && (index < MAX_KEYS)) + { + if (local->key[index].len > 0) + { + local->transmit_key = index; + local->wep_on = 1; + } + else + rc = -EINVAL; + } + } + // Read the flags + if (wrq->u.encoding.flags & IW_ENCODE_DISABLED) + local->wep_on = 0; // disable encryption + if (wrq->u.encoding.flags & IW_ENCODE_RESTRICTED) + rc = -EINVAL; // Invalid + // Commit the changes + if (rc == 0) + local->need_commit = 1; + break; + + // Get the WEP keys and mode + case SIOCGIWENCODE: + // Is it supported? + if (!wvlan_hw_getprivacy(&local->ifb)) + { + rc = -EOPNOTSUPP; + break; + } + // Only super-user can see WEP key + if (!capable(CAP_NET_ADMIN)) + { + rc = -EPERM; + break; + } + // Basic checking... + if (wrq->u.encoding.pointer != (caddr_t) 0) + { + int index = (wrq->u.encoding.flags & IW_ENCODE_INDEX) - 1; + // Note: should read from adapter(?), and check if WEP capable + // Set the flags + wrq->u.encoding.flags = 0; + if (local->wep_on == 0) + wrq->u.encoding.flags |= IW_ENCODE_DISABLED; + // Which key do we want + if ((index < 0) || (index >= MAX_KEYS)) + index = local->transmit_key; + wrq->u.encoding.flags |= index + 1; + // Copy the key to the user buffer + wrq->u.encoding.length = local->key[index].len; + spin_unlock_irqrestore(&local->slock,flags); + rc = copy_to_user(wrq->u.encoding.pointer, local->key[index].key, local->key[index].len); + spin_lock_irqsave(&local->slock,flags); + if (rc) + rc = -EFAULT; + + } + break; +#endif /* WIRELESS_EXT > 8 */ + +#if WIRELESS_EXT > 9 + // Get the current Tx-Power + case SIOCGIWTXPOW: + wrq->u.txpower.value = 15; /* 15 dBm */ + wrq->u.txpower.fixed = 1; /* No power control */ + wrq->u.txpower.disabled = 0; /* Can't turn off */ + wrq->u.txpower.flags = IW_TXPOW_DBM; + break; +#endif /* WIRELESS_EXT > 9 */ + + // Get range of parameters + case SIOCGIWRANGE: + if (wrq->u.data.pointer) + { + struct iw_range range; + rc = verify_area(VERIFY_WRITE, wrq->u.data.pointer, sizeof(struct iw_range)); + if (rc) + break; + /* Set the length (very important for + * backward compatibility) */ + wrq->u.data.length = sizeof(range); + + /* Set all the info we don't care or + * don't know about to zero */ + memset(&range, 0, sizeof(range)); + +#if WIRELESS_EXT > 10 + /* Set the Wireless Extension versions */ + range.we_version_compiled = WIRELESS_EXT; + range.we_version_source = 10; +#endif /* WIRELESS_EXT > 10 */ + + // Throughput is no way near 2 Mb/s ! + // This value should be : + // 1.6 Mb/s for the 2 Mb/s card + // ~5 Mb/s for the 11 Mb/s card + // Jean II + range.throughput = 1.6 * 1024 * 1024; + range.min_nwid = 0x0000; + range.max_nwid = 0x0000; + range.num_channels = 14; + range.num_frequency = wvlan_hw_getfrequencylist(&local->ifb, + range.freq, + IW_MAX_FREQUENCIES); + range.sensitivity = 3; + if (local->port_type == 3 && + local->spy_number == 0) + { + range.max_qual.qual = 0; + range.max_qual.level = 0; + range.max_qual.noise = 0; + } + else + { + range.max_qual.qual = 0x8b - 0x2f; + range.max_qual.level = 0x2f - 0x95 - 1; + range.max_qual.noise = 0x2f - 0x95 - 1; + } +#if WIRELESS_EXT > 7 + range.num_bitrates = wvlan_getbitratelist(&local->ifb, + range.bitrate, + IW_MAX_BITRATES); + range.min_rts = 0; + range.max_rts = 2347; + range.min_frag = 256; + range.max_frag = 2346; + range.we_version_compiled = WIRELESS_EXT; + range.we_version_source = 11; +#endif /* WIRELESS_EXT > 7 */ +#if WIRELESS_EXT > 8 + // Is WEP it supported? + if (wvlan_hw_getprivacy(&local->ifb)) + { + // WEP: RC4 40 bits + range.encoding_size[0] = MIN_KEY_SIZE; + // RC4 ~128 bits + range.encoding_size[1] = MAX_KEY_SIZE; + range.num_encoding_sizes = 2; + range.max_encoding_tokens = 4; // 4 keys + } + else + { + range.num_encoding_sizes = 0; + range.max_encoding_tokens = 0; + } +#endif /* WIRELESS_EXT > 8 */ +#if WIRELESS_EXT > 9 + /* Power Management */ + range.min_pmp = 0; /* ??? */ + range.max_pmp = 65535000; /* ??? */ + range.pmp_flags = IW_POWER_PERIOD; + range.pmt_flags = 0; + range.pm_capa = IW_POWER_PERIOD | + IW_POWER_UNICAST_R; + /* Transmit Power */ + range.txpower[0] = 15; + range.num_txpower = 1; + range.txpower_capa = IW_TXPOW_DBM; +#endif /* WIRELESS_EXT > 9 */ + spin_unlock_irqrestore(&local->slock,flags); + rc = copy_to_user(wrq->u.data.pointer, &range, sizeof(struct iw_range)); + spin_lock_irqsave(&local->slock,flags); + if (rc) + rc = -EFAULT; + } + break; + +#ifdef WIRELESS_SPY + // Set the spy list + case SIOCSIWSPY: + if (wrq->u.data.length > IW_MAX_SPY) + { + rc = -E2BIG; + break; + } + local->spy_number = wrq->u.data.length; + if (local->spy_number > 0) + { + struct sockaddr address[IW_MAX_SPY]; + int i; + rc = verify_area(VERIFY_READ, wrq->u.data.pointer, sizeof(struct sockaddr) * local->spy_number); + if (rc) + break; + spin_unlock_irqrestore(&local->slock,flags); + rc = copy_from_user(address, wrq->u.data.pointer, sizeof(struct sockaddr) * local->spy_number); + spin_lock_irqsave(&local->slock,flags); + if (rc) { + rc = -EFAULT; + break; + } + for (i=0; ispy_number; i++) + memcpy(local->spy_address[i], address[i].sa_data, MAC_ADDR_SIZE); + memset(local->spy_stat, 0, sizeof(struct iw_quality) * IW_MAX_SPY); + DEBUG(DEBUG_INFO, "%s: New spy list:\n", dev_info); + for (i=0; iu.data.length; i++) + DEBUG(DEBUG_INFO, "%s: %d - %02x:%02x:%02x:%02x:%02x:%02x\n", dev_info, i+1, + local->spy_address[i][0], local->spy_address[i][1], + local->spy_address[i][2], local->spy_address[i][3], + local->spy_address[i][4], local->spy_address[i][5]); + } + break; + + // Get the spy list + case SIOCGIWSPY: + wrq->u.data.length = local->spy_number; + if ((local->spy_number > 0) && (wrq->u.data.pointer)) + { + struct sockaddr address[IW_MAX_SPY]; + int i; + rc = verify_area(VERIFY_WRITE, wrq->u.data.pointer, (sizeof(struct iw_quality)+sizeof(struct sockaddr)) * IW_MAX_SPY); + if (rc) + break; + for (i=0; ispy_number; i++) + { + memcpy(address[i].sa_data, local->spy_address[i], MAC_ADDR_SIZE); + address[i].sa_family = AF_UNIX; + } + spin_unlock_irqrestore(&local->slock,flags); + rc = copy_to_user(wrq->u.data.pointer, address, sizeof(struct sockaddr) * local->spy_number); + rc += copy_to_user(wrq->u.data.pointer + (sizeof(struct sockaddr)*local->spy_number), local->spy_stat, sizeof(struct iw_quality) * local->spy_number); + spin_lock_irqsave(&local->slock,flags); + if (rc) + rc = -EFAULT; + + for (i=0; ispy_number; i++) + local->spy_stat[i].updated = 0; + } + break; +#endif /* WIRELESS_SPY */ + +#ifdef HISTOGRAM + // Set the histogram range + case SIOCDEVPRIVATE + 0xd: + // Only super-user can set histogram data + if (!capable(CAP_NET_ADMIN)) + { + rc = -EPERM; + break; + } + if (wrq->u.data.length > 16) + { + rc = -E2BIG; + break; + } + local->his_number = wrq->u.data.length; + if (local->his_number > 0) + { + rc = verify_area(VERIFY_READ, wrq->u.data.pointer, sizeof(char) * local->his_number); + if (rc) + break; + spin_unlock_irqrestore(&local->slock,flags); + rc = copy_from_user(local->his_range, wrq->u.data.pointer, sizeof(char) * local->his_number); + spin_lock_irqsave(&local->slock,flags); + if (rc) { + rc = -EFAULT; + break; + } + memset(local->his_sum, 0, sizeof(long) * 16); + } + break; + + // Get the histogram statistic + case SIOCDEVPRIVATE + 0xe: + wrq->u.data.length = local->his_number; + if ((local->his_number > 0) && (wrq->u.data.pointer)) + { + rc = verify_area(VERIFY_WRITE, wrq->u.data.pointer, sizeof(long) * 16); + if (rc) + break; + spin_unlock_irqrestore(&local->slock,flags); + rc = copy_to_user(wrq->u.data.pointer, local->his_sum, sizeof(long) * local->his_number); + spin_lock_irqsave(&local->slock,flags); + if (rc) + rc = -EFAULT; + } + break; +#endif /* HISTOGRAM */ + + // Get valid private ioctl calls + case SIOCGIWPRIV: + if (wrq->u.data.pointer) + { + struct iw_priv_args priv[] = { +#ifdef HISTOGRAM + { SIOCDEVPRIVATE + 0xd, IW_PRIV_TYPE_BYTE | 16, 0, "sethisto" }, + { SIOCDEVPRIVATE + 0xe, 0, IW_PRIV_TYPE_INT | 16, "gethisto" }, +#endif +#ifdef PCMCIA_DEBUG + { SIOCDEVPRIVATE + 0x0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "force_reset" }, + { SIOCDEVPRIVATE + 0x1, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "debug_getinfo" }, +#endif + }; + rc = verify_area(VERIFY_WRITE, wrq->u.data.pointer, sizeof(priv)); + if (rc) + break; + wrq->u.data.length = sizeof(priv) / sizeof(priv[0]); + spin_unlock_irqrestore(&local->slock,flags); + rc = copy_to_user(wrq->u.data.pointer, priv, sizeof(priv)); + spin_lock_irqsave(&local->slock,flags); + if (rc) + rc = -EFAULT; + } + break; + +#ifdef PCMCIA_DEBUG + // Force card reset (debug purpose only) + case SIOCDEVPRIVATE + 0x0: + // Only super-user can reset the card... + if (!capable(CAP_NET_ADMIN)) + { + rc = -EPERM; + break; + } + if (*((int *) wrq->u.name) > 0) + { + // 'hard' reset + printk(KERN_DEBUG "%s: Forcing hard reset\n", dev_info); + /* IRQ already disabled, don't do it again */ + wvlan_hw_shutdown(dev); + wvlan_hw_config(dev); + } + else + { + // 'soft' reset + printk(KERN_DEBUG "%s: Forcing soft reset\n", dev_info); + /* IRQ already disabled, don't do it again */ + wvlan_hw_reset(dev); + } + break; + + // Get info from card and dump answer to syslog (debug purpose only) + case SIOCDEVPRIVATE + 0x1: + { + CFG_ID_STRCT ltv; + char *p; + int typ = *((int *) wrq->u.name); + ltv.len = 18; + ltv.typ = typ; + rc = hcf_get_info(&local->ifb, (LTVP) <v); + if (rc) + printk(KERN_DEBUG "%s: hcf_get_info(0x%x) returned error 0x%x\n", dev_info, typ, rc); + else + { + p = (char *) <v.id; + printk(KERN_DEBUG "%s: hcf_get_info(0x%x) returned %d words:\n", dev_info, ltv.typ, ltv.len); + printk(KERN_DEBUG "%s: hex-dump: ", dev_info); + for (rc=0; rc<(ltv.len); rc++) + printk("%04x ", le16_to_cpup(<v.id[rc])); + printk("\n"); + printk(KERN_DEBUG "%s: ascii-dump: '", dev_info); + for (rc=0; rc<(ltv.len*2); rc++) + printk("%c", (p[rc]>31) ? p[rc] : '.'); + printk("'\n"); + } + } + break; +#endif /* PCMCIA_DEBUG */ + + // All other calls are currently unsupported + default: + rc = -EOPNOTSUPP; + } + + /* Some of the "set" function may have modified some of the + * parameters. It's now time to commit them in the card */ + if(local->need_commit) { + /* Is the driver active ? + * Here, we optimise. If the driver is not active, we don't + * commit the individual changes, and all the changes will + * be committed together in wvlan_open(). This significantely + * speed up the card startup when using wireless.opts + * Jean II */ + if((local->link->open) || (cmd == SIOCSIWESSID)) { + /* IRQ are already disabled */ + wvlan_hw_shutdown(dev); + wvlan_hw_config(dev); + local->need_commit = 0; + } + } + + // Re-enable interrupts + spin_unlock_irqrestore(&local->slock,flags); + + DEBUG(DEBUG_CALLTRACE, "<- wvlan_ioctl()\n"); + return rc; +} + +struct iw_statistics *wvlan_get_wireless_stats (struct net_device *dev) +{ + struct net_local *local = (struct net_local *) dev->priv; + CFG_COMMS_QUALITY_STRCT ltv; + unsigned long flags; + int rc; + + DEBUG(DEBUG_CALLTRACE, "-> wvlan_get_wireless_stats(%s)\n", dev->name); + + // Disable interrupts + spin_lock_irqsave(&local->slock,flags); + + local->wstats.status = 0; + if (local->port_type != 3) + { + ltv.len = 4; + ltv.typ = CFG_COMMS_QUALITY; + rc = hcf_get_info(&local->ifb, (LTVP) <v); + DEBUG(DEBUG_NOISY, "%s: hcf_get_info(CFG_COMMS_QUALITY) returned 0x%x\n", dev_info, rc); + local->wstats.qual.qual = max(min(le16_to_cpup(<v.coms_qual), 0x8b-0x2f), 0); + local->wstats.qual.level = max(min(le16_to_cpup(<v.signal_lvl), 0x8a), 0x2f) - 0x95; + local->wstats.qual.noise = max(min(le16_to_cpup(<v.noise_lvl), 0x8a), 0x2f) - 0x95; + local->wstats.qual.updated = 7; + } + else + { + // Quality levels cannot be determined in ad-hoc mode, + // because we can 'hear' more that one remote station. + // If a spy address is defined, we report stats of the + // first spy address + local->wstats.qual.qual = 0; + local->wstats.qual.level = 0; + local->wstats.qual.noise = 0; + local->wstats.qual.updated = 0; +#ifdef WIRELESS_SPY + if (local->spy_number > 0) + { + local->wstats.qual.qual = local->spy_stat[0].qual; + local->wstats.qual.level = local->spy_stat[0].level; + local->wstats.qual.noise = local->spy_stat[0].noise; + local->wstats.qual.updated = local->spy_stat[0].updated; + } +#endif /* WIRELESS_SPY */ + } + + // Packets discarded in the wireless adapter due to wireless specific problems + local->wstats.discard.nwid = 0; + local->wstats.discard.code = local->ifb.IFB_NIC_Tallies.RxWEPUndecryptable; + local->wstats.discard.misc = local->ifb.IFB_NIC_Tallies.RxFCSErrors + + local->ifb.IFB_NIC_Tallies.RxDiscards_NoBuffer + + local->ifb.IFB_NIC_Tallies.TxDiscardsWrongSA; + + // Re-enable interrupts + spin_unlock_irqrestore(&local->slock,flags); + + DEBUG(DEBUG_CALLTRACE, "<- wvlan_get_wireless_stats()\n"); + return (&local->wstats); +} + +#ifdef WIRELESS_SPY +static inline void wvlan_spy_gather (struct net_device *dev, u_char *mac, u_char *stats) +{ + struct net_local *local = (struct net_local *)dev->priv; + int i; + + // Gather wireless spy statistics: for each packet, compare the + // source address with out list, and if match, get the stats... + for (i=0; ispy_number; i++) + if (!memcmp(mac, local->spy_address[i], MAC_ADDR_SIZE)) + { + local->spy_stat[i].qual = stats[2]; + local->spy_stat[i].level = stats[0] - 0x95; + local->spy_stat[i].noise = stats[1] - 0x95; + local->spy_stat[i].updated = 7; + } +} +#endif /* WIRELESS_SPY */ + +#ifdef HISTOGRAM +static inline void wvlan_his_gather (struct net_device *dev, u_char *stats) +{ + struct net_local *local = (struct net_local *)dev->priv; + u_char level = stats[0] - 0x2f; + int i; + + // Calculate a histogram of the signal level. Each time the + // level goes into our defined set of interval, we increment + // the count. + i = 0; + while ((i < (local->his_number-1)) && (level >= local->his_range[i++])); + local->his_sum[i]++; +} +#endif /* HISTOGRAM */ +#endif /* WIRELESS_EXT */ + +int wvlan_change_mtu (struct net_device *dev, int new_mtu) +{ + if (new_mtu < WVLAN_MIN_MTU || new_mtu > WVLAN_MAX_MTU) + { + DEBUG(DEBUG_INFO, "%s: New MTU of %d for %s out of range!\n", dev_info, new_mtu, dev->name); + return -EINVAL; + } + dev->mtu = new_mtu; + DEBUG(DEBUG_INFO, "%s: MTU of %s set to %d bytes\n", dev_info, dev->name, new_mtu); + return 0; +} + +static void wvlan_set_multicast_list (struct net_device *dev) +{ + struct net_local *local = (struct net_local *)dev->priv; + unsigned long flags; + + // Note: check if hardware up & running? + + // Disable interrupts + spin_lock_irqsave(&local->slock,flags); + + DEBUG(DEBUG_INFO, "%s: setting multicast Rx mode %02X to %d addresses.\n", dev->name, dev->flags, dev->mc_count); + + // Ok, what do we want? + if (dev->flags & IFF_PROMISC) + { + // Enable promiscuous mode: receive all packets. + if (!local->promiscuous) + { + local->promiscuous = 1; + local->mc_count = 0; + wvlan_hw_setpromisc(&local->ifb, local->promiscuous); + dev->flags |= IFF_PROMISC; + } + } + else + // If all multicast addresses + // or too many multicast addresses for the hardware filter + if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > WVLAN_MAX_MULTICAST)) + { + // Disable promiscuous mode, but active the all multicast mode + if (!local->promiscuous) { + // The Wavelan IEEE doesn't seem to have a + // "receive all multicast" flag, which allow to + // grab all multicast frames. So we go for + // promiscuous and let TCP filter packets, + // which is *very* far from optimal. + // Note that CNF_MCAST_RX is quite different, + // as it specify if the Wavelan will wake up for + // the broadcast announcements from the AP (DTIM) + local->promiscuous = 1; + local->mc_count = 0; + wvlan_hw_setpromisc(&local->ifb, local->promiscuous); + // Tell the kernel that we are doing a really bad job... + dev->flags |= IFF_PROMISC; + } + } + else + // If there is some multicast addresses to send + if (dev->mc_list != (struct dev_mc_list *) NULL) + { + // Disable promiscuous mode, but receive all packets + // in multicast list +#ifdef MULTICAST_AVOID + if (local->promiscuous || local->allmulticast || (dev->mc_count != local->mc_count)) +#endif + { + struct dev_mc_list *dmi; + int i; + CFG_GROUP_ADDR_STRCT ltv; + int rc; + + local->promiscuous = 0; + local->mc_count = dev->mc_count; + // Disable promiscuous + wvlan_hw_setpromisc(&local->ifb, local->promiscuous); + dev->flags &= ~IFF_PROMISC; + // Write multicast addresses in the adapter + for (i=0, dmi=dev->mc_list; dmi; dmi=dmi->next) + memcpy(ltv.mac_addr[i++], dmi->dmi_addr, dmi->dmi_addrlen); + ltv.len = (ETH_ALEN * local->mc_count / 2) + 1; + ltv.typ = CFG_GROUP_ADDR; + rc = hcf_put_info(&local->ifb, (LTVP) <v); + DEBUG(DEBUG_NOISY, "%s: hcf_put_info(CFG_GROUP_ADDR:0x%x) returned 0x%x\n", dev_info, local->mc_count, rc); + } + } + else + { + // Switch to normal mode: disable promiscuous mode and + // clear the multicast list. + if (local->promiscuous || local->mc_count != 0) + { + CFG_GROUP_ADDR_STRCT ltv; + int rc; + local->promiscuous = 0; + local->mc_count = 0; + wvlan_hw_setpromisc(&local->ifb, local->promiscuous); + // Clear multicast list + ltv.len = 1; + ltv.typ = CFG_GROUP_ADDR; + rc = hcf_put_info(&local->ifb, (LTVP) <v); + DEBUG(DEBUG_NOISY, "%s: hcf_put_info(CFG_GROUP_ADDR:0x%x) returned 0x%x\n", dev_info, local->mc_count, rc); + } + } + // Re-enable interrupts + spin_unlock_irqrestore(&local->slock,flags); + + return; +} + + + +/******************************************************************** + * NET TX / RX + */ +static void wvlan_watchdog (struct net_device *dev) +{ +#ifdef WVLAN_RESET_ON_TX_TIMEOUT + struct net_local *local = (struct net_local *) dev->priv; + unsigned long flags; +#endif + + DEBUG(DEBUG_CALLTRACE, "-> wvlan_wathdog(%s)\n", dev->name); + + // In theory, we could try to abort the current Tx command here, + // this would avoid to go through a long reset process in many + // cases (obstruction of the channel, very high contention)... + + // Reset card in case of Tx timeout +#ifdef WVLAN_RESET_ON_TX_TIMEOUT + printk(KERN_WARNING "%s: %s Tx timed out! Resetting card\n", dev_info, dev->name); + /* IRQ currently enabled, so disable it */ + spin_lock_irqsave(&local->slock,flags); + wvlan_hw_shutdown(dev); + wvlan_hw_config(dev); + spin_unlock_irqrestore(&local->slock,flags); +#else + printk(KERN_WARNING "%s: %s Tx timed out! Ignoring...\n", dev_info, dev->name); +#endif + + DEBUG(DEBUG_CALLTRACE, "<- wvlan_watchdog()\n"); +} + +int wvlan_tx (struct sk_buff *skb, struct net_device *dev) +{ + struct net_local *local = (struct net_local *)dev->priv; + unsigned long flags; + int rc, len; + u_char *p; + + DEBUG(DEBUG_CALLTRACE, "-> wvlan_tx(%s)\n", dev->name); + +#if (KERNEL_VERSION_CODE < KERNEL_VERSION(2,3,42)) + // We normally shouldn't be called if queue is stopped (transmitter busy) + // but older kernel code does anyway. So we'll check if the last + // transmission has timed out and reset the device in case + if (netif_queue_stopped(dev)) + { + DEBUG(DEBUG_TXRX, "%s: wvlan_tx(%s) called while busy!\n", dev_info, dev->name); + if ((jiffies - dev->trans_start) < TX_TIMEOUT) + return 1; + if (!netif_running(dev)) + { + printk(KERN_WARNING "%s: %s Tx on stopped device!\n", dev_info, dev->name); + return 1; + } + wvlan_watchdog(dev); + } +#endif + + skb_tx_check(dev, skb); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,79)) + skb->arp = 1; +#endif + + // Tell queueing layer to stop sending + // TODO: We should use multiple Tx buffers and + // re-enable the queue (netif_wake_queue()) if + // there's space left in the Tx buffers. + netif_stop_queue(dev); + + // Disable interrupts + spin_lock_irqsave(&local->slock,flags); + + // Prepare packet + p = skb->data; + len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN; + + // Add Ethernet-II frame encapsulation, because + // HCF-light doesn't support that. + if (p[13] + (p[12] << 8) > 1500) + { + hcf_put_data(&local->ifb, p, 12, 0); + len += sizeof(snap_header); + snap_header[1] = (len-0x0e) & 0xff; + snap_header[0] = (char)((len-0x0e) >> 8); + hcf_put_data(&local->ifb, snap_header, sizeof(snap_header), 0); + hcf_put_data(&local->ifb, p+12, len-12-sizeof(snap_header), 0); + } + else + hcf_put_data(&local->ifb, p, len, 0); + + // Send packet + rc = hcf_send(&local->ifb, 0); + + // Remeber time transmission and count tx bytes + dev->trans_start = jiffies; + add_tx_bytes(&local->stats, len); + + // Re-enable interrupts + spin_unlock_irqrestore(&local->slock,flags); + + // It might be no good idea doing a printk() debug output during + // disabled interrupts (I'm not sure...). So better do it here. + DEBUG(DEBUG_TXRX, "%s: Sending 0x%x octets\n", dev_info, len); + DEBUG(DEBUG_NOISY, "%s: hcf_send() returned 0x%x\n", dev_info, rc); + + DEV_KFREE_SKB(skb); + DEBUG(DEBUG_CALLTRACE, "<- wvlan_tx()\n"); + return 0; +} + +void wvlan_rx (struct net_device *dev, int len) +{ + struct net_local *local = (struct net_local *)dev->priv; + struct sk_buff *skb; + u_char *p; + + DEBUG(DEBUG_CALLTRACE, "-> wvlan_rx(%s)\n", dev->name); + + // Create skb packet + skb = dev_alloc_skb(len+2); + if (!skb) + { + printk(KERN_WARNING "%s: %s Rx cannot allocate buffer for new packet\n", dev_info, dev->name); + local->stats.rx_dropped++; + return; + } + DEBUG(DEBUG_TXRX, "%s: Receiving 0x%x octets\n", dev_info, len); + + // Align IP on 16b boundary + skb_reserve(skb, 2); + p = skb_put(skb, len); + dev->last_rx = jiffies; + + // Add Ethernet-II frame decapsulation, because + // HCF-light doesn't support that. + if (local->ifb.IFB_RxStat == 0x2000 || local->ifb.IFB_RxStat == 0x4000) + { + // We copy len-12-sizeof(snap_header) bytes below. Make sure we + // don't accidentally copy a negative count of bytes. + if (len < (12+sizeof(snap_header))) + { + int i; + unsigned char c; + + printk(KERN_WARNING "%s: %s dropping short packet\n", + dev_info, dev->name); + printk(KERN_WARNING "%s: length = %d:", dev_info, len); + for (i = 0; i < len && i < 50; i++) { + hcf_get_data(&local->ifb, i, &c, 1); + printk(" %02x", c); + } + printk("\n"); + local->stats.rx_length_errors++; + local->stats.rx_dropped++; + DEV_KFREE_SKB(skb); + return; + } + + hcf_get_data(&local->ifb, 0, p, 12); + hcf_get_data(&local->ifb, 12+sizeof(snap_header), p+12, len-12-sizeof(snap_header)); + skb_trim(skb, len-sizeof(snap_header)); + } + else + hcf_get_data(&local->ifb, 0, p, len); + + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + skb->ip_summed = CHECKSUM_NONE; + + // Hand the packet over to the kernel + netif_rx(skb); + local->stats.rx_packets++; + add_rx_bytes(&local->stats, len); + +#ifdef WIRELESS_EXT +#if defined(WIRELESS_SPY) || defined(HISTOGRAM) + if ( +#ifdef WIRELESS_SPY + (local->spy_number > 0) || +#endif +#ifdef HISTOGRAM + (local->his_number > 0) || +#endif + 0 ) + { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(1,3,0)) + char *srcaddr = skb->mac.raw + MAC_ADDR_SIZE; +#else + char *srcaddr = skb->data + MAX_ADDR_SIZE; +#endif + u_char stats[3]; + int rc, i; + local->wstats.status = 0; + + // Using spy support with port_type==1 will really + // slow down everything, because the signal quality + // must be queried for each packet here. + // If the user really asks for it (set some address in the + // spy list), we do it, but he will pay the price. + // Note that to get here, you need both WIRELESS_SPY + // compiled in AND some addresses in the list !!! + // TODO: Get and cache stats here so that they + // are available but don't need to be retreived + // every time a packet is received. +#if defined(HISTOGRAM) + // We can't be clever... + rc = hcf_get_data(&local->ifb, HFS_Q_INFO, stats, 2); + DEBUG(DEBUG_NOISY, "%s: hcf_get_data(HFS_Q_INFO) returned 0x%x\n", dev_info, rc); +#else // Therefore WIRELESS_SPY only !!! + memset(&stats, 0, sizeof(stats)); + // Query only for addresses in our list ! + for (i=0; ispy_number; i++) + if (!memcmp(srcaddr, local->spy_address[i], MAC_ADDR_SIZE)) + { + rc = hcf_get_data(&local->ifb, HFS_Q_INFO, stats, 2); + break; + } +#endif + stats[2] = stats[0]; + stats[0] = max(min(stats[1], 0x8a), 0x2f); + stats[1] = max(min(stats[2], 0x8a), 0x2f); + stats[2] = stats[0] - stats[1]; +#ifdef WIRELESS_SPY + wvlan_spy_gather(dev, srcaddr, stats); +#endif +#ifdef HISTOGRAM + wvlan_his_gather(dev, stats); +#endif + } +#endif /* WIRELESS_SPY || HISTOGRAM */ +#endif /* WIRELESS_EXT */ + + DEBUG(DEBUG_CALLTRACE, "<- wvlan_rx()\n"); +} + + +/******************************************************************** + * NET OPEN / CLOSE + */ + +static int wvlan_open (struct net_device *dev) +{ + struct net_local *local = (struct net_local *) dev->priv; + struct dev_link_t *link = local->link; + + DEBUG(DEBUG_CALLTRACE, "-> wvlan_open(%s)\n", dev->name); + + /* Check if we need to re-setup the card */ + if(local->need_commit) { + unsigned long flags; + spin_lock_irqsave(&local->slock,flags); + wvlan_hw_shutdown(dev); + wvlan_hw_config(dev); + local->need_commit = 0; + spin_unlock_irqrestore(&local->slock,flags); + } + + // TODO: Power up the card here and power down on close? + // For now this is done on device init, not on open + // Might be better placed here so that some settings can + // be made by shutting down the device without removing + // the driver (iwconfig). + // But this is no real problem for now :-) + + // Start reception and declare the driver ready + if (!local->ifb.IFB_CardStat) + return -ENODEV; + netif_device_attach(dev); + netif_start_queue(dev); + local->interrupt = 0; + link->open++; + MOD_INC_USE_COUNT; + + DEBUG(DEBUG_CALLTRACE, "<- wvlan_open()\n"); + return 0; +} + +static int wvlan_close (struct net_device *dev) +{ + struct net_local *local = (struct net_local *) dev->priv; + struct dev_link_t *link = local->link; + + // If the device isn't open, then nothing to do + if (!link->open) + { + DEBUG(DEBUG_CALLTRACE, "<> wvlan_close(%s)\n", dev->name); + return 0; + } + + DEBUG(DEBUG_CALLTRACE, "-> wvlan_close(%s)\n", dev->name); + + // Close the device + link->open--; + MOD_DEC_USE_COUNT; + + // Check if card is still present + if (netif_running(dev)) + { + netif_stop_queue(dev); + netif_device_detach(dev); + // TODO: Shutdown hardware (see wvlan_open) + } + else + if (link->state & DEV_STALE_CONFIG) + mod_timer(&link->release, jiffies + HZ/20); + + DEBUG(DEBUG_CALLTRACE, "<- wvlan_close()\n"); + return -EINVAL; +} + + +/******************************************************************** + * INTERRUPT HANDLER + */ +static void wvlan_interrupt (int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct net_local *local = (struct net_local *) dev->priv; + int rc, cnt, ev, len; + + DEBUG(DEBUG_INTERRUPT, "-> wvlan_interrupt(%d)\n", irq); + + // Check device + if (!dev) + { + printk(KERN_WARNING "%s: IRQ %d for unknown device!\n", dev_info, irq); + return; + } + + /* Prevent reentrancy. We need to do that because we may have + * multiple interrupt handler running concurently. + * It is safe because wv_driver_lock() disable interrupts before + * aquiring the spinlock. */ + spin_lock(&local->slock); + + // Turn off interrupts + rc = hcf_action(&local->ifb, HCF_ACT_INT_OFF); + DEBUG(DEBUG_NOISY, "%s: hcf_action(HCF_ACT_INT_OFF) returned 0x%x\n", dev_info, rc); + /* Check state of interrupt */ + if (test_and_set_bit(0, (void *)&local->interrupt)) + printk(KERN_DEBUG "%s: Warning: IRQ %d Reentering interrupt handler!\n", dev_info, irq); + + // Process pending interrupts. + // We continue until hcf_service_nic tells that no received + // frames are pending. However we should check to not lock up + // here in an endless loop. + cnt = 7; + while (cnt--) + { + // Ask NIC why interrupt occurred + ev = hcf_service_nic(&local->ifb); + DEBUG(DEBUG_NOISY, "%s: hcf_service_nic() returned 0x%x RscInd 0x%x\n", dev_info, ev, local->ifb.IFB_PIFRscInd); + + // Transmission completion seem to be also signalled with ev==0 + // better check that out with RscInd and complete transfer also + if (local->ifb.IFB_PIFRscInd && netif_queue_stopped(dev)) + ev |= HREG_EV_TX; + + // HREG_EV_TICK: WMAC controller auxiliary timer tick + if (ev & HREG_EV_TICK) + { + DEBUG(DEBUG_INFO,"%s: Auxiliary timer tick\n", dev_info); + } + + // HREG_EV_RES: WMAC controller H/W error (wait timeout) + if (ev & HREG_EV_RES) + { + // This message seems to occur often on heavy load + // but it seem to don't have any effects on transmission + // so we simply ignore it. + //printk(KERN_WARNING "%s: WMAC H/W error (wait timeout, ignoring)!\n", dev_info); + } + + // HREG_EV_INFO_DROP: WMAC did not have sufficient RAM to build unsollicited frame + if (ev & HREG_EV_INFO_DROP) + printk(KERN_WARNING "%s: WMAC did not have sufficient RAM to build unsollicited frame!\n", dev_info); + + // HREG_EV_INFO: WMAC controller asynchronous information frame + if (ev & HREG_EV_INFO) + { + DEBUG(DEBUG_INFO, "%s: WMAC controller asynchronous information frame\n", dev_info); + } + + // HREG_EV_CMD: WMAC controller command completed, status and response available + // unnecessary to handle here, it's handled by polling in HCF + + // HREG_EV_ALLOC: WMAC controller asynchronous part of allocation/reclaim completed + // also unnecessary to handle here, it's handled by polling in HCF + + // HREG_EV_TX_EXC: WMAC controller asynchronous transmission unsuccessful completed + if (ev & HREG_EV_TX_EXC) + { + printk(KERN_WARNING "%s: WMAC controller asynchronous transmission unsuccessful completed\n", dev_info); + local->stats.tx_errors++; + netif_wake_queue(dev); + } + + // HREG_EV_TX: WMAC controller asynchronous transmission successful completed + if (ev & HREG_EV_TX) + { + DEBUG(DEBUG_TXRX, "%s: Transmission successful completed\n", dev_info); + local->stats.tx_packets++; + netif_wake_queue(dev); + } + + // HREG_EV_RX: WMAC controller asynchronous receive frame + // Break loop if no frame was received. + if (!(ev & HREG_EV_RX)) + break; + + // If a frame was received, we process it and wrap back + // up to the top of the while() loop so that hcf_service_nic() + // gets called again after the frame drained from the NIC. + // This allows us to find out if yet another frame has + // arrived, and also to immediately acknowledge the just- + // processed frame so that the NIC's buffer gets de- + // allocated right away. + len = local->ifb.IFB_RxLen; + if (len) + { + DEBUG(DEBUG_INTERRUPT, "%s: Frame received. rx_len=0x%x\n", dev_info, len); + wvlan_rx(dev, len); + } + } + if (!cnt) + printk(KERN_WARNING "%s: Maximum interrupt loops reached!\n", dev_info); + + // From now on, we don't care if we re-enter the interrupt handler + local->interrupt = 0; + + // Turn back interrupts on (unlock) + rc = hcf_action(&local->ifb, HCF_ACT_INT_ON); + DEBUG(DEBUG_NOISY, "%s: hcf_action(HCF_ACT_INT_ON) returned 0x%x\n", dev_info, rc); + + /* Release spinlock */ + spin_unlock (&local->slock); + + DEBUG(DEBUG_INTERRUPT, "<- wvlan_interrupt()\n"); +} + + +/******************************************************************** + * PCMCIA CONFIG / RELEASE + */ +#define CS_CHECK(fn, args...) while ((last_ret=CardServices(last_fn=(fn),args))!=0) goto cs_failed +#define CFG_CHECK(fn, args...) if (CardServices(fn, args) != 0) goto next_entry +static int wvlan_config (dev_link_t *link) +{ + client_handle_t handle = link->handle; + tuple_t tuple; + cisparse_t parse; + struct net_device *dev = (struct net_device *) link->priv; + struct net_local *local = (struct net_local *) dev->priv; + int last_fn, last_ret; + u_char buf[64]; + win_req_t req; + memreq_t map; + int rc, i; + config_info_t config; + cistpl_cftable_entry_t dflt = { 0 }; + + DEBUG(DEBUG_CALLTRACE, "-> wvlan_config(0x%p)\n", link); + + // This reads the card's CONFIG tuple to find its configuration registers. + tuple.DesiredTuple = CISTPL_CONFIG; + tuple.Attributes = 0; + tuple.TupleData = buf; + tuple.TupleDataMax = sizeof(buf); + tuple.TupleOffset = 0; + CS_CHECK(GetFirstTuple, handle, &tuple); + CS_CHECK(GetTupleData, handle, &tuple); + CS_CHECK(ParseTuple, handle, &tuple, &parse); + link->conf.ConfigBase = parse.config.base; + link->conf.Present = parse.config.rmask[0]; + + // Configure card + link->state |= DEV_CONFIG; + + // Use card's current Vcc setting + CS_CHECK(GetConfigurationInfo, handle, &config); + link->conf.Vcc = config.Vcc; + + // In this loop, we scan the CIS for configuration table entries, + // each of which describes a valid card configuration, including + // voltage, IO window, memory window, and interrupt settings. + // We make no assumptions about the card to be configured: we use + // just the information available in the CIS. In an ideal world, + // this would work for any PCMCIA card, but it requires a complete + // and accurate CIS. In practice, a driver usually "knows" most of + // these things without consulting the CIS, and most client drivers + // will only use the CIS to fill in implementation-defined details. + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + CS_CHECK(GetFirstTuple, handle, &tuple); + while (1) { + cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); + CFG_CHECK(GetTupleData, handle, &tuple); + CFG_CHECK(ParseTuple, handle, &tuple, &parse); + + if (cfg->index == 0) goto next_entry; + link->conf.ConfigIndex = cfg->index; + + // Does this card need audio output? + if (cfg->flags & CISTPL_CFTABLE_AUDIO) + { + link->conf.Attributes |= CONF_ENABLE_SPKR; + link->conf.Status = CCSR_AUDIO_ENA; + } + + // Use power settings for Vpp if present + // Note that the CIS values need to be rescaled + + if (cfg->vpp1.present & (1<conf.Vpp1 = link->conf.Vpp2 = cfg->vpp1.param[CISTPL_POWER_VNOM]/10000; + else if (dflt.vpp1.present & (1<conf.Vpp1 = link->conf.Vpp2 = dflt.vpp1.param[CISTPL_POWER_VNOM]/10000; + + // Do we need to allocate an interrupt? + if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1) + link->conf.Attributes |= CONF_ENABLE_IRQ; + + // IO window settings + link->io.NumPorts1 = link->io.NumPorts2 = 0; + if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) { + cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; + if (!(io->flags & CISTPL_IO_8BIT)) + link->io.Attributes1 = IO_DATA_PATH_WIDTH_16; + if (!(io->flags & CISTPL_IO_16BIT)) + link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + link->io.BasePort1 = io->win[0].base; + link->io.NumPorts1 = io->win[0].len; + if (io->nwin > 1) { + link->io.Attributes2 = link->io.Attributes1; + link->io.BasePort2 = io->win[1].base; + link->io.NumPorts2 = io->win[1].len; + } + } + + // This reserves IO space but doesn't actually enable it + CFG_CHECK(RequestIO, link->handle, &link->io); + + // Now set up a common memory window, if needed. There is room + // in the dev_link_t 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. + // Note that the memory window base is a physical address, and + // needs to be mapped to virtual space with ioremap() before it + // is used. + if ((cfg->mem.nwin > 0) || (dflt.mem.nwin > 0)) { + cistpl_mem_t *mem = (cfg->mem.nwin) ? &cfg->mem : &dflt.mem; + req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM; + req.Base = mem->win[0].host_addr; + req.Size = mem->win[0].len; + req.AccessSpeed = 0; + link->win = (window_handle_t)link->handle; + CFG_CHECK(RequestWindow, &link->win, &req); + map.Page = 0; map.CardOffset = mem->win[0].card_addr; + CFG_CHECK(MapMemPage, link->win, &map); + } + + // If we got this far, we're cool! + break; + +next_entry: + if (cfg->flags & CISTPL_CFTABLE_DEFAULT) + dflt = *cfg; + CS_CHECK(GetNextTuple, handle, &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) + { + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; + link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID; + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i=0; i<4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->irq.Handler = wvlan_interrupt; + link->irq.Instance = dev; + CS_CHECK(RequestIRQ, link->handle, &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, link->handle, &link->conf); + + // Feed the netdevice with this info + dev->irq = link->irq.AssignedIRQ; + dev->base_addr = link->io.BasePort1; + netif_start_queue(dev); + + // 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); + if (link->conf.Attributes & CONF_ENABLE_IRQ) + printk(", irq %d", link->irq.AssignedIRQ); + if (link->io.NumPorts1) + printk(", io 0x%04x-0x%04x", link->io.BasePort1, link->io.BasePort1+link->io.NumPorts1-1); + if (link->io.NumPorts2) + printk(" & 0x%04x-0x%04x", link->io.BasePort2, link->io.BasePort2+link->io.NumPorts2-1); + if (link->win) + printk(", mem 0x%06lx-0x%06lx", req.Base, req.Base+req.Size-1); + printk("\n"); + + link->state &= ~DEV_CONFIG_PENDING; + + // Make netdevice's name (if not ethX) and remember the device + // Not very efficient here, this should go somewhere into dev_list, + // but it works for now (taken from register_netdevice in kernel) + /* Note : may fail if other drivers are also using this name */ + if(!eth) + { + for (i=0; iname, "wvlan%d", i); + wvlandev_index[i] = dev; + break; + } + } + + // Register the netdevice + rc = register_netdev(dev); + if (rc) + { + printk(KERN_WARNING "%s: register_netdev() failed!\n", dev_info); + wvlan_release((u_long)link); + return 0; + } + printk(KERN_INFO "%s: Registered netdevice %s\n", dev_info, dev->name); + + strcpy(local->node.dev_name, (dev)->name); + link->dev = &local->node; + + // Success! + DEBUG(DEBUG_CALLTRACE, "<- wvlan_config()\n"); + return 1; + +cs_failed: + cs_error(link->handle, last_fn, last_ret); + wvlan_release((u_long)link); + DEBUG(DEBUG_CALLTRACE, "<- wvlan_config()\n"); + return 0; +} + +static void wvlan_release (u_long arg) +{ + dev_link_t *link = (dev_link_t *) arg; + struct net_device *dev = (struct net_device *) link->priv; + struct net_local *local = (struct net_local *) dev->priv; + unsigned long flags; + int i; + + DEBUG(DEBUG_CALLTRACE, "-> wvlan_release(0x%p)\n", link); + + // If the device is currently in use, we won't release + // until it's actually closed. + if (link->open) + { + DEBUG(DEBUG_INFO, "%s: wvlan_release: release postponed, %s still locked\n", dev_info, link->dev->dev_name); + link->state |= DEV_STALE_CONFIG; + return; + } + + // Power down - IRQ currently enabled, so disable it + spin_lock_irqsave(&local->slock,flags); + wvlan_hw_shutdown(dev); + spin_unlock_irqrestore(&local->slock,flags); + + // Remove our device from index (only devices named wvlanX) + for (i=0; iwin) + CardServices(ReleaseWindow, link->win); + CardServices(ReleaseConfiguration, link->handle); + if (link->io.NumPorts1) + CardServices(ReleaseIO, link->handle, &link->io); + if (link->irq.AssignedIRQ) + CardServices(ReleaseIRQ, link->handle, &link->irq); + + link->state &= ~DEV_CONFIG; + + DEBUG(DEBUG_CALLTRACE, "<- wvlan_release()\n"); +} + + +/******************************************************************** + * PCMCIA ATTACH / DETACH + */ +static dev_link_t *wvlan_attach (void) +{ + dev_link_t *link; + struct net_device *dev; + struct net_local *local; + int rc; + client_reg_t client_reg; + + DEBUG(DEBUG_CALLTRACE, "-> wvlan_attach()\n"); + + // Flush stale links + for (link=dev_list; link; link=link->next) + if (link->state & DEV_STALE_LINK) + wvlan_detach(link); + + // Initialize the dev_link_t structure + link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL); + memset(link, 0, sizeof(struct dev_link_t)); + link->release.function = &wvlan_release; + link->release.data = (u_long) link; + link->conf.IntType = INT_MEMORY_AND_IO; + + // Allocate space for netdevice (private data of link) + dev = kmalloc(sizeof(struct net_device), GFP_KERNEL); + memset(dev, 0, sizeof(struct net_device)); + link->priv = dev; + + // Allocate space for netdevice priv (private data of netdevice) + local = kmalloc(sizeof(struct net_local), GFP_KERNEL); + memset(local, 0, sizeof(struct net_local)); + dev->priv = local; + + // Initialize specific data + local->link = link; + local->dev = dev; + spin_lock_init(&local->slock); + // Copy modules parameters to private struct + local->port_type = port_type; + local->allow_ibss = allow_ibss; + strcpy(local->network_name, network_name); + local->channel = channel; + // Initialise Wireless Extension stuff +#ifdef WIRELESS_EXT + local->station_name[0] = '\0'; + local->ap_density = 1; + local->medium_reservation = 2347; + local->frag_threshold = 2346; + local->mwo_robust = 0; + local->transmit_rate = 3; + local->wep_on = 0; + local->pm_on = 0; + local->pm_multi = 1; + local->pm_period = 100000; + // Check obsolete module parameters + if(*(station_name)) { + strcpy(local->station_name, station_name); + printk(KERN_INFO "%s: ``station_name'' is an obsolete module parameter, please use iwconfig.", dev_info); + } +#endif /* WIRELESS_EXT */ + + // Standard setup for generic data + ether_setup(dev); + + // kernel callbacks + dev->open = wvlan_open; + dev->stop = wvlan_close; + dev->hard_start_xmit = wvlan_tx; + dev->get_stats = wvlan_get_stats; +#ifdef WIRELESS_EXT + dev->do_ioctl = wvlan_ioctl; + dev->get_wireless_stats = wvlan_get_wireless_stats; +#endif /* WIRELESS_EXT */ + dev->change_mtu = wvlan_change_mtu; + dev->set_multicast_list = wvlan_set_multicast_list; +// dev->set_mac_address = wvlan_set_mac_address; +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,42)) + dev->tx_timeout = &wvlan_watchdog; + dev->watchdog_timeo = TX_TIMEOUT; +#endif + + // Other netdevice data + strcpy(dev->name, local->node.dev_name); + + dev->mtu = mtu; + netif_stop_queue(dev); + + // Register with CardServices + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT; + client_reg.EventMask = CS_EVENT_REGISTRATION_COMPLETE | + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &wvlan_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + + rc = CardServices(RegisterClient, &link->handle, &client_reg); + if (rc) + { + cs_error(link->handle, RegisterClient, rc); + wvlan_detach(link); + return NULL; + } + + DEBUG(DEBUG_CALLTRACE, "<- wvlan_attach()\n"); + return link; +} + +static void wvlan_detach (dev_link_t *link) +{ + dev_link_t **linkp; + + DEBUG(DEBUG_CALLTRACE, "-> wvlan_detach(0x%p)\n", link); + + // Locate device structure + for (linkp=&dev_list; *linkp; linkp=&(*linkp)->next) + if (*linkp == link) + break; + if (!*linkp) + { + printk(KERN_WARNING "%s: Attempt to detach non-existing PCMCIA client!\n", dev_info); + return; + } + + // 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() + del_timer(&link->release); + if (link->state & DEV_CONFIG) + { + DEBUG(DEBUG_INFO, "%s: wvlan_detach: detach postponed, %s still locked\n", dev_info, link->dev->dev_name); + wvlan_release((u_long)link); + if (link->state & DEV_STALE_CONFIG) + { + link->state |= DEV_STALE_LINK; + return; + } + } + + // Break the line with CardServices + if (link->handle) + CardServices(DeregisterClient, link->handle); + + // Unlink device structure, free pieces + *linkp = link->next; + if (link->priv) + { + struct net_device *dev = (struct net_device *) link->priv; + if (link->dev) + { + unregister_netdev(dev); + DEBUG(DEBUG_INFO, "%s: Netdevice unregistered\n", dev_info); + } + if (dev->priv) + kfree(dev->priv); + kfree(link->priv); + } + kfree(link); + + DEBUG(DEBUG_CALLTRACE, "<- wvlan_detach()\n"); +} + + +/******************************************************************** + * PCMCIA EVENT HANDLER + */ +static int wvlan_event (event_t event, int priority, event_callback_args_t *args) +{ + dev_link_t *link = (dev_link_t *) args->client_data; + struct net_device *dev = (struct net_device *) link->priv; + + DEBUG(DEBUG_CALLTRACE, "-> wvlan_event(%s, %d, 0x%p)\n", + ((event==CS_EVENT_REGISTRATION_COMPLETE) ? "registration complete" : + ((event==CS_EVENT_CARD_INSERTION) ? "card insertion" : + ((event==CS_EVENT_CARD_REMOVAL) ? "card removal" : + ((event==CS_EVENT_RESET_PHYSICAL) ? "physical physical" : + ((event==CS_EVENT_CARD_RESET) ? "card reset" : + ((event==CS_EVENT_PM_SUSPEND) ? "pm suspend" : + ((event==CS_EVENT_PM_RESUME) ? "pm resume" : + "unknown"))))))), priority, args); + + switch (event) + { + case CS_EVENT_CARD_INSERTION: + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + if (!wvlan_config(link) || wvlan_hw_config_locked(dev)) + dev->irq = 0; + break; + + case CS_EVENT_CARD_REMOVAL: + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) + { + netif_stop_queue(dev); + netif_device_detach(dev); + mod_timer(&link->release, jiffies + HZ/20); + } + break; + + case CS_EVENT_PM_SUSPEND: + link->state |= DEV_SUSPEND; + case CS_EVENT_RESET_PHYSICAL: + if (link->state & DEV_CONFIG) + { + if (link->open) + { + netif_stop_queue(dev); + netif_device_detach(dev); + } + CardServices(ReleaseConfiguration, link->handle); + } + break; + + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + // Fall through + case CS_EVENT_CARD_RESET: + if (link->state & DEV_CONFIG) + { + CardServices(RequestConfiguration, link->handle, &link->conf); + if (link->open) + { + struct net_local *local = + (struct net_local *) dev->priv; + unsigned long flags; + + /* IRQ currently enabled, + * so disable it */ + spin_lock_irqsave(&local->slock,flags); + wvlan_hw_shutdown(dev); + wvlan_hw_config(dev); + spin_unlock_irqrestore(&local->slock,flags); + netif_device_attach(dev); + netif_start_queue(dev); + } + } + break; + } + + DEBUG(DEBUG_CALLTRACE, "<- wvlan_event()\n"); + return 0; +} + + +/******************************************************************** + * MODULE INSERTION / REMOVAL + */ +extern int __init init_wvlan_cs (void) +{ + servinfo_t serv; + + DEBUG(DEBUG_CALLTRACE, "-> init_module()\n"); + + printk(KERN_INFO "%s: WaveLAN/IEEE PCMCIA driver v%s\n", dev_info, version); + printk(KERN_INFO "%s: (c) Andreas Neuhaus \n", dev_info); + + // Check CardServices release + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) + { + printk(KERN_WARNING "%s: CardServices release does not match!\n", dev_info); + return -1; + } + + // Register PCMCIA driver + register_pcmcia_driver(&dev_info, &wvlan_attach, &wvlan_detach); + + DEBUG(DEBUG_CALLTRACE, "<- init_module()\n"); + return 0; +} + +extern void __exit exit_wvlan_cs (void) +{ + DEBUG(DEBUG_CALLTRACE, "-> cleanup_module()\n"); + + // Unregister PCMCIA driver + unregister_pcmcia_driver(&dev_info); + + // Remove leftover devices + if (dev_list) + DEBUG(DEBUG_INFO, "%s: Removing leftover devices!\n", dev_info); + while (dev_list) + { + if (dev_list->state & DEV_CONFIG) + wvlan_release((u_long)dev_list); + wvlan_detach(dev_list); + } + + printk(KERN_INFO "%s: Driver unloaded\n", dev_info); + DEBUG(DEBUG_CALLTRACE, "<- cleanup_module()\n"); +} + +module_init(init_wvlan_cs); +module_exit(exit_wvlan_cs); +MODULE_LICENSE("GPL"); + +/******************************************************************** + * EOF + */ diff -urN orig/drivers/net/pcmcia/wvlan_hcf.c linux/drivers/net/pcmcia/wvlan_hcf.c --- orig/drivers/net/pcmcia/wvlan_hcf.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/net/pcmcia/wvlan_hcf.c Wed Oct 9 19:03:08 2002 @@ -0,0 +1,1978 @@ +/* This file is part of the Hardware Control Functions Light (HCF-light) library + to control the Lucent Technologies WaveLAN/IEEE Network I/F Card. + The HCF is the implementation of the Wireless Connection I/F (WCI). + + The HCF-light files are a subset of the HCF files. The complete set offers a + number of additional facilities, e.g. firmware download, Etherner-II encapsulation, + additional diagnostic facilities, ASSERT logic to support debugging, 802.11 support, + Configuration Management. + This complete set is explicitely not in the Public Domain but can be made + available under certain restriction. (see the pointer below for support) + + The HCF-light files are 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. + + At the time of this writing, you can request for support at: + betasupport@wavelan.com + + Documentation is expected to be available in the week of 8 Februari 1999 + +*/ + + +/************************************************************************************************************** +* +* FILE : HCF.CPP *************** 2.0 *********************************************************************** +* +* DATE : 2000/01/06 23:30:52 1.2 +* +* AUTHOR : Nico Valster +* +* DESC : HCF Routines hcf_action, hcf_connect, hcf_disable +* hcf_disconnect, hcf_download, hcf_enable, hcf_generate_int +* hcf_get_info, hcf_get_data, hcf_service_nic +* hcf_put_info, hcf_put_data, hcf_register_mailbox, hcf_send, +* hcf_send_diag_msg +* Local Support Routines for above procedures +* +* Customizable via HCFCFG.H, which is included by HCF.H +* +*************************************************************************************************************** +* COPYRIGHT (c) 1995 by AT&T. All Rights Reserved +* COPYRIGHT (c) 1996, 1997, 1998 by Lucent Technologies. All Rights Reserved +* +* At the sole discretion of Lucent Technologies parts of this source may be extracted +* and placed in the Public Domain under the GPL. +* This extraction takes place by means of an AWK-script acting on embedded tags. +* The AWK script is: + * BEGIN { c = 0 } + * { if ( c == 0 ) i = 1} #if in @HCF_L>..@HCF_L< block, skip + * { if (name != FILENAME ) name = FILENAME } + * + * { if ( i && c == 0 ) { print ; hcf_l_cnt++ } } + * #{ if ( i ) { print ; hcf_l_cnt++ } } + * #{if ( c == 0 ) { printf("N%d", c) ; hcf_l_cnt++ } } + * #{if ( c == 1 ) { printf("E%d", c) ; hcf_l_cnt++ } } + * + * #END { printf("%s:: HCF lines: %d, HCF_Light lines: %d", name, NR, hcf_l_cnt ) } +* +* and is not in any sense derived from the extracted source. +* +**************************************************************************************************************/ + + + + + +/**************************************************************************** +wvlan_hcf.c,v +Revision 1.2 2000/01/06 23:30:52 root +*** empty log message *** + + * + * Rev 1.0 02 Feb 1999 14:32:28 NVALST + * Initial revision. +Revision 1.3 1999/02/01 22:58:40 nico +*** empty log message *** + * + * Rev 2.12 29 Jan 1999 10:48:40 NVALST + * + * Rev 1.108 28 Jan 1999 14:43:18 NVALST + * intermediate, once more correction of loop in hcf_service_nic + download + * passed to Marc + * +****************************************************************************/ + +/************************************************************************************************************** +* +* CHANGE HISTORY +* + + 960702 - NV + Original Entry - derived from WaveLAN-I HCF 2.12 + + +* +* ToDo +* + 1: For all/most functions, update "MSF-accessible fields of Result Block:" entry + 2: Use the "numbered comments" in the NARRATIVE consistently, i.e. hcf_put_info + 3: hcf_put_data, hcf_send, hcf_send_diag_msg + once the dust is settled whether hcf_put_data or hcf_send is the appropriate place is to specify port, + it can be considered whether part of the hcf_send_diag_msg and hcf_send can be isolated in a common + routine. + 4: hcf_send_diag_msg: + - what are the appropriate return values + - once the dust is settled whether type should or shouldn't be parameter of hcf_send_diag_msg, it can + be decided whether the HFX_TX_CNTL_ABS update at each call is needed + 5: hcf_service_nic, hcf_send, hcf_send_diag_msg etc + check for a CONSISTENT strategy for the testing of IFB_CardStat, for presence, enabled, ports + 6: Decide on the relative merits of HCF_ACT_ASSERT_OFF/_ON versus CFG_REG_MSF_ASSERT + + +* +* Implementation Notes +* + - C++ style cast is not used to keep DA-C happy + - a leading marker of //! is used. The purpose of such a sequence is to help the + (maintenance) programmer to understand the flow + An example in hcf_action( HCF_ACT_802_3 ) is + //! ifbp->IFB_RxFence = 0; + which is superfluous because IFB_RxFence gets set at every hcf_service_nic but + it shows to the (maintenance) programmer it is an intentional omission at + the place where someone could consider it most appropriate at first glance + - using near pointers in a model where ss!=ds is an invitation for disaster, so be aware of how you specify + your model and how you define variables which are used at interrupt time + - Once the comment "the value of -1 for parameter len is meaningless but it guarantees that the next call + to bap_ini is interpreted as an initial call, causing the BAP to be really initialized." was considered + useful information. Does this trick still lingers somewhere;? + - remember that sign extension on 32 bit platforms may cause problems unless code is carefully constructed, + e.g. use "(hcf_16)~foo" rather than "~foo" + + +* +* Miscellaneous Notes +* + - AccessPoint performance could be improved by adding a hcf_send_pif_msg equivalent of hcf_send_diag_msg + + +*************************************************************************************************************/ + +#include "wvlan_hcf.h" // HCF and MSF common include file +#include "wvlan_hcfdef.h" // HCF specific include file + +/*************************************************************************************************************/ +/*************************************** PROTOTYPES ********************************************************/ +/*************************************************************************************************************/ +// moving these prototypes to HCFDEF.H turned out to be less attractive in the HCF-light generation +STATIC int aux_cntl( IFBP ifbp, hcf_16 cmd ); +STATIC int calibrate( IFBP ifbp ); +STATIC int cmd_wait( IFBP ifbp, int cmd_code, int par_0 ); +STATIC void enable_int(IFBP ifbp, int event ); + int hcf_initialize( IFBP ifbp ); +STATIC int ini_hermes( IFBP ifbp ); +STATIC void isr_info( IFBP ifbp ); +STATIC int put_info( IFBP ifbp, LTVP ltvp ); +STATIC hcf_16 alloc( IFBP ifbp, int len ); + + +/************************************************************************************************************** +******************************* D A T A D E F I N I T I O N S ********************************************** +**************************************************************************************************************/ + +STATIC hcf_8 BASED hcf_rev[] = "\nHCF1.2\n"; + +/* Note that the "BASED" construction (supposedly) only amounts to something in the small memory model. + * In that case CS and DS are equal, so we can ignore the consequences of casting the BASED cfg_drv_... + * structure to hcf_16 + * Note that the whole BASED riggamarole is needlessly complicated because both the Microsoft Compiler and + * Linker are unnecessary restrictive in what far pointer manipulation they allow + */ + + +/* + The below table accessed via a computed index was the original implementation for hcf_get_info with + CFG_DRV_IDENTITY, CFG_DRV_SUP_RANGE, CFG_DRV_ACT_RANGE_PRI, CFG_DRV_ACT_RANGE_STA, CFG_DRV_ACT_RANGE_HSI + as type. However it was reported that the 68K compiler for MAC OS is unable to initialize pointers. + Accepting this story at face value, the HCF is coded around this problem by implementing a direct access.. + To save part of the invested effort, the original table is kept as comment. + +STATIC LTV_STRCT* BASED xxxx[ ] = { + (LTV_STRCT*)&cfg_drv_identity, //CFG_DRV_IDENTITY 0x0826 + (LTV_STRCT*)&cfg_drv_sup_range, //CFG_DRV_SUP_RANGE 0x0827 + (LTV_STRCT*)&cfg_drv_act_range_pri, //CFG_DRV_ACT_RANGE_PRI 0x0828 + (LTV_STRCT*)&cfg_drv_act_range_sta //CFG_DRV_ACT_RANGE_STA 0x0829 + (LTV_STRCT*)&cfg_drv_act_range_hsi //CFG_DRV_ACT_RANGE_HSI 0x082A + }; +*/ + + +/************************************************************************************************************** +************************** T O P L E V E L H C F R O U T I N E S **************************************** +**************************************************************************************************************/ + + +/******************************************************************************************************************* + + +.MODULE hcf_action +.LIBRARY HCF +.TYPE function +.SYSTEM msdos +.SYSTEM NW4 +.APPLICATION Card configuration +.DESCRIPTION Changes the run-time Card behavior + +.ARGUMENTS + int hcf_action(IFBP ifbp, hcf_action_cmd action ) +.RETURNS + int + + MSF-accessible fields of Result Block: - + +.NARRATIVE + + Name: hcf_action + + Summary: Changes the run-time Card behavior + + Parameters: + ifbp address of the Interface Block + + action number identifying the type of change + + o HCF_ACT_INT_ON enable interrupt generation by WaveLAN NIC + o HCF_ACT_INT_OFF disable interrupt generation by WaveLAN NIC + o HCF_ACT_CARD_IN MSF reported Card insertion + o HCF_ACT_CARD_OUT MSF reported Card removal + + Returns: + o HCF_ACT_INT_OFF + 0: no interrupt pending + 1: interrupt pending + o all other + 0 (((however, see the special treatment for HCF_ACT_INT_ON))) + + Remarks: + o HCF_ACT_INT_OFF/HCF_ACT_INT_ON codes may be nested but must be balanced. The INT_OFF/INT_ON housekeeping + is initialized by hcf_connect with a call of hcf_action with INT_OFF, causing the interrupt generation + mechanism to be disabled at first. This suits MSF implementation based on a polling strategy. An MSFT + based on a interrupt strategy must call hcf_action with INT_ON in its initialization logic. + + o To prevent I/O while the I/O space is no longer owned by the HCF, due to a card swap, no I/O is allowed + when the CARD_STAT_PRESENT bit of IFB_CardStat is off. + +.DIAGRAM + 2: IFB_IntOffCnt is used to balance the INT_OFF and INT_ON calls. + 4: Disabling of the interrupts is simply achieved by writing a zero to the Hermes IntEn register + 5: To be able to return the information to the MSF whether an interrupt is actually pending, the Hermes + EvStat register is sampled and compared against the current IFB_IntEnMask value + 6: Originally the construction "if ( ifbp->IFB_IntOffCnt-- <= 1 )" was used in stead of + "if ( --ifbp->IFB_IntOffCnt == 0 )". This serviced to get around the unsigned logic, but as additional + "benefit" it seemed the most optimal "fail safe" code (in the sense of shortest/quickest path in error + free flows, fail safe in the sense of too many INT_ON invocations compared to INT_OFF). However when a + real life MSF programmer ran to a MSF sequence problem, exactly causing that problem, he was annoyed + with this fail safe code. As a consequence it is taken out. As a side-effect of this unhappy MSF programmer + adventures to find his problem, the return status is defined to reflect the IFBIntOffCnt, Note that this + is solely intended for aid debugging, no MSF logic should depend on this feature, No garuantees for the + future are given. + Enabling of the interrupts is achieved by writing the contents of IFB_IntEnMask to the Hermes IntEn + register. + 7: Since the card is present again, it must be re-initialized. Since this may be another card we may as well + clear all bits in IFB_CardStat and set only the "present" bit. + The first call to hcf_enable will restore the contents of HREG_INT_EN register taking the + HCF_ACT_IN_ON/OFF history in account. + 9: The MSF must call hcf_action with HCF_ACT_CARD_OUT when the MSF detects a card removal (e.g. when the MSF + is notified by the CAD). As a minimum, the "present" bit in IFB_CardStat must be reset, however since + the card insertion will clear all other bits, the simplest solution is to clear IFB_CardStat here as well. + As a result of the resetting of the CARD_STAT_PRESENT bit, no hcf-function except hcf_action with + HCF_ACT_CARD_IN results in card I/O anymore. However hcf_functions may still perform their other + activities, e.g. hcf_get_info_mb still supplies a MBIB if one is available. + As a result of the resetting of the CARD_STAT_INI bit, the call to hcf_initialize by hcf_action with + HCF_ACT_CARD_IN results in re-initialization of the NIC. +.ENDOC END DOCUMENTATION + + +**************************************************************************************************************/ +int hcf_action( IFBP ifbp, //address of the Interface Block + hcf_action_cmd action /*number identifying the type of change + */ + ) { + +int rc = HCF_SUCCESS; +//int i, j; +//hcf_16 scratch[2]; + + + + switch (action) { + case HCF_ACT_INT_OFF: // Disable Interrupt generation + ifbp->IFB_IntOffCnt++; /* 2 */ + if ( ifbp->IFB_CardStat & CARD_STAT_PRESENT ) { + OUT_PORT_WORD( ifbp->IFB_IOBase + HREG_INT_EN, 0 ); /* 4 */ + if ( IN_PORT_WORD( ifbp->IFB_IOBase + HREG_EV_STAT ) & ifbp->IFB_IntEnMask ) { /* 5 */ + rc = HCF_INT_PENDING; + } + } + break; + + case HCF_ACT_INT_ON: // Enable Interrupt generation + if ( --ifbp->IFB_IntOffCnt == 0 ) { /* 6 */ + if ( ifbp->IFB_CardStat & CARD_STAT_PRESENT ) { + OUT_PORT_WORD( ifbp->IFB_IOBase + HREG_INT_EN, ifbp->IFB_IntEnMask ); + } + } + rc = ifbp->IFB_IntOffCnt; + break; + + case HCF_ACT_CARD_IN: // MSF reported Card insertion /* 7 */ + ifbp->IFB_CardStat = CARD_STAT_PRESENT; + hcf_initialize ( ifbp ); + + if ( ifbp->IFB_CardStat & CARD_STAT_ENABLED ) { + (void)hcf_enable( ifbp, 0 ); + } + break; + + case HCF_ACT_CARD_OUT: // MSF reported Card removal /* 9 */ + ifbp->IFB_CardStat = 0; + break; + + + case HCF_ACT_TALLIES: // Hermes Inquire Tallies (F100) command /*12 */ + action = (hcf_action_cmd)(action - HCF_ACT_TALLIES + CFG_TALLIES); + if ( ifbp->IFB_CardStat & CARD_STAT_ENABLED ) { + rc = cmd_wait( ifbp, HCMD_INQUIRE, action ); + } + break; + + + + + default: + break; + } + return rc; +}/* hcf_action */ + + + +/******************************************************************************************************************* + +.MODULE hcf_connect +.LIBRARY HCF +.TYPE function +.SYSTEM msdos +.SYSTEM unix +.SYSTEM NW4 +.APPLICATION Card Initialization Group for WaveLAN based drivers and utilities +.DESCRIPTION Initializes Card and HCF housekeeping + +.ARGUMENTS + void hcf_connect( IFBP ifbp, hcf_io io_base ) + +.RETURNS + n.a. + + MSF-accessible fields of Result Block: + IFB_IOBase entry parameter io_base + IFB_IORange HREG_IO_RANGE (0x40) + IFB_HCFVersionMajor the major part of the PVCS maintained version number + IFB_HCFVersionMinor the minor part of the PVCS maintained version number + IFB_Version version of the IFB layout (0x01 for this release) + +.NARRATIVE + + Parameters: + ifbp address of the Interface Block + io_base I/O Base address of the NIC + + + Hcf_connect grants access right for the HCF to the IFB and initializes the HCF housekeeping part of the + IFB. Hcf_connect does not perform any I/O. + + The HCF-Version fields are set dynamically, because I do not know of any C mechanism to have the compiler + and the version control system (PVCS) cooperate to achieve this at compile time. The HCFVersions fields are + constructed by collecting and shifting the low order nibbles of the PVCS controlled ASCII representation. + Note that the low order nibble of a space (0x20) nicely coincides with the low order nibble of an ASCII '0' + (0x30). Also note that the code breaks when major or minor number exceeds 99. + + +.DIAGRAM + 1: patch_catch is called as early in the flow as the C-entry code allows to help the HCF debugger as much as + possible. The philosophy behind patch_catch versus a simple direct usage of the INT_3 macro is explained + in the description of patch_catch + 2: The IFB is zero-filled. + This presets IFB_CardStat and IFB_TickIni at appropriate values for hcf_initialize. +10: In addition to the MSF readable fields mentioned in the description section, the following HCF specific + fields are given their actual value: + - a number of fields as side effect of the calls of hcf_action (see item 14) + - IFB_Magic + IFB_VERSION, which reflects the version of the IFB layout, is defined in HCF.H +14: Hcf_connect defaults to "no interrupt generation" (by calling hcf_action with the appropriate parameter), + "802.3 frame type" and "no card present" (implicitly achieved by the zero-filling of the IFB). + Depending on HCFL, the 802.3 frame type is either initialized in line or by calling hcf_action. + +.NOTICE + If io_base ever needs to be dynamic, it may be more logical to pass + - io_base at hcf_enable or + - have a separate hcf_put_config command or + - demand a hcf_disconnect - hcf_connect sequence + +.NOTICE + On platforms where the NULL-pointer is not a bit-pattern of all zeros, the zero-filling of the IFB results + in an seemingly incorrect initialization of IFB_MBp. The implementation of the MailBox manipulation in + put_mb_info protects against the absence of a MailBox based on IFB_MBSize, IFB_MBWp and ifbp->IFB_MBRp. This + has ramifications on the initialization of the MailBox via hcf_put_info with the CFG_REG_MB type. + +.ENDOC END DOCUMENTATION +-------------------------------------------------------------------------------------------------------------*/ +void hcf_connect( IFBP ifbp, //address of the Interface Block + hcf_io io_base //I/O Base address of the NIC + ) { + +hcf_8 *q; + +#if defined _M_I86TM +#endif // _M_I86TM + + + for ( q = (hcf_8*)&ifbp[1]; q > (hcf_8*)ifbp; *--q = 0) /*NOP*/; /* 2 */ + + ifbp->IFB_Version = IFB_VERSION; /* 10*/ + ifbp->IFB_IOBase = io_base; + ifbp->IFB_IORange = HREG_IO_RANGE; + ifbp->IFB_Magic = HCF_MAGIC; + ifbp->IFB_HCFVersionMajor = (hcf_8)( (hcf_rev[REV_OFFSET] << 4 | hcf_rev[REV_OFFSET+1]) & 0x0F ); + ifbp->IFB_HCFVersionMinor = (hcf_8)( hcf_rev[REV_OFFSET+4] == ' ' ? + hcf_rev[REV_OFFSET+3] & 0x0F : + (hcf_rev[REV_OFFSET+3] << 4 | hcf_rev[REV_OFFSET+4]) & 0x0F ); + + (void)hcf_action(ifbp, HCF_ACT_INT_OFF ); /* 14*/ + ifbp->IFB_FSBase = HFS_ADDR_DEST_ABS; + return; +}/* hcf_connect */ + + + + + +/******************************************************************************************************************* + +.MODULE hcf_disable +.LIBRARY HCF +.TYPE function +.SYSTEM msdos +.SYSTEM unix +.SYSTEM NW4 +.APPLICATION Card Initialization Group for WaveLAN based drivers and utilities +.DESCRIPTION Disables data transmission and reception +.ARGUMENTS + int hcf_disable( IFBP ifbp, hcf_16 port ) + +.RETURNS + HCF_SUCCESS + HCF_ERR_NO_NIC + HCF_ERR_TIME_OUT (via cmd_wait) + HCF_FAILURE (via cmd_wait) + + MSF-accessible fields of Result Block: + IFB_CardStat - reset CARD_STAT_ENABLED bit iff at completion no port enabled anymore + +.NARRATIVE + + Parameters: + ifbp address of the Interface Block + + Condition Settings: + Card Interrupts - Unchanged + - Disabled (Note that the value of IFB_IntOffCnt is unchanged) + + +.NOTICE + o hcf_disable may disable the card interrupts, however it does NOT influence IFB_IntOffCnt. + This way it is symmetrical with hcf_enable, which does NOT enable the card interrupts. + +**************************************************************************************************************/ +int hcf_disable( IFBP ifbp, hcf_16 port ) { + +int rc; +//hcf_16 p_bit; + + rc = cmd_wait( ifbp, HCMD_DISABLE | (port << 8 ), 0 ); + ifbp->IFB_CardStat &= (hcf_16)~CARD_STAT_ENABLED; + (void)hcf_action( ifbp, HCF_ACT_INT_OFF ); /* 40 */ + ifbp->IFB_IntOffCnt--; + return rc; +}/* hcf_disable */ + + + +/******************************************************************************************************************* + + +.MODULE hcf_disconnect +.LIBRARY HCF +.TYPE function +.SYSTEM msdos +.SYSTEM NW4 +.APPLICATION Card Connection for WaveLAN based drivers and utilities +.DESCRIPTION + Disable transmission and reception, release the IFB +.ARGUMENTS + void hcf_disconnect( IFBP ifbp ) +.RETURNS + void + + MSF-accessible fields of Result Block: + IFB_CardStat cleared + +.NARRATIVE + Parameters: + ifbp address of the Interface Block + + Description: + Brings the NIC in quiescent state by calling hcf_initialize, thus preventing any interrupts in the future. + +.DIAGRAM + 1: hcf_initialize gives a justification to execute the Hermes Initialize command only when really needed. + Despite this basic philosophy and although the HCF can determine whether the NIC is initialized based + on IFB_CardStat, the minimal set of actions to initialize the Hermes is always done by calling + ini_hermes. + 5: clear all IFB fields + The clearing of IFB_CardStat prevents I/O on any subsequent hcf_function + +.ENDOC END DOCUMENTATION +-------------------------------------------------------------------------------------------------------------*/ +void hcf_disconnect( IFBP ifbp ) { + +hcf_8 *q; + + + ini_hermes( ifbp ); + + for ( q = (hcf_8*)&ifbp[1]; q > (hcf_8*)ifbp; *--q = 0) /*NOP*/; /* 5 */ + +}/* hcf_disconnect */ + + + + + +/******************************************************************************************************************* + + +.MODULE hcf_enable +.LIBRARY HCF +.TYPE function +.SYSTEM msdos +.SYSTEM unix +.SYSTEM NW4 +.APPLICATION Card Initialization Group for WaveLAN based drivers and utilities +.DESCRIPTION Enables data transmission and reception +.ARGUMENTS + int hcf_enable( IFBP ifbp, hcf_16 port ) +.RETURNS + HCF_SUCCESS + HCF_ERR_TIME_OUT (via cmd_wait) + HCF_FAILURE (via cmd_wait) + + MSF-accessible fields of Result Block + + Condition Settings: + Card Interrupts: Off if IFB_IntOffCnt > 0; On if IFB_IntOffCnt == 0 + (Note that the value of IFB_IntOffCnt is unchanged) + +.NARRATIVE + Parameters: + ifbp address of the Interface Block + + Description: + + hcf_enable takes successively the following actions: + 6: If the requested port is disabled and if the NIC is present, the Hermes Enable command is executed. + If CARD_STAT_PRESENT is off, the body of hcf_enable must be skipped to prevent I/O because the I/O space + may no longer owned by the HCF, due to a card swap. + The IFB_IntEnMask is set to allow Info events, Receive events and Allocate events to generate interrupts + and effectuated if appropriate based on IFB_IntOffCnt by calling enable_int. + Note that since the effect of interrupt enabling has no effect on IFB_IntOffCnt, this code may + be called not only at the transition from disabled to enabled but whenever a port is enabled. +12: When the port successfully changes from disabled to enabled - including the case when no NIC is + present - , the NIC status as reflected by IFB_CardStat must change to enabled + +.DIAGRAM + +.NOTICE + When the Hermes enable cmd is given, the static configuration of the Hermes is done. +.ENDOC END DOCUMENTATION + +-------------------------------------------------------------------------------------------------------------*/ +int hcf_enable( IFBP ifbp, hcf_16 port ) { + +int rc; + + + + if ( (ifbp->IFB_CardStat & CARD_STAT_PRESENT) == 0 + ) { rc = HCF_ERR_NO_NIC; } /* 6 */ + else { + rc = HCF_SUCCESS; + rc = cmd_wait( ifbp, HCMD_ENABLE | ( port << 8 ), 0 ); + if ( rc == HCF_SUCCESS ) enable_int( ifbp, HREG_EV_INFO | HREG_EV_RX | HREG_EV_ALLOC ); /* 8 */ + } + if ( rc == HCF_SUCCESS || rc == HCF_ERR_NO_NIC ) { + ifbp->IFB_CardStat |= CARD_STAT_ENABLED; + } + return rc; + +}/* hcf_enable */ + + + +/******************************************************************************************************************* + + +.MODULE hcf_get_data +.LIBRARY HCF +.TYPE function +.SYSTEM msdos +.SYSTEM unix +.APPLICATION Data Transfer Function for WaveLAN based drivers and utilities +.DESCRIPTION + Obtains received message data parts from NIC RAM +.ARGUMENTS + int hcf_get_data( IFBP ifbp, int offset, wci_bufp bufp, int len ) + Card Interrupts disabled +.RETURNS + hcf_16 + zero NIC not removed during data copying process + HCF_ERR_NO_NIC NIC removed during data copying process + ...... + + MSF-accessible fields of Result Block: - + +.NARRATIVE + parameters: + ifbp address of the Interface Block + offset offset (in bytes) in buffer in NIC RAM to start copy process + len length (in bytes) of data to be copied + bufp char pointer, address of buffer in PC RAM + + When hcf_service_nic reports the availability of data, hcf_get_data can be + called to copy that data from NIC RAM to PC RAM. + + Hcf_get_data copies the number of bytes requested by the parameter len from + NIC RAM to PC RAM. If len is larger than the (remaining) length of the + message, undefined data is appended to the message. This implies that if + hcf_get_data is called while the last hcf_service_nic reported no data + available, undefined data is copied. + + Hcf_get_data starts the copy process at the offset requested by the + parameter offset, e.g. offset HFS_ADDR_DEST will start copying from the + Destination Address, the very begin of the 802.3 framemessage. + In case of a fragmented PC RAM buffer, it is the responsibility of the MSF, + to specify as offset the cumulative values of the len parameters of the + preceeding hcf_get_data calls. This I/F gives a MSF the facility to read + (part of) a message and then read it again. + +.DIAGRAM +.ENDOC END DOCUMENTATION + + +-------------------------------------------------------------------------------------------------------------*/ +int hcf_get_data( IFBP ifbp, int offset, wci_bufp bufp, int len ) { + +int rc = HCF_SUCCESS; +//int tlen; + + + if ( ifbp->IFB_CardStat & CARD_STAT_PRESENT ) { + if ( offset < 0 ) offset -= HFS_STAT; + else { + offset += ifbp->IFB_FSBase; + } + if ( rc == HCF_SUCCESS ) rc = hcfio_string( ifbp, BAP_1, ifbp->IFB_RxFID, offset, bufp, 0, len, IO_IN ); + } + return rc; +}/* hcf_get_data */ + + + + +/************************************************************************************************************** + + + Name: hcf_get_info + + Summary: Obtains transient and persistent configuration information from the + Card and from the HCF + + Parameters: + ifbp address of the Interface Block + + ltvp address of LengthTypeValue structure specifying the "what" and the "how much" of the information + to be collected from the HCF or from the Hermes + + Returns: + int + ..... ???????????????? + + Remarks: Transfers operation information and transient and persistent + configuration information from the Card and from the HCF to the MSF. + The exact layout of the provided data structure + depends on the action code. Copying stops if either the complete + Configuration Information is copied or if the number of bytes indicated + by len is copied. Len acts as a safe guard against Configuration + Information blocks which have different sizes for different Hermes + versions, e.g. when later versions support more tallies than earlier + versions. It is a consious decision that unused parts of the PC RAM buffer are not cleared. + + Remarks: The only error against which is protected is the "Read error" + as result of Card removal. Only the last hcf_io_string need + to be protected because if the first fails the second will fail + as well. Checking for cmd_wait errors is supposed superfluous because + problems in cmd_wait are already caught or will be caught by + hcf_enable. + + + 3: tallying of "No inquire space" is done by cmd_wait + + Note: + the codes for type are "cleverly" chosen to be identical to the RID + + + 7: The return status of cmd_wait and the first hcfio_in_string can be ignored, because when one fails, the + other fails via the IFB_TimStat mechanism + +**************************************************************************************************************/ +int hcf_get_info(IFBP ifbp, LTVP ltvp ) { + +int rc = HCF_ERR_LEN; +//hcf_io reg; +hcf_16 i, len; +hcf_16 type; //don't change type to unsigned cause of "is it a RID" test +hcf_16 *q; //source pointer (Tally-part of IFB) +//hcf_16 FAR *bq; //source pointer (Identity or Range records) ;?why bq and not e.g. wq +wci_recordp p = ltvp->val; //destination word pointer (in LTV record) +//wci_bufp cp = (wci_bufp)ltvp->val; //destination char pointer (in LTV record) + + + len = ltvp->len; + type = ltvp->typ; + + if ( len > 1 ) { + + rc = HCF_SUCCESS; + switch ( type ) { + +#if MSF_COMPONENT_ID != COMP_ID_AP1 + case CFG_TALLIES: /* 3 */ + ltvp->len = len = min( len, (hcf_16)(HCF_TOT_TAL_CNT + HCF_TOT_TAL_CNT + 1) ); + q = (hcf_16*)/*(wci_recordp)*/&ifbp->IFB_NIC_Tallies; //.TxUnicastFrames; + while ( --len ) *p++ = *q++; + (void)hcf_action( ifbp, HCF_ACT_TALLIES ); + break; +#endif //COMP_ID_AP1 + + + + + + default: + rc = HCF_ERR_NO_NIC; + if ( ifbp->IFB_CardStat & CARD_STAT_PRESENT ) { + rc = HCF_ERR_TIME_OUT; + if ( type < CFG_RID_CFG_MIN ) { + ltvp->len = 0; + } else { + (void)cmd_wait( ifbp, HCMD_ACCESS, type ); /* 7 */ + (void)hcfio_string( ifbp, BAP_1, type, 0, (wci_bufp)&i, 1, sizeof(hcf_16), IO_IN ); + ltvp->len = min( i, len ); + rc = hcfio_string( ifbp, BAP_1, type, sizeof(hcf_16), (wci_bufp)<vp->typ, 1, MUL_BY_2(ltvp->len), IO_IN ); + if ( rc == HCF_SUCCESS && i > len ) rc = HCF_ERR_LEN; + } + } + } + } + return rc; + +}/* hcf_get_info */ + + +/******************************************************************************************************************* + +.MODULE hcf_initialize ;?in fact an hcf-support routine, given an hcf_... name just in case we want to + export it over the WCI later +.LIBRARY HCF +.TYPE function +.SYSTEM msdos +.SYSTEM unix +.SYSTEM NW4 +.APPLICATION Card Initialization Group for WaveLAN based drivers and utilities +.DESCRIPTION ..........., in addition, a light-weight diagnostic test of the NIC +.ARGUMENTS + int hcf_initialize( IFBP ifbp ) + +.RETURNS + HCF_SUCCESS + HCF_ERR_NO_NIC + HCF_ERR_TIME_OUT; + HCF_FAILURE (via cmd_wait) + (via hcf_get_info) + HCF_ERR_INCOMP_PRI + HCF_ERR_INCOMP_STA + + MSF-accessible fields of Result Block: + IFB_DUIFRscInd, IFB_NotifyRscInd, IFB_PIFRscInd cleared + IFB_MBInfoLen, IFB_RxLen, IFB_RxStat cleared + IFB_CardStat - CARD_STAT_ENA_0 through CARD_STAT_ENA_6 + - CARD_STAT_INCOMP_PRI + - CARD_STAT_INCOMP_STA + + +.NARRATIVE + + Parameters: + ifbp address of the Interface Block + + hcf_initialize will successively: + - initialize the NIC by calling ini_hermes + - calibrate the S/W protection timer against the Hermes Timer by calling calibrate + + Condition Settings: + Card Interrupts: Disabled (Note that the value of IFB_IntOffCnt is unchanged) + + Remarks: since hcf_initialize is the first step in the initialization of the card and since the strategy is to + detect problems as a side effect of "necessary" actions, hcf_initialize has, in deviation of the general + strategy, an additional "wait for busy bit drop" at all places where Hermes commands are executed. An + additional complication is that no calibrated value for the protection count can be assumed since it is + part of the first execution of hcf_disable to determine this calibrated value (a catch 22). The initial + value (set at INI_TICK_INI by hcf_connect) of the protection count is considered safe, because: + o the HCF does not use the pipeline mechanism of Hermes commands. + o the likelihood of failure (the only time when protection count is relevant) is small. + o the time will be sufficiently large on a fast machine (busy bit drops on good NIC before counter expires) + o the time will be sufficiently small on a slow machine (counter expires on bad NIC before the enduser + switches the power off in despair + IFB_TickIni is used in cmd_wait to protect the Initialize command. The time needed to wrap a 32 bit counter + around is longer than many humans want to wait, hence the more or less arbitrary value of 0x10000L is + chosen, assuming it does not take too long on an XT and is not too short on a scream-machine. + Once we passed the CARD_STAT_PRESENT test on IFB_CardStat, the other bits can be reset. This is needed + to have a dynamical adjustment of the Station/Primary Incompatibility flags. + Especially IFB_TimStat must be cleared (new round, new chances) + + Remarks: First the HCF disables the generation of card interrupts. Next it waits for the Busy bit in the + Command register to drop (the additional safety required for hcf_initialize as described above). If the Hermes + passes this superficial health test, the Hermes Initialize command is executed. The Initialize command acts + as the "best possible" reset under HCF control. A more "severe/reliable" reset is under MSF control via the + COR register. + + Remarks: If the Initialize of the Hermes succeeds, the S/W protection counter is calibrated if not already + calibrated. This calibration is "reasonably" accurate because the Hermes is in a quiet state as a result of + the Initialize command. The hcf_put_info function is used to program the Hermes Tick for its minimum + interval (1024 microseconds). Programming the Tick interval terminates the old interval timing immediately + and starts the new interval. Due to this ad-hoc switch the first interval has a larger inaccuracy. By + timing immediately a second interval the accuracy improves due to the synchronization of HCF and Hermes. + After this second interval, the Hermes Tick is programmed for its default value of 1 second again. + +.NARRATIVE + 2: Clear all fields of the IFB except those which need to survive hcf_initialize. This is intended to make + the behavior and - as a consequence - the debugging of the HCF more reproduceable. + + 3: Disable the interrupt generation facility (see also #30) + 5: Depending on whether there is selective disabling of a single port or a collective disabling of + all ports a specific bit or all bits representing the enabled ports are reset. In case of "all ports" + none of the other bits except CARD_STAT_ENABLED is relevant, so as an easy implementation all those + other bits are cleared. + The individual bits conveyed in IFB_CardStat are historically grown. To leave the WCI unchanged, the + individual "port enabled" bits are scattered through IFB_CardStat. As a consequence there is some bit + arithmetic involved to convert a port number to a bit flag + The masking of port with HCF_PORT_MASK is a cheap safeguard against I/F violations by the MSF. If the MSF + supplies an invalid bit pattern, unwanted bits may end up in Hermes Command register via Disable command + with unpredictable effects. + 7: Check whether CARD_STAT_PRESENT bit of IFB_CardStat is on. If not the remainder of hcf_initialize must be + skipped to prevent I/O because the I/O space may no longer owned by the HCF, due to a card swap. +12: When a single port must be disabled AND it is not the only enabled port, that port is selectively + disabled. If all ports or the only enabled port is to be disabled, the disabling is skipped and the + Hermes Initiate command is supposed to take care of it all. +16: perform a superficial Hermes health test. Note that in hcf_initialize all (or at least most) activities are + checked for conditions like "Busy bit should drop". If such a condition is not met in time, hcf_initialize + returns an error code. Hcf_functions which are only called during "normal" operations may ignore such + events. Their only obligation is to prevent that the code hangs. + Note that early models of the Hermes needed some arbitrary read before the first write activity to operate + stable (ref tracker 27). This code achieves that function as a side effect. +18: Initialize the Hermes. The results of this way of initialization depends on the capabilities of the Hermes + firmware. Therefore, the Hermes H/W controlled bits, like those in the evitable register are not guaranteed + to be cleared as result of the initialize command. However it is guaranteed that no more events are in the + pipeline. Ack-ing indiscriminately all events resolves this problem. An alternative would be to use the + resulting value of "IN_PORT_WORD( ifbp->IFB_IOBase + HREG_EV_STAT )" rather than 0xFFFF to specifically + only reset those events which are set. This strategy is considered to be only more aesthetically pleasing + (if that). +20: Perform some housekeeping tasks + - Write HCF_MAGIC as signature to S/W support register 0. This signature is used to detect card removal + wherever the presence of the card is critical while HCF may not yet have been informed by the MSF of the + removal of the card. Note that this task can not be postponed because that would cause the hcfio_in_string + called by hcf_get_info to fail +22: + + - IFB_TickIni must be initialized (at INI_TICK_INI) to prevent that actions like hcf_put_info immediately + after hcf_connect (that is without intervening hcf_disable) at an absent (or failing) NIC take too + long. Note that this is a legal hcf-sequence according to the WCI-specification. + + + + + IFB_TickIni is the value used to initialize the S/W protection counter in a way which makes the + expiration period more or less independent of the processor speed. If IFB_TickIni is not yet calibrated, + it is done now. + First off all the Hermes Tick period is programmed for a "reasonable" interval, currently 8092 or 8k + microseconds, by means of hcf_put_info. Note that IFB_DLTarget, which is guaranteed to get the correct + value later on in hcf_enable, is misused as re-entrant storage for the RID used to program the Tick period. + The HCF synchronizes itself with the Hermes timer by waiting for the first timer tick. This synchronizing is done by + ack-ing the Tick regardless of its current value. This guarantees Tick becomes low. Then Tick is sampled + till it is high, which guarantees a new complete interval starts in the Hermes. Tick is acked again and + another Tick is awaited. This period is as accurate as we can get. + To diminish the chance that in a pre-emptive environment IFB_TickIni is calibrated too low because the HCF + just happens to loose control during this calibration, the calibration is performed 10 times and the + largest value is used. + IFB_TickIni is then set at approximately 1 second by multiplying that largest value by 128. The 8k + microseconds interval and the multiplication by 128 are chosen as a compromise between accuracy of + the calibration. time consumed by the calibration and possibilities for the compiler to optimize + the arithmetic. +26: Finally the Hermes Tick period is programmed for a "reasonable" runtime interval, currently 1 second + (1,000,000/1,024 kilo-microseconds), by means of hcf_put_info (again the available CONCATENATED storage + is misused) + Note that in case of failure, IFB_TickIni ends up as INI_TICK_INI, which is a supposedly acceptable + value to handle the rare case of a broken card. +30: The Supplier Range of the Primary Firmware function is retrieved from the Hermes and checked against + the Top and Bottom level supported by this HCF. + If the primary firmware does not supply this RID or supplies the "old" HardwareStructure Info, the + Primary Compatibility check is skipped. These conditions are recognized based on the length field supplied + by the Hermes. ;?is this wise in the post-GCA area +32: In case of a HCF compiled for station functionality, the Supplier Range of the Station Firmware function + is retrieved from the Hermes and checked against the Top and Bottom level supported by this HCF. + Note that the Firmware can have multiple Variants, but that the HCF currently only supports a single + variant. +40: Perform some more housekeeping tasks + - Decrement ifbp->IFB_IntOffCnt to compensate side effect of ACT_INT_OFF action at begin of hcf_initialize (see + #2). This can not be handled by calling hcf_action with HCF_ACT_ON, because this could as undesirable + side effect actually enable interrupts + +.NOTICE + o For all practical WCI purposes there is no practical difference between a Hermes disable command at all + individual ports and an hermes initialize command + o hcf_initialize disables the card interrupts, however it does NOT influence IFB_IntOffCnt. + This way it is symmetrical with hcf_enable, which does NOT enable the card interrupts. + + + IFB_CardStat - CARD_STAT_INCOMP_PRI + - CARD_STAT_INCOMP_STA + + 5: The house keeping is done, consisting of the steps: + - allocate a Tx Frame Structure for the protocol stack + - allocate a Tx Frame Structure for the utility + - allocate a Information Frame Structure for the Notify command + Note that a subsequent allocate is only performed if the preceding one + succeeded + - if all allocates succeeded, the Resource Indicators corresponding + with the Tx Frames are set + + +**************************************************************************************************************/ +int hcf_initialize( IFBP ifbp ) { + +int rc; +//hcf_16 *p; +hcf_8 *q; + + + for ( q = (hcf_8*)&ifbp->IFB_PIFRscInd; q < (hcf_8*)&ifbp[1]; *q++ = 0) /*NOP*/; /* 2 */ + +// if ( (ifbp->IFB_CardStat & CARD_STAT_INI) == 0 ) { //present but not initialized /* 7 */ + do { //;?CARD_STAT_PRESENT check superfluous as long as hcf_initialize is not defined on the WCI + rc = ini_hermes( ifbp ); + if ( rc != HCF_SUCCESS ) break; /* 32*/ + OUT_PORT_WORD( ifbp->IFB_IOBase + HREG_SW_0, HCF_MAGIC ); /* 20*/ + rc = calibrate( ifbp ); /* 22*/ + if ( rc != HCF_SUCCESS ) break; + +#if defined MSF_COMPONENT_ID //;?interesting question at which level HCFL interacts +#endif // MSF_COMPONENT_ID + ifbp->IFB_CardStat |= CARD_STAT_INI; //consider this as sufficient to reach CARD_STAT_INI? /* 24*/ + if ( rc != HCF_SUCCESS ) break; //;? apparently this still should follow the moved IFB_DLTarget logic but + //;? think this over for the different scenarios + if ( ( ifbp->IFB_PIF_FID = alloc( ifbp, HFS_TX_ALLOC_SIZE ) ) == 0 || /* 5 */ + ( ifbp->IFB_DUIF_FID = alloc( ifbp, HFS_TX_ALLOC_SIZE ) ) == 0 ) { + rc = HCF_FAILURE; + } else { + ifbp->IFB_PIFRscInd = ifbp->IFB_DUIFRscInd = 1; + + } + } while ( 0 ); //pseudo goto-less, accept "warning: conditional expression is constant" + + return rc; +}/* hcf_initialize */ + + +/******************************************************************************************************************* + + +.MODULE hcf_put_data +.LIBRARY HCF +.TYPE function +.SYSTEM msdos +.SYSTEM unix +.SYSTEM NW4 +.APPLICATION Data Transfer Function for WaveLAN based drivers and utilities +.ARGUMENTS + void hcf_put_data( IFBP ifbp, wci_bufp bufp, int len, hcf_16 port ) + Card Interrupts disabled + +.RETURNS + void + + MSF-accessible fields of Result Block: - + +.DESCRIPTION Transfers (part of) transmit message to the NIC and handles + the Ethernet-II encapsulation if applicable +.NARRATIVE + parameters: + ifbp address of the Interface Block + bufp char pointer, address of buffer in PC RAM + len length (in bytes) of data to be copied + port HCF_PORT_0 - HCF_PORT_6 .........;? + HCF_PUT_DATA_RESET + + + Refer to hcf_service;?non-existing reference, for a concise description about possible + relation/sequencing of hcf_put_data in the Interrupt Service Routine + + In essence, hcf_put_data copies the number of bytes specified by parameter len from the location in PC + RAM specified by bufp to the NIC RAM buffer associated with the Protocol Stack dedicated FID. + The first call succeeding hcf_send (or hcf_enable), writes the first byte at offset HFS_ADDR_DEST in + the transmit data buffer, successive hcf_put_data calls continue where the preceeding hcf_put_data stopped. + + IFB_FrameType determines whether the message in the PC RAM buffer is interpreted as an 802.3 or 802.11 + frame. This influences: + o the position where the first byte of the initial hcf_put_data is stored + o Only in case of the 802.3 frame type, hcf_put_data checks whether the frame is an Ethernet-II rather + than an "official" 802.3 frame. The E-II check is based on the length/type field in the MAC header. If + this field has a value larger than 1500, E-II is assumed. The implementation of this test fails if the + length/type field is split over 2 hcf_put_data calls. + If E-II is recognized, the length field HFS_LEN_ABS is skipped for the time being and a SNAP header is + inserted starting at HFS_DAT_ABS. This SNAP header represents either RFC1042 or Bridge-Tunnel + encapsulation, depending on whether the type is absent or present in enc_trans_tbl. + o In case of the 802.11 frame type, hcf_put_data checks whether the complete header + length field is + written (note that part of the header may be written by previous hcf_put_data calls and part may be + written by this call). If so, the next byte is written at HFS_DAT_ABS (the 802.3 header area is skipped) + + It is allowed to write the 802.3 header, 802.11 header and/or data in fragments, e.g. the first + hcf_put_data call writes 18 bytes starting at location HFS_ADDR_1_ABS and the second call writes 6 more + bytes starting at location HFS_ADDR_4. Although Address part #4 is not present in some 802.11 headers, + all 4 addressing parts and the length field must be written in case of 802.11. Once the complete header + is written, the data part is written starting from offset HFS_DAT_ABS. + + Hcf_put_data does not check for transmit buffer overflow because the Hermes does this protection. + In case of a transmit buffer overflow, the surplus which does not fit in the buffer is simply dropped. + Note that this possibly results in the transmission of incomplete frames. + +.DIAGRAM +1*: If the card is not present, prevent all I/O because "our" I/O base may have been given away to someone + else in a CS/SS environment. Also no I/O should be performed if the NIC is not enabled. However + an MSF which calls hcf_put_data while the NIC is not enabled probably contains a logic flaw (or has a + strategy which was not foreseen at the time this was written) +10* HCF_PUT_DATA_RESET discards all the data put by preceeding hcf_put_data calls and resets the HCF + housekeeping just the same as after an hcf_send triggered allocate event. + Note: To make the WCI fail-safe, invalid port numbers are silently rejected by treating them as + HCF_PUT_DATA_RESET. Note that the assumption is that this should never ever occure in a debugged MSF and + that during debugging the ASSERT is sufficient support to help the MSF programmer. +2*: This statement is only true at the first hcf_put_data call after an hcf_send result or hcf_enable + The housekeeping is done. + o the PIFRscInd is cleared, so the MSF can not begin another hcf_put_data/hcf_send sequence + before completing the current one + o the Tx Encoding flag (TxFrameType) is cleared + o the index to the first free position in the FID (IFB_PIFLoadIdx) is initialized based on IFB_FSBase. + IFB_FSBase is initialized when hcf_action is called with HCF_ACT_802_3 or HCF_ACT_802_11 +3*: Pay Attention: it may seem attractive to change this code, e.g. to save the superfluous call to + hcfio_out_string when the Destination and Source address are written by the preceeding call and the + current call starts at the length/type field. However this code is "reasonably carefully" crafted + to take in account all boundary conditions. It is very easy to make a change which does not work under + all feasible split ups of the message in fragments. + First IFB_PIFLoadIdx is checked. + - If IFB_PIFLoadIdx points past HFS_LEN_ABS, the preceeding call(s) to hcf_put_data already passed the + length/type field. As a consequence the fragment can be concatenated to the data already copied to + NIC RAM. + - If IFB_PIFLoadIdx does not point past HFS_LEN_ABS, the current fragment may or may not contain part of + the Destination and/or Source Address and it may or may not contain the length/type field. + If the fragment contains addressing information or -in case of 802.11- length info , this information + is copied/concatenated to the NIC RAM buffer. The working variables (pointer and length of fragment) as + well as the IFB_PIFLoadIdx are adjusted. + The semi-obscure differences in the boundary testing are caused by: + o 802.11: the "below the boundary" area is Addr1, Addr2, Addr3, Ctrl, Adrr4 + DataLen and the "above" + area is the "real" data + o 802.3: the "below the boundary" area is DestAddr + SrcAddr and the "above" area is the length + + "real" data + o E-II: the "below the boundary" area is DestAddr + SrcAddr, then there is a "virtual" area with the + SNAP header (which will in the end include the HCF calculated length) and the "above" area is the + "protocol stack length" (is in reality the type code) + "real" data +4*: If there is still data left, IFB_PIFLoadIdx may need adjustment (802.11 and E-II encapsulation). Again + note that this length check is crucial to prevent mis-manipulation of IFB_PIFLoadIdx in case the header + is written in multiple fragments. + In case of 802.3, the E-II check is done. In case of E-II, the encapsulation type (RFC1042 versus + Bridge-Tunnel) is determined and the corresponding SNAP header is written to NIC RAM and + IFB_PIFLoadIdx is adjusted. +6*: All data which is not already copied under 3*, is copied here. + In case of 802.11, the HFS_DAT field is the first field written by this code. + In case of 802.3, the HFS_LEN field is the first field written by this code. + In case of E-II encapsulation, the HFS_TYPE field is the first field written by this code. + Note that in case of E-II encapsulation, the HFS_LEN field is not written by hcf_put_data at all, but by + hcf_send because the data length is not known till all fragments have been processed. + +.NOTE + The possible split of a single hcf_put_data call into 2 calls to hcfio_out_string results in 2 calls + to bap_ini, which may be unexpected while you are debugging, but is never the less the intended behavior. + Along the same line a call of hcfio_out_string with a value of 0 for parameter len may be unexpected, e.g. + when the len parameter of hcf_put_data is either 1 or 2, the latter depending on the odd/even aspects of + IFB_PIFLoadIdx. + + + +.NOTE + The test on PIFRscInd to distinguish the initial hcf_put_data from subsequent calls is not thread safe. + It is assumed that threaded MSFs have their own mechanism to assure that hcf_put_data calls belonging to + a single frame are atomic with respect to each other. It is also assumed that the MSF takes care that + the hcf_put_data calls of multiple frames do not run concurrent + + +.ENDOC END DOCUMENTATION + + +-------------------------------------------------------------------------------------------------------------*/ +void hcf_put_data( IFBP ifbp, wci_bufp bufp, int len, hcf_16 port ) { + +int idx; //working index into Tx Frame structure, presume MSF control +//int tlen; //working type/length of frame, working length for partial copy of frame + + + if ( ifbp->IFB_CardStat & CARD_STAT_PRESENT ) { /* 1 */ + if ( ifbp->IFB_PIFRscInd ) { /* 2 */ + ifbp->IFB_PIFRscInd = 0; + ifbp->IFB_PIFLoadIdx = ifbp->IFB_FSBase; //;? should result in 0 + } + + idx = ifbp->IFB_PIFLoadIdx; + ifbp->IFB_PIFLoadIdx += (hcf_16)len; + (void)hcfio_string( ifbp, BAP_0, ifbp->IFB_PIF_FID, idx, bufp, 0, len, IO_OUT); /* 6 */ + } + return; +}/* hcf_put_data */ + + +/************************************************************************************************************** + + + Name: hcf_put_info + + Summary: Transfers operation information and transient and persistent + configuration information to the Card. + + Parameters: + ifbp address of the Interface Block + + type specifies the RID (as defined by Hermes I/F) + + bufp address in NIC RAM where record data is located + + len length of data (in bytes) + +.NARRATIVE + + Remarks: + CFG_NOTIFY: only runs after hcf_enable + + Remarks: Configuration information is copied from the provided data + structure into the Card. The exact layout of the provided data + structure depends on the action code. Also the mechanism used to copy + the data to the card depends on the action code. In order to make the + changes which are based on the Access command (see support routine put_info) + sustain over activities like hcf_diagnose and recovery from PCMCIA card + insertion, the data associated with these particular action codes, is + saved in the IF-block. The codes for this type are "cleverly" chosen to + be identical to the RID. + + bufp is defined as a pointer to items of type hcf_16 because of the + correlation with the Hermes definition + + +.DIAGRAM + +.NOTICE + Future enhancements in the functionality offered by the WCI and/or implementation aspects of the HCF + may warrant filtering on the type-field of the LTV to recognize non-MSF accessible records, e.g. CFG_TICK + +**************************************************************************************************************/ + +int hcf_put_info( IFBP ifbp, + LTVP ltvp /*number identifying the type of change +
+									CFG_INFO_FRAME_MIN	lowest value representing an Information Frame
+									CFG_NOTIFY			Handover Address
+										
+									CFG_TALLIES			Communications Tallies
+									CFG_SCAN			Scan results
+										                        	
+									CFG_LINK_STAT 		Link Status
+									CFG_ASSOC_STAT		Association Status
+								
+
*/ + ) { + +//int cnt = 3; +//hcf_16 i = ltvp->len - 1; +int rc = HCF_SUCCESS; + + + if ( CFG_RID_CFG_MIN <= ltvp->typ && ltvp->typ <= CFG_RID_CFG_MAX ) { + //all codes between 0xFC00 and 0xFCFF are passed to Hermes) +// if ( ( ifbp->IFB_CardStat & CARD_STAT_PRI_STA_PRES ) == CARD_STAT_PRESENT ) { +// do { +// rc = hcfio_string( ifbp, BAP_1, +// ltvp->typ, 0, (wci_bufp)ltvp, 2, MUL_BY_2(ltvp->len + 1), IO_OUT_CHECK ); +// } while ( cnt-- && rc != HCF_SUCCESS ); +// if ( rc == HCF_SUCCESS ) rc = cmd_wait( ifbp, HCMD_ACCESS + HCMD_ACCESS_WRITE, ltvp->typ ); +// if ( rc == HCF_SUCCESS ) rc = put_info( ifbp, ltvp ); + + rc = put_info( ifbp, ltvp ); +// } + } + return rc; +}/* hcf_put_info */ + + + + + +/****************************************************************************************************************** + +.MODULE hcf_send +.LIBRARY HCF +.TYPE function +.SYSTEM msdos +.SYSTEM unix +.SYSTEM NW4 +.APPLICATION Data Transfer Function for WaveLAN based drivers and utilities +.DESCRIPTION Transmit a message on behalf of the protocol stack +.ARGUMENTS + void hcf_send( IFBP ifbp , hcf_send_type type ) + Card Interrupts disabled + +.RETURNS + void + + MSF-accessible fields of Result Block: - + +.NARRATIVE + Hcf_send transmits the Protocol Stack message loaded in NIC RAM by the + preceeding hcf_put_data calls. + +.DIAGRAM +1: The actual data length (the number of bytes in the Tx Frame structure + following the 802.3/802.11 Header Info blocks, is determined by + IFB_PIFLoadIdx, the index in the Transmit Frame Structure to store the + "next" byte. Note that this value must be compensated for the header info + by subtracting HFS_DAT. +2/3:TxFrameType - which is based on the preceding hcf_put_data calls - defines + whether the actual data length is written to the 802.11 or 802.3 Header Info + block. +2: In case of 802.11, the entry parameter type is augmented to reflect 802.11 + before it is written to the Control Field block. +3: In case of 802.3, the actual length must be converted from the native + format of the Host (Little Endian in case of an 80x86) to Big Endian before + it is written to the 802.3 Header Info block. +4: The actual send+reclaim command is performed by the routine send. +7: The return status of hcfio_in_string can be ignored, because when it fails, cmd_wait will fail via the + IFB_TimStat mechanism +.NOTICE + ;?This comment is definitely out of date + The choice to let hcf_send calculate the actual data length as + IFB_PIFLoadIdx - HFS_DAT, implies that hcf_put_data with the HFS_LUCENT + mechanism MUST be used to write the Data Info Block. A change in this I/F + will impact hcf_send as well. + An alternative would be to have a parameter datlen. If datlen is zero, the + current behavior is used. If datlen has a non-zero value, its value is used + as the actual data length (without validating against HCF_MAX_MSG and without + validating the total number of bytes put by hcf_put_data). + +.NOTICE + hcf_put_data/send leave the responsibility to only send messages on enabled ports at the MSF level. + This is considered the strategy which is sufficiently adequate for all "robust" MSFs, have the least + processor utilization and being still acceptable robust at the WCI !!!!! +.ENDOC END DOCUMENTATION + +------------------------------------------------------------------------------------------------------------*/ +int hcf_send( IFBP ifbp , hcf_16 port ) { //;?note that port is unused due to ambivalence about what the "right" I/F is + +int rc = HCF_SUCCESS; + + + if ( ifbp->IFB_CardStat & CARD_STAT_PRESENT ) { /* 1 */ + /* H/W Pointer problem detection */ + if ( ifbp->IFB_BAP_0[0] != ifbp->IFB_FSBase ) { //;? should BE HARD CODED, also add to send diag msg /* 30*/ + rc = hcfio_string( ifbp, BAP_0, ifbp->IFB_PIF_FID, ifbp->IFB_PIFLoadIdx, NULL, 0, 0, IO_OUT_CHECK ); + + if ( rc == HCF_FAILURE ) { + ifbp->IFB_PIFRscInd = 1; + } + } + if ( rc == HCF_SUCCESS ) { + if ( /*ifbp->IFB_FrameType == ENC_802_11 || */ ifbp->IFB_TxFrameType == ENC_TX_E_II ) { /* 2 */ + ifbp->IFB_PIFLoadIdx -= HFS_DAT_ABS; //actual length of frame /* 1 */ + CNV_INT_TO_BIG_NP(&ifbp->IFB_PIFLoadIdx); //;?is it worthwhile to have this additional macro + (void)hcfio_string( ifbp, BAP_0, ifbp->IFB_PIF_FID, HFS_LEN_ABS, + (wci_bufp)&ifbp->IFB_PIFLoadIdx, 0, 2, IO_OUT ); /* 7 */ + } +// send( ifbp, ifbp->IFB_PIF_FID, port | ifbp->IFB_FrameType ); /* 4 */ + (void)cmd_wait( ifbp, HCMD_TX + HCMD_RECL, ifbp->IFB_PIF_FID ); //justify "void" + } + /* reset the BAP pointer for the Tx Framestructure, note that we access the BAP not the NIC RAM + * after we relinguished control of the Tx FID to the Hermes + */ + (void)hcfio_string( ifbp, BAP_0, ifbp->IFB_PIF_FID, ifbp->IFB_FSBase, NULL, 0, 0, IO_IN ); + } + return rc; +} /* hcf_send */ + + + +/******************************************************************************************************************* + +.MODULE hcf_send_diag_msg +.LIBRARY HCF +.TYPE function +.SYSTEM msdos +.SYSTEM unix +.SYSTEM NW4 +.APPLICATION Data Transfer Function for WaveLAN based drivers and utilities +.DESCRIPTION Transmit a message on behalf of the Driver-Utility I/F +.ARGUMENTS + void hcf_send_diag_msg( IFBP ifbp, wci_bufp bufp, hcf_16 len ) + Card Interrupts disabled + +.RETURNS + void + + MSF-accessible fields of Result Block: - + +.NARRATIVE + Hcf_send_diag_msg transmits the message +.DIAGRAM + + 2: + + 4: Based on the assumption that hcf_send_diag_msg is called at a low frequency, HFS_TX_CNTL_ABS is written + on each call rather than using an IFB-field to remember the previous value and update only if needed + + +.ENDOC END DOCUMENTATION + +-------------------------------------------------------------------------------------------------------------*/ +int hcf_send_diag_msg( IFBP ifbp, hcf_16 type, wci_bufp bufp, int len ) { + +int rc = HCF_SUCCESS; + + + if ( ifbp->IFB_CardStat & CARD_STAT_PRESENT ) { + rc = HCF_ERR_BUSY; //;?more appropriate value needed + if ( ifbp->IFB_DUIFRscInd ) { /* 2 */ + rc = hcfio_string( ifbp, BAP_0, ifbp->IFB_DUIF_FID, HFS_ADDR_DEST_ABS, bufp, 0, len, IO_OUT_CHECK ); + if ( rc == HCF_SUCCESS ) { + ifbp->IFB_DUIFRscInd = 0; + (void)hcfio_string( ifbp, BAP_0, ifbp->IFB_PIF_FID, HFS_TX_CNTL_ABS, //justify void + (wci_bufp)&type, 1, 2, IO_OUT ); /* 4 */ + (void)cmd_wait( ifbp, HCMD_TX + HCMD_RECL, ifbp->IFB_DUIF_FID ); + } + } + } + return rc; +} /* hcf_send_diag_msg */ + + + + + +/******************************************************************************************************************* + + +.MODULE hcf_service_nic +.LIBRARY HCF +.TYPE function +.SYSTEM msdos +.APPLICATION Data Transfer Function for WaveLAN based drivers and utilities +.DESCRIPTION Provides received message and link status information +.ARGUMENTS + int hcf_service_nic(IFBP ifbp ) + Card Interrupts disabled + +.RETURNS + int + all the bits of the Hermes evitable register, which are encountered during execution of hcf_service_nic + the "pseudo"-events HREG_EV_NO_CARD, HREG_EV_DUIF_RX + MSF-accessible fields of Result Block + IFB_RxLen 0 or Frame size as reported by LAN Controller + IFB_RxStat + IFB_MBInfoLen + IFB_PIFRscInd + IFB_DUIFRscInd + IFB_NotifyRscInd + IFB_HCF_Tallies + + +.NARRATIVE + hcf_service_nic is primarily intended to be part of the Interrupt Service Routine. + hcf_service_nic is presumed to neither interrupt other HCF-tasks nor to be interrupted by other HCF-tasks. + A way to achieve this is to precede hcf_service_nic as well as all other HCF-tasks with a call to + hcf_action to disable the card interrupts and, after all work is completed, with a call to hcf_action to + restore (which is not necessarily the same as enabling) the card interrupts. + In case of a polled environment, it is assumed that the MSF programmer is sufficiently familiar with the + specific requirements of that environment to translate the interrupt strategy to a polled strategy. + + hcf_service_nic services the following Hermes events: + HREG_EV_INFO Asynchronous Information Frame + HREG_EV_INFO_DROP WMAC did not have sufficient RAM to build Unsolicited Information Frame + HREG_EV_ALLOC Asynchronous part of Allocation/Reclaim completed + HREG_EV_RX the detection of the availability of received messages + + If a message is available, its length is reflected by the IFB_RxLen field of the IFB. This length + reflects the 802.3 message length (i.e. the data itself but not the Destination Address, Source Address, + DataLength field nor the SAP-header in case of decapsulation by the HCF). + If no message is available, IFB_RxLen is zero. + + **Buffer free strategy + When hcf_service_nic reports the availability of a message, the MSF can access that message or parts + thereof, by means of hcf_get_data calls till the next call of hcf_service_nic. Therefore it must be + prevented that the LAN Controller writes new data in the buffer associated with the last hcf_service_nic + report. + As a consequence hcf_service_nic is the only procedure which can free receive buffers for re-use by the + LAN Controller. Freeing a buffer is done implicitly by acknowledging the Rx event to the Hermes. The + strategy of hcf_service_nic is to free the buffer it has reported as containing an available message in + the preceeding call (assuming there was an available message). + A consequence of this strategy is that the Interrupt Service Routine of the MSF must repeatedly call + hcf_service_nic till hcf_service_nic returns "no message available". It can be reasoned that + hcf_action( INT_ON ) should not be given before the MSF has completely processed a reported Rx-frame. The + reason is that the INT_ON action is guaranteed to cause a (Rx-)interrupt (the MSF is processing a + Rx-frame, hence the Rx-event bit in the Hermes register must be active). This interrupt will cause + hcf_service_nic to be called, which will cause the ack-ing of the "last" Rx-event to the Hermes, + causing the Hermes to discard the associated NIC RAM buffer. + + +.DIAGRAM + 2: IFB_RxLen and IFB_RxStat must be cleared before the NIC presence check otherwise these values may stay + non-zero if the NIC is pulled out at an inconvenient moment + 4: If the card is not present, prevent all I/O because "our" I/O base may have been given away to someone + else in a CS/SS environment. + The MSF may have considerable latency in informing the HCF of the card removal by means of an hcf_disable. + To prevent that hcf_service_nic reports bogus information to the MSF with all - possibly difficult to + debug - undesirable side effects, hcf_service_nic pays performance wise the prize to use the momentanuous + NIC presence test by checking the contents of the Hermes register HREG_SW_0 against the value HCF_MAGIC. + 6: The return status of hcf_service_nic is defined as reflecting all interrupt causes this call has run into, + hence an accumulator is needed. This return status services ONLY to help the MSF programmer to debug the + total system. + When the card is removed, the pseudo event HREG_EV_NO_CARD is reported. + NOTE, the HREG_EV_NO_CARD bit is explicitly not intended for the MSF to detect NIC removal. The MSF must + use its own - environment specific - means for that. +10: ack the "old" Rx-event. See "Buffer free strategy" above for more explanation. + IFB_RxFID, IFB_RxLen and IFB_RxStat must be cleared to bring both the internal HCF house keeping as the + information supplied to the MSF in the state "no frame received" +12: The evitable register of the Hermes is sampled and all non-Rx activities are handled. + The non-Rx activities are: + - Alloc. The corresponding FID register is sampled, and based on this FID, either the IFB_PIFRscInd, + the IFB_DUIFRscInd or the IFB_NotifyRscInd is raised. + Note that no ASSERT is performed to check whether the RscInd corresponding with the sampled + HREG_ALLOC_FID has a zero value. It is felt that this obscures the code to the reader while adding + little practical value + - LinkEvent (including solicited and unsolicited tallies) are handled by procedure isr_info. + - Info drop events are handled by incrementing a tally +14: All the non-Rx/non-Cmd activities are acknowledged. Combining all these acknowledgements to a single + place, is considered an optimization. + Note that the Rx-acknowledgement is explicitly not included, as justified in "Buffer free strategy" above. + Note that the Cmd-acknowledgement is explicitly not included, because all command handling is handled + in line. +16: The handling of the non-Rx activities, may have bought the Hermes sufficient time to raise an Rx event + in the evitable register (assuming an other Rx event was pending and this loop through hcf_service_nic + acknowledged an Rx event). Therefore the evitable register is sampled again. If a frame is available, + the FID of that frame and the characteristics (status and length) are read from the NIC. These values are, + after Endianess conversion if needed, stored in IFB_RxStat and IFB_RxLen. IFB_RxLen is also adjusted for + the size of the 802.3 MAC header. Note: Whether this adjustment is the correct/most optimal for 802.11 + is debatable, however it is paramount that IFB_RxFID and IFB_RxLEN must either be both zero or both + non-zero to get a coherent behavior of the MSF+HCF. +18: If the Hermes frame status reflects an error - which can only occure in promiscuous mode - the frame + is not further processed and control is passed back to the MSF +20: WMP messages are processed by copying them to the MailBox. Accu is updated to reflect the reception + of the WMP frame to the debugger and sample is modified to go once more through the loop in the hope + to process the next pending Rx frame. +22: Both in 802.11 mode and 802.3_pure mode, the frame is not further processed (no decapsulation) and + control is passed back to the MSF. + In 802.3 mode the HCF checks whether decaposulation is needed (i.e. the Hermes reported Tunnel + encapsulation or the Hermes reported 1042 Encapsulation and the frame type does not match one of the + values in enc_trans_tbl. + The actual decapsulation takes place on the fly in hcf_get_data, based on the value of IFB_RxFence. + Note that in case of decapsulation the SNAP header is not passed to the MSF, hence IFB_RxLen must be + compensated for the SNAP header length + +.NOTICES + To make it possible to discriminate between a message without payload (only MAC addresses and implicit + the length) it is a convenient I/F to add 14 ( space occupied by MAC addresses and Length) to the + Hermes reported payload. So the maintenance programmer should be forwarned when considering changing + this strategy. Also the impact on 802.11 should be considered ;? +.ENDOC END DOCUMENTATION + +-------------------------------------------------------------------------------------------------------------*/ +int hcf_service_nic(IFBP ifbp ) { + +int accu = HREG_EV_NO_CARD; +hcf_16 sample, tmp; + + + ifbp->IFB_RxLen = ifbp->IFB_RxStat = 0; /* 2 */ + if ( ifbp->IFB_CardStat & CARD_STAT_PRESENT && IN_PORT_WORD( ifbp->IFB_IOBase + HREG_SW_0) == HCF_MAGIC ) { /* 4 */ + accu = 0; /* 6 */ + if ( ifbp->IFB_RxFID ) { /*10 */ + OUT_PORT_WORD( ifbp->IFB_IOBase + HREG_EV_ACK, HREG_EV_RX ); + ifbp->IFB_RxFID = ifbp->IFB_RxLen = ifbp->IFB_RxStat = 0; + } + sample = IN_PORT_WORD( ifbp->IFB_IOBase + HREG_EV_STAT ); /*12*/ + accu |= sample; + + if ( sample & HREG_EV_INFO ) isr_info( ifbp ); + if ( sample & HREG_EV_ALLOC ) { + tmp = IN_PORT_WORD(ifbp->IFB_IOBase + HREG_ALLOC_FID ); + if ( tmp == ifbp->IFB_PIF_FID ) ifbp->IFB_PIFRscInd = 1; + else if ( tmp == ifbp->IFB_DUIF_FID ) ifbp->IFB_DUIFRscInd = 1; + else { + ifbp->IFB_NotifyRscInd = 1; + } + } + tmp = sample & (hcf_16)~(HREG_EV_RX | HREG_EV_CMD ); /*14 */ + if ( tmp ) OUT_PORT_WORD( ifbp->IFB_IOBase + HREG_EV_ACK, tmp ); + + sample = IN_PORT_WORD( ifbp->IFB_IOBase + HREG_EV_STAT ); /*16 */ + if ( sample & HREG_EV_RX ) { + ifbp->IFB_RxFID = IN_PORT_WORD( ifbp->IFB_IOBase + HREG_RX_FID); + (void)hcfio_string(ifbp, BAP_1, ifbp->IFB_RxFID, HFS_STAT_ABS, (wci_bufp)&ifbp->IFB_RxStat, 1, 2, IO_IN ); + (void)hcfio_string(ifbp, BAP_1, ifbp->IFB_RxFID, HFS_DAT_LEN_ABS, (wci_bufp)&ifbp->IFB_RxLen, 1,2,IO_IN ); + ifbp->IFB_RxLen += HFS_DAT; + } + } + return accu; +}/* hcf_service_nic */ + + + + + +/************************************************************************************************************** +************************** H C F S U P P O R T R O U T I N E S ******************************************** +**************************************************************************************************************/ + + + +/****************************************************************************************************************** + + +.MODULE alloc +.LIBRARY HCF_SUP +.TYPE function +.SYSTEM msdos +.SYSTEM unix +.SYSTEM NW4 +.APPLICATION Support for HCFR routines +.DESCRIPTION allocates a (TX or Notify) FID in NIC RAM and clears it + +.ARGUMENTS + +.RETURNS + 0: failure + <>0: PIF value + +.NARRATIVE + + +.DIAGRAM + 1: execute the allocate command by calling cmd_wait + 2: wait till either the alloc event or a time-out occures + 3: if the alloc event occures, + - read the FID to return it to the caller of alloc + - acknowledge the alloc event + - clear the storage allocated in NIC RAM (see notice below) + 4: since alloc is only called after an Hermes initialize command but before the Hermes enable, a failing + allocation is considered a H/W failure, hence the Miscellaneous Error tally is incremented + +.NOTICE + o Clearing the FID is not only an aesthetical matter, it is also the cheapest (code-size) way to enforce + - correlation between IFB_TxCntl (which is cleared by hcf_disable) and the field HFS_TX_CNTL_ABS of a + IFB_PIF_FID + - zero value of the field HFS_TX_CNTL_ABS of a IFB_DUIF_FID (hcf_send_diag_msg only supports port 0) + + Note that Card Interrupts are disabled as a side effect of hcf_disable when alloc is called. This is a + necessary condition, otherwise the ISR can get control, causing hcf_service_nic to run. This would + constitute an error because hcf_service_nic needs a defined IFB_PIF_FID and IFB_DUIF_FID to manipulate + IFB_PIFRscInd and IFB_DUIFRscInd + + The put_info functions must be called before the alloc calls because of cnfMaxDataLength ;?Really, what is the catch + + +.DIAGRAM +.ENDOC END DOCUMENTATION +*/ +/*-----------------------------------------------------------------------------------------------------------*/ +hcf_16 alloc( IFBP ifbp, int len ) { + +hcf_32 prot_cnt = ifbp->IFB_TickIni; +hcf_16 pif = 0; +hcf_16 zero = 0; + + if ( cmd_wait( ifbp, HCMD_ALLOC, len ) == HCF_SUCCESS ) { /* 1 */ + while ( prot_cnt ) { + prot_cnt--; + if ( IN_PORT_WORD(ifbp->IFB_IOBase + HREG_EV_STAT ) & HREG_EV_ALLOC ) { /* 2 */ + pif = IN_PORT_WORD(ifbp->IFB_IOBase + HREG_ALLOC_FID ); /* 3 */ + OUT_PORT_WORD(ifbp->IFB_IOBase + HREG_EV_ACK, HREG_EV_ALLOC ); + (void)hcfio_string( ifbp, BAP_0, pif, 0, (wci_bufp)&zero, 0, 2, IO_OUT ); //justify void + len = DIV_BY_2( len ); + while ( --len ) OUT_PORT_WORD( ifbp->IFB_IOBase + BAP_0, 0 ); + break; + } + } + } + return pif; +}/* alloc */ + + + + + + + + + +/****************************************************************************************************************** + +.MODULE calibrate +.LIBRARY HCF_SUP +.TYPE function +.SYSTEM msdos +.SYSTEM unix +.SYSTEM NW4 +.APPLICATION Support for HCFR routines +.DESCRIPTION + +.ARGUMENTS + int calibrate( IFBP ifbp ) + +.RETURNS + HCF_SUCCESS + HCF_ERR_TIME_OUT + HCF_..... (via hcf_put_info) + +.NARRATIVE + o + o + + +.DIAGRAM + 1: + 2: + +.NOTICE + o + +.DIAGRAM +.ENDOC END DOCUMENTATION +*/ +/*-----------------------------------------------------------------------------------------------------------*/ +int calibrate( IFBP ifbp ) { + +LTV_STRCT x; // initialization with "= { 2, CFG_TICK_TIME};" causes memset + // to be used under some compilers +int cnt = 10; +int rc = HCF_SUCCESS; +hcf_32 prot_cnt; + +// if ( ifbp->IFB_TickIni == 0 ) { /* 22*/ + x.len = 2; + x.typ = CFG_TICK_TIME; + x.val[0] = CNV_LITTLE_TO_INT( 8 ); //no compile time conversion available + rc = hcf_put_info( ifbp, &x ); + ifbp->IFB_TickIni = 0; /* 22*/ + while ( rc == HCF_SUCCESS && --cnt ) { + prot_cnt = 0; + OUT_PORT_WORD( ifbp->IFB_IOBase + HREG_EV_ACK, HREG_EV_TICK ); + while ( (IN_PORT_WORD( ifbp->IFB_IOBase + HREG_EV_STAT ) & HREG_EV_TICK) == 0 && + ++prot_cnt <= INI_TICK_INI ) /*NOP*/; + ifbp->IFB_TickIni = max( ifbp->IFB_TickIni, prot_cnt); + } + if ( ifbp->IFB_TickIni == INI_TICK_INI ) rc = HCF_ERR_TIME_OUT; + ifbp->IFB_TickIni *= 128; //time out value of 8*128 = 1024 k microseconds +// } + x.val[0] = CNV_LITTLE_TO_INT( ONE_SECOND ); + rc = hcf_put_info( ifbp, &x ); + return rc; +} /* calibrate */ + + + + + + +/************************************************************************************************************** + + +.MODULE cmd_wait +.LIBRARY HCF_SUP +.TYPE function +.SYSTEM msdos +.SYSTEM unix +.SYSTEM NW4 +.APPLICATION Support for HCFR routines +.DESCRIPTION + +.ARGUMENTS + +.RETURNS + hcf_16 + HCF_SUCCESS + HCF_ERR_TIME_OUT + HCF_FAILURE + HCF_ERR_BUSY + +.NARRATIVE +.DIAGRAM + + 1: the test on rc checks whether a BAP initialization or a call to cmd_wait did ever fail. If so, the Hermes + is assumed inoperable/defect, and all subsequent bap_ini/cmd_wait calls are nullified till hcf_disable + clears the IFB_TimStat field. + + 2: Based on the Hermes design, the read of the busy bit is superfluous because we wait for the Cmd bit in + the Event Status register. + + 3: When the Hermes reports on another command than the Host just issued, the two are apparently out of + sync and all bets are off about the consequences. Therefore this situation is treated the same as an + Hermes failure as indicated by time-out (blocking all further bap_ini/cmd_wait calls till hcf_disable. + + 5: If HREG_STAT reflects an error it is either an HCF bug or a H/W problem. Since + no distinction can be made at forehand, the "vague" HCF_FAILURE code is used + +.NOTICE + Due to the general HCF strategy to wait for command completion, a 2nd command can never be excuted + overlapping a previous command. As a consequence the Hermes requirement that no Inquiry command may be + executed if there is still an unacknowledged Inquiry command outstanding, is automatically met. + However, there are two pseudo-asynchronous commands (Diagnose and Download) which do not adhere to this + general HCF strategy. In that case we rely on the MSF to do not overlap these commands, but no protection + is offered by the HCF +.ENDOC END DOCUMENTATION +*/ +/* -------------------------------------------------------------------------------------------------------------------*/ +int cmd_wait( IFBP ifbp, int cmd_code, int par_0 ) { + +int rc = ifbp->IFB_TimStat; +hcf_32 prot_cnt = ifbp->IFB_TickIni; + + + if ( rc == HCF_SUCCESS ) { /* 1 */ + OUT_PORT_WORD( ifbp->IFB_IOBase + HREG_PARAM_0, par_0 ); + OUT_PORT_WORD( ifbp->IFB_IOBase + HREG_CMD, cmd_code ); + while (prot_cnt && (IN_PORT_WORD(ifbp->IFB_IOBase + HREG_EV_STAT) & HREG_EV_CMD) == 0 ) prot_cnt--;/*2 */ + if ( prot_cnt == 0 ) { + rc = ifbp->IFB_TimStat = HCF_ERR_TIME_OUT; + } else { + rc = IN_PORT_WORD( ifbp->IFB_IOBase + HREG_STAT ); + OUT_PORT_WORD( ifbp->IFB_IOBase + HREG_EV_ACK, HREG_EV_CMD ); + if ( (rc ^ cmd_code) & HREG_STAT_CMD_CODE ) { /* 3 */ + rc = ifbp->IFB_TimStat = HCF_FAILURE; + } else { + rc &= HREG_STAT_CMD_RESULT; //Hermes defined Result Code + } + if ( rc ) rc = HCF_FAILURE; /* 5 */ + } + } + return rc; +}/* cmd_wait */ + + + +/*********************************************************************************************************************** + + + Name: enable_int + + Summary: Enables a specific Hermes interrupt + + Parameters: + ifbp address of the Interface Block + event Hermes event to be enabled as interrupt source + + Remarks: To get the contents of the IntEn register changed is a two step process: + + o change the "shadow" in IFB_IntEnMask + + o call hcf_action to actually copy the shadow to the IntEn register. + + To prevent a change in the "Card Interrupt En/Disabled state, a balancing + pair of HCF_ACT_INT_OFF and HCF_ACT_INT_ON must be used. To prevent + a temporary enabling as undesirable side effect, the first call must be + HCF_ACT_INT_OFF. + Note that at the very first interrupt, hcf_service_nic causes the removal of + the Tick and Cmd bit in the IntEn register. + + +.DIAGRAM +***********************************************************************************************************************/ +//#pragma Reminder2( "enable_int: shouldn't this be used in more places" ) +void enable_int(IFBP ifbp, int event ) { + + ifbp->IFB_IntEnMask |= event; + (void)hcf_action( ifbp, HCF_ACT_INT_OFF ); + (void)hcf_action( ifbp, HCF_ACT_INT_ON ); + return; + +}/* enable_int */ + + + + + +/**************************************************************************************************************************** + + +.MODULE ini_hermes +.LIBRARY HCF_SUP +.TYPE function +.SYSTEM msdos +.SYSTEM unix +.SYSTEM NW4 +.APPLICATION Support for HCF routines +.DESCRIPTION + +.ARGUMENTS + +.RETURNS + void + + + As side effect of the Hermes Initialize command, the interrupts are disabled +.NARRATIVE + 1: + 2: +.DIAGRAM + +.ENDOC END DOCUMENTATION +-------------------------------------------------------------------------------------------------------------*/ +int ini_hermes( IFBP ifbp ) { + +int rc = HCF_ERR_NO_NIC; +//hcf_32 prot_cnt = ifbp->IFB_TickIni = INI_TICK_INI; //initialize at best guess before calibration +hcf_32 prot_cnt; + + if ( ifbp->IFB_CardStat & CARD_STAT_PRESENT ) {//present /* 7 */ + if ( ifbp->IFB_TickIni == 0) ifbp->IFB_TickIni = INI_TICK_INI; //initialize at best guess before calibration + prot_cnt = ifbp->IFB_TickIni; + while (prot_cnt && IN_PORT_WORD(ifbp->IFB_IOBase + HREG_CMD ) & HCMD_BUSY ) prot_cnt--; /* 16*/ + + rc = HCF_ERR_TIME_OUT; + if ( prot_cnt != 0 ) { + rc = cmd_wait( ifbp, HCMD_INI, 0 ); /* 18*/ + OUT_PORT_WORD( ifbp->IFB_IOBase + HREG_EV_ACK, 0xFFFF ); + } + + } + return rc; +}/* ini_hermes */ + + +/**************************************************************************************************************************** + + +.MODULE isr_info +.LIBRARY HCF_SUP +.TYPE function +.SYSTEM msdos +.SYSTEM unix +.SYSTEM NW4 +.APPLICATION Support for HCF routines +.DESCRIPTION + +.ARGUMENTS + +.RETURNS + void + +.NARRATIVE + 1: info[0] becomes the length of the T-field + the length of the Value-field in words. Note that it is + dangerous to determine the length of the Value field by decrementing info[0], because if -e.g. as a + result of a bug in the Hermes as has happened in real life- info[0] becomes 0, a decrement results in + a very large number. Therefore all code is crafted around an unchanged info[0], with the intention to + make the HCF more robust against I/F violations. + 2: This is an example of the strategy described in #1 above. Info[0] is expected to be HCF_NIC_TAL_CNT + 1. + By using a pre-decrement, this value results in HCF_NIC_TAL_CNT movements of a single tally value into + the IFB_NIC_Tallies area of the IFB. + 3: Although put_info_mb is robust against a len-parameter with value zero, it accepts any bogus value + for the type-parameter. +.DIAGRAM + +.ENDOC END DOCUMENTATION +-------------------------------------------------------------------------------------------------------------*/ +void isr_info( IFBP ifbp ) { + +hcf_16 info[2], tmp; +hcf_32 *p; + + tmp = IN_PORT_WORD( ifbp->IFB_IOBase + HREG_INFO_FID ); + (void)hcfio_string(ifbp, BAP_1, tmp, 0, (wci_bufp)info, 2, sizeof(info), IO_IN ); /* 1 */ + + if ( info[1] == CFG_TALLIES ) { + if ( info[0] > HCF_NIC_TAL_CNT ) info[0] = HCF_NIC_TAL_CNT + 1; /* 2 */ + p = (hcf_32*)&ifbp->IFB_NIC_Tallies;//.TxUnicastFrames; + while ( --info[0] ) *p++ += IN_PORT_WORD( ifbp->IFB_IOBase + BAP_1 ); + } + return; +}/* isr_info */ + + + + + + +#if defined _M_I86TM +#endif //_M_I86TM + +/*********************************************************************************************************************** + + + Name: put_info + + Summary: stores Hermes configuration information in the ConfigTable of the IFB + + Parameters: + ifbp address of the Interface Block + ltvp address in NIC RAM where LVT-records are located + + +.NARRATIVE + +**************************************************************************************************************/ +int put_info( IFBP ifbp, LTVP ltvp ) { + +int cnt = 3; +//hcf_16 i = ltvp->len - 1; +int rc = HCF_SUCCESS; + + + if ( ifbp->IFB_CardStat & CARD_STAT_PRESENT ) { + + + do { + rc = hcfio_string( ifbp, BAP_1, + ltvp->typ, 0, (wci_bufp)ltvp, 2, MUL_BY_2(ltvp->len + 1), IO_OUT_CHECK ); + } while ( cnt-- && rc != HCF_SUCCESS ); + if ( rc == HCF_SUCCESS ) rc = cmd_wait( ifbp, HCMD_ACCESS + HCMD_ACCESS_WRITE, ltvp->typ ); + } + + return rc; +}/* put_info */ + + + + + + + diff -urN orig/drivers/net/pcmcia/wvlan_hcf.h linux/drivers/net/pcmcia/wvlan_hcf.h --- orig/drivers/net/pcmcia/wvlan_hcf.h Thu Jan 1 01:00:00 1970 +++ linux/drivers/net/pcmcia/wvlan_hcf.h Wed Oct 9 19:03:08 2002 @@ -0,0 +1,287 @@ +/* This file is part of the Hardware Control Functions Light (HCF-light) library + to control the Lucent Technologies WaveLAN/IEEE Network I/F Card. + The HCF is the implementation of the Wireless Connection I/F (WCI). + + The HCF-light files are a subset of the HCF files. The complete set offers a + number of additional facilities, e.g. firmware download, Etherner-II encapsulation, + additional diagnostic facilities, ASSERT logic to support debugging, 802.11 support, + Configuration Management. + This complete set is explicitely not in the Public Domain but can be made + available under certain restriction. (see the pointer below for support) + + The HCF-light files are 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. + + At the time of this writing, you can request for support at: + betasupport@wavelan.com + + Documentation is expected to be available in the week of 8 Februari 1999 + +*/ + + +#ifndef HCF_H +#define HCF_H 1 + +/************************************************************************************************************* +* +* FILE : hcf.h *************** 2.0 ************************************************************************* +* +* DATE : 2000/01/06 23:30:52 1.2 +* +* AUTHOR : Nico Valster +* +* DESC : Definitions and Prototypes for MSF as well as HCF sources +* +* Customizable via HCFCFG.H +* +* +************************************************************************************************************** +Instructions to convert HCF.H to HCF.INC by means of H2INC + +Use a command line which defines the specific macros and command line options +needed to build the C-part, e.g. for the DOS ODI driver + `h2inc /C /Ni /Zp /Zn hcf hcf.h` + + +************************************************************************************************************** +* COPYRIGHT (c) 1996, 1997, 1998 by Lucent Technologies. All Rights Reserved. +**************************************************************************************************************/ + +/**************************************************************************** +wvlan_hcf.h,v +Revision 1.2 2000/01/06 23:30:52 root +*** empty log message *** + + * + * Rev 1.0 02 Feb 1999 14:32:30 NVALST + * Initial revision. +Revision 1.2 1999/02/01 22:58:35 nico +*** empty log message *** + +Revision 1.1 1999/01/30 19:24:39 nico +Initial revision + +Revision 1.1 1999/01/30 19:07:57 nico +Initial revision + + * + * Rev 1.110 29 Jan 1999 15:52:42 NVALST + * intermediate, maybe working but seems to need two times to load in + * light-version + * + * Rev 2.12 29 Jan 1999 10:48:44 NVALST + * + * Rev 1.108 28 Jan 1999 14:43:22 NVALST + * +****************************************************************************/ + +/************************************************************************************************************** +* +* CHANGE HISTORY +* + 961018 - NV + Original Entry + +*************************************************************************************************************/ + + + +#include "wvlan_hcfcfg.h" // System Constants to be defined by the MSF-programmer to tailor the HCF +#include //do not move to hcf.cpp to keep Chris (Borland) and Marc (MSVC 4)happy (defines NULL) + +#include "wvlan_mdd.h" // Include file common for HCF, MSF, UIL, USF + +/************************************************************************************************************/ +/****************** H C F F U N C T I O N P A R A M E T E R ****************************************/ +/************************************************************************************************************/ + +//offsets for hcf_put_data and hcf_get_data + + +// 802.3/E-II/802.11 offsets to access Hermes control fields +#define HFS_STAT -0x2E //0x0000 +#define HFS_STAT_ERR RX_STAT_ERR //link "natural" HCF name to "natural" MSF name + +#define HFS_Q_INFO -0x28 //0x0006 +#define HFS_TX_CNTL -0x22 //0x000C +#define HFS_FRAME_CNTL -0x20 //0x000E +#define HFS_ID -0x1E //0x0010 + +// 802.11 relative offsets to access 802.11 header fields +#define HFS_ADDR_1 0x00 //0x0012 +#define HFS_ADDR_2 0x06 //0x0018 +#define HFS_ADDR_3 0x0C //0x001E +#define HFS_SEQ_CNTL 0x12 //0x0024 +#define HFS_ADDR_4 0x14 //0x0026 +#define HFS_DAT_LEN 0x1A //0x002C + +// 802.3 / E-II relative offsets to access 802.3 header fields +#define HFS_ADDR_DEST 0x00 //0x002E +#define HFS_ADDR_SRC 0x06 //0x0034 +#define HFS_LEN 0x0C //0x003A +#define HFS_DAT 0x0E //0x003C + +// E-II relative offsets to access SNAP header fields +#define HFS_TYPE 0x14 //0x0042 //Eternet-II type in 1042/Bridge-Tunnel encapsulated frame + + +//#define HCF_ACT_INT_PENDING 0x0001 //interrupt pending, return status HCF_ACT_INT_OFF + + + +/*************************************************************************************************************/ +/**************** H C F F U N C T I O N R E T U R N C O D E S ***************************************/ +/*************************************************************************************************************/ + +//Debug Purposes only !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +#define HREG_EV_TICK 0x8000 //WMAC Controller Auxiliary Timer Tick +#define HREG_EV_RES 0x4000 //WMAC Controller H/W error (Wait Time-out) +#define HREG_EV_INFO_DROP 0x2000 //WMAC did not have sufficient RAM to build Unsollicited Frame +#define HREG_EV_NO_CARD 0x0800 /* PSEUDO event: card removed */ +#define HREG_EV_DUIF_RX 0x0400 /* PSEUDO event: WMP frame received */ +#define HREG_EV_INFO 0x0080 //WMAC Controller Asynchronous Information Frame +#define HREG_EV_CMD 0x0010 //WMAC Controller Command completed, Status and Response avaialble +#define HREG_EV_ALLOC 0x0008 //WMAC Controller Asynchronous part of Allocation/Reclaim completed +#define HREG_EV_TX_EXC 0x0004 //WMAC Controller Asynchronous Transmission unsuccessful completed +#define HREG_EV_TX 0x0002 //WMAC Controller Asynchronous Transmission successful completed +#define HREG_EV_RX 0x0001 //WMAC Controller Asynchronous Receive Frame + + + +//========================================= T A L L I E S =================================================== + +typedef struct CFG_HERMES_TALLIES_STRCT { //Hermes Tallies (IFB substructure) + hcf_32 TxUnicastFrames; + hcf_32 TxMulticastFrames; + hcf_32 TxFragments; + hcf_32 TxUnicastOctets; + hcf_32 TxMulticastOctets; + hcf_32 TxDeferredTransmissions; + hcf_32 TxSingleRetryFrames; + hcf_32 TxMultipleRetryFrames; + hcf_32 TxRetryLimitExceeded; + hcf_32 TxDiscards; + hcf_32 RxUnicastFrames; + hcf_32 RxMulticastFrames; + hcf_32 RxFragments; + hcf_32 RxUnicastOctets; + hcf_32 RxMulticastOctets; + hcf_32 RxFCSErrors; + hcf_32 RxDiscards_NoBuffer; + hcf_32 TxDiscardsWrongSA; + hcf_32 RxWEPUndecryptable; + hcf_32 RxMsgInMsgFragments; + hcf_32 RxMsgInBadMsgFragments; +}CFG_HERMES_TALLIES_STRCT; + + +//Note this way to define CFG_TALLIES_STRCT_SIZE implies that all tallies must keep the same (hcf_32) size +#define HCF_NIC_TAL_CNT (sizeof(CFG_HERMES_TALLIES_STRCT)/ sizeof(hcf_32)) +#define HCF_TOT_TAL_CNT (HCF_NIC_TAL_CNT) + +/************************************************************************************************************/ +/*********** W C I F U N C T I O N S P R O T O T Y P E S ******************************************/ +/************************************************************************************************************/ + +#define IFB_VERSION 0x82 /* initially 80, to be incremented by every IFB layout change */ + + + +/* identifier IFB_STRCT on typedef line needed to get the individual fields in the MS Browser DataBase */ +typedef struct IFB_STRCT{ //I/F Block +/* MSF readable part of Result block structure *************************************/ + hcf_io IFB_IOBase; /* I/O address of Hermes chip as passed by MSF at hcf_connect call */ +#if defined HCF_PORT_IO + hcf_16 IFB_IOBase_pad; // Optional field, makes IFB-layout independent of IFB_IOBase size +#endif //HCF_PORT_IO + hcf_16 IFB_IORange; // I/O Range used by Hermes chip + hcf_8 IFB_Version; /* initially 0, to be incremented by every IFB layout change */ + hcf_8 IFB_Slack_2; /* align/slack space */ + hcf_8 IFB_HCFVersionMajor; // Major version of the HCF.0x01 for this release + hcf_8 IFB_HCFVersionMinor; /* Minor version of the HCF. Incremented for each coding maintenance + * cycle. 0x01 for the Initial release */ + CFG_HERMES_TALLIES_STRCT IFB_NIC_Tallies; //Hermes tallies + +/* part I (survives hcf_disable) ************************************************************************/ + hcf_16 IFB_CardStat; /* see Design spec */ + hcf_16 IFB_FSBase; // frame type dependent offset (HFS_ADDR_1_ABS or HFS_ADDR_DEST_ABS) + hcf_16 IFB_RxFence; // frame type dependent gap fence (HFS_ADDR_DEST_ABS or HFS_LEN_ABS) + hcf_16 IFB_IntOffCnt; /* see Design spec */ + hcf_32 IFB_TickIni; /* initialization of counter for 1 ms processor loop */ + /* keep this unsigned otherwise the "clever" ASSERT in hcf_disable + * has a higher risk to get into trouble on slow machines + * keep this hcf_16 to prevent a "close to infinity" time out if + * calibration fails on 32-bits machine */ + hcf_16 IFB_Magic; /* see Design spec */ + hcf_16 IFB_Slack_4[2]; /* align/slack space */ + +/* part II (cleared or re-initialized at hcf_disable/hcf_enable) *****************************************/ + hcf_8 IFB_PIFRscInd; /* see Design spec //;?Q:int better than hcf_8 A: No! */ + hcf_8 IFB_DUIFRscInd; /* Value indicating the command resource availability for the + * Driver-Utility I/F (i.e. hcf_send_diag_msg). */ + /* Values: */ + /* * No command resource 0 */ + /* * Command resource available 01h-FFh */ + hcf_8 IFB_NotifyRscInd; /* see Design spec //;?Q:int better than hcf_8 A: No! */ + hcf_8 IFB_Slack_6; /* align/slack space */ + hcf_16 IFB_PIF_FID; /* see Design spec */ + hcf_16 IFB_DUIF_FID; /* field which contains FID value identifying the Tx Frame Structure, + * to be used by hcf_send_diag_msg */ + hcf_16 IFB_Notify_FID; /* field which contains FID value identifying the Notify Frame Struct + * to be used by hcf_put_info in case of Notify type codes */ + hcf_16 IFB_RxFID; /* see Design spec */ + hcf_16 IFB_MB_FID; /* pass appropriate FID to hcf_put_mb_info */ + hcf_16 IFB_TxFrameType; /* see Design spec */ + hcf_16 IFB_RxLen; /* see Design spec */ + hcf_16 IFB_RxStat; /* see Design spec */ + hcf_16 IFB_UnloadIdx; /* see Design spec */ + hcf_16 IFB_PIFLoadIdx; /* see Design spec */ + hcf_8 IFB_TxCntl[2]; /* contents of HFS_TX_CNTL field of TFS + * 0: MACPort, 1: StrucType,TxEx,TxOK */ + hcf_16 IFB_BAP_0[2]; /* offset + * RID/FID */ + hcf_16 IFB_BAP_1[2]; /* offset + * RID/FID */ + hcf_16 IFB_IntEnMask; /* see Design spec */ + hcf_16 IFB_TimStat; /* BAP initialization or Cmd Completion failed once */ + +}IFB_STRCT; + + + +typedef IFB_STRCT* IFBP; + + +EXTERN_C int hcf_action (IFBP ifbp, hcf_action_cmd cmd ); +EXTERN_C void hcf_assert (IFBP ifbp, wci_bufp file_name, unsigned int line_number, int q ); +EXTERN_C void hcf_connect (IFBP ifbp, hcf_io io_base ); +EXTERN_C int hcf_disable (IFBP ifbp, hcf_16 port ); +EXTERN_C void hcf_disconnect (IFBP ifbp ); +EXTERN_C int hcf_enable (IFBP ifbp, hcf_16 port ); +EXTERN_C int hcf_get_info (IFBP ifbp, LTVP ltvp ); +EXTERN_C int hcf_get_data (IFBP ifbp, int offset, wci_bufp bufp, int len ); +EXTERN_C int hcf_service_nic (IFBP ifbp ); +//EXTERN_C void hcf_put_data (IFBP ifbp, wci_bufp bufp, int len ); +EXTERN_C void hcf_put_data (IFBP ifbp, wci_bufp bufp, int len, hcf_16 port ); +EXTERN_C int hcf_put_info (IFBP ifbp, LTVP ltvp ); +EXTERN_C int hcf_put_header (IFBP ifbp, int offset, wci_bufp bufp, int len, hcf_8 check ); +EXTERN_C int hcf_send (IFBP ifbp, hcf_16 type ); +EXTERN_C int hcf_send_diag_msg (IFBP ifbp, hcf_16 type, wci_bufp bufp, int len ); + + + + +#endif /* HCF_H */ + diff -urN orig/drivers/net/pcmcia/wvlan_hcfcfg.h linux/drivers/net/pcmcia/wvlan_hcfcfg.h --- orig/drivers/net/pcmcia/wvlan_hcfcfg.h Thu Jan 1 01:00:00 1970 +++ linux/drivers/net/pcmcia/wvlan_hcfcfg.h Wed Oct 9 19:03:09 2002 @@ -0,0 +1,1084 @@ +/* This file is part of the Hardware Control Functions Light (HCF-light) library + to control the Lucent Technologies WaveLAN/IEEE Network I/F Card. + The HCF is the implementation of the Wireless Connection I/F (WCI). + + The HCF-light files are a subset of the HCF files. The complete set offers a + number of additional facilities, e.g. firmware download, Etherner-II encapsulation, + additional diagnostic facilities, ASSERT logic to support debugging, 802.11 support, + Configuration Management. + This complete set is explicitely not in the Public Domain but can be made + available under certain restriction. (see the pointer below for support) + + The HCF-light files are 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. + + At the time of this writing, you can request for support at: + betasupport@wavelan.com + + Documentation is expected to be available in the week of 8 Februari 1999 + +*/ +#include + +#ifndef HCFCFG_H +#define HCFCFG_H 1 + +/************************************************************************************************************** +* +* FILE : hcfcfg.tpl // hcfcfg.h **************************** 2.0 ******************************************** +* +* DATE : 2000/12/13 22:58:23 1.3 +* +* AUTHOR : Nico Valster +* +* DESC : HCF Customization Macros +* +*************************************************************************************************************** +* COPYRIGHT (c) 1994, 1995 by AT&T. All Rights Reserved. +* COPYRIGHT (c) 1996, 1997, 1998 by Lucent Technologies. All Rights Reserved. +* +*************************************************************************************************************** +* +* hcfcfg.tpl list all #defines which must be specified to: +* I: adjust the HCF functions defined in HCF.CPP to the characteristics of a specific environment +* o maximum sizes for messages and notification frames, persistent configuration storage +* o Endianess +* +* II: Compiler specific macros +* o port I/O macros +* o type definitions +* +* III: Environment specific ASSERT macro +* +* IV: Compiler specific +* +* V: ;? specific +* +* +* By copying HCFCFG.TPL to HCFCFG.H and -if needed- modifying the #defines the WCI functionality can be +* tailored +* T O D O : A D D A R E C I P E T O D O T H I S +* +**************************************************************************************************************/ + +/**************************************************************************** +wvlan_hcfcfg.h,v +Revision 1.3 2000/12/13 22:58:23 root +*** empty log message *** + +Revision 1.2 2000/01/06 23:30:52 root +*** empty log message *** + + * + * Rev 1.0 02 Feb 1999 14:32:32 NVALST + * Initial revision. +Revision 1.2 1999/02/01 21:01:41 nico +*** empty log message *** + +Revision 1.1 1999/01/30 19:24:39 nico +Initial revision + +Revision 1.1 1999/01/30 19:07:57 nico +Initial revision + + * + * Rev 1.110 29 Jan 1999 15:52:44 NVALST + * intermediate, maybe working but seems to need two times to load in + * light-version + * + * Rev 1.109 29 Jan 1999 12:50:26 NVALST + * intermediate + * + * Rev 1.108 28 Jan 1999 14:43:24 NVALST + * intermediate, once more correction of loop in hcf_service_nic + download + * passed to Marc + * + * Rev 1.107 27 Jan 1999 13:53:24 NVALST + * intermediate, once more correction of loop in hcf_service_nic + * + * Rev 1.106 26 Jan 1999 16:42:46 NVALST + * intermediate, corrected loop in hcf_service_nic (which was as result of a + * walkthrough, changed from a bug without consequences into one with consequences + * + * Rev 1.105 25 Jan 1999 14:24:48 NVALST + * intermediate, hopefully suitable for release + * + * Rev 1.104 22 Jan 1999 16:59:34 NVALST + * intermediate, minor corrections + some HCF-L stuff + * + * Rev 1.103 15 Jan 1999 15:14:46 NVALST + * intermediate, deposited as HCF2.10 + * +*************************************************************************************************/ + + +/**************************************************************************** +* +* CHANGE HISTORY +* + + 960702 - NV + Original Entry - derived from HCF 2.12 +*************************************************************************************************/ + + +/* * * * * * * * * * * * * * * * * * * * * * * I * * * * * * * * * * * * * * * * * * * * * * */ + +/* Endianess + * Default: HCF_LITTLE_ENDIAN + * Little Endian (a.k.a. Intel), least significant byte first + * Big Endian (a.k.a. Motorola), most significant byte first + * + * If neither HCF_LITTLE_ENDIAN nor HCF_BIG_ENDIAN, the defintion of the following macros must be supplied + * by the MSF programmer: + * o CNV_LITTLE_TO_INT(w) interprets the 16-bits input value as Little Endian, returns an hcf_16 + * o CNV_BIG_TO_INT(w) interprets the 16-bits input value as Big Endian, returns an hcf_16 + * o CNV_INT_TO_BIG_NP(addr) converts in place the 16-bit value addressed by a near pointer from hcf_16 + * to Big Endian + * o CNV_LITTLE_TO_INT_NP(addr) converts in place the 16-bit value addressed by a near pointer from + * Little endian to hcf_16 + * + * At a number of places in the HCF code, the CNV_INT_TO_BIG_NP macro is used. While it does have the desired + * effect on all platforms, it's naming is misleading, so revisit all places where these CNV macros are used + * to assure the right name is used at the right place. + * Hint: introduce CNV_HOST_TO_NETWORK names if appropriate + * + */ + +#ifdef __BIG_ENDIAN +#define HCF_BIG_ENDIAN // selects Big Endian (a.k.a. Motorola), most significant byte first +#else +#define HCF_LITTLE_ENDIAN // selects Little Endian (a.k.a. Intel), least significant byte first +#endif + +/* I/O Address size + * Platforms which use port mapped I/O will (in general) have a 64k I/O space, conveniently expressed in + * a 16-bits quantity + * Platforms which use memory mapped I/O will (in general) have an I/O space much larger than 64k, + * and need a 32-bits quantity to express the I/O base + * To accomodate this the macros HCF_PORT_IO and HCF_MEM_IO are available. Exactly 1 of these must be + * defined. If HCF_PORT_IO is defined, the HCF will use an hcf_16 to express I/O base and store in the + * IFB. If HCF_MEM_IO, an hcf_32 is used for this purpose. The default is HCF_PORT_IO + */ +#define HCF_PORT_IO +//#define HCF_MEM_IO + +/* Alignment + * Some platforms can access words on odd boundaries (with possibly an performance impact), at other + * platforms such an access may result in a memory access violation. + * It is assumed that everywhere where the HCF casts a char pointer into a word pointer, the + * alignment criteria are met. This put some restrictions on the MSF, which are assumed to be + * "automatically" fullfilled at the applicable platforms + * To assert this assumption, the macro HCF_ALIGN can be defined. The default vaslue is 0, meaning no + * alignment, a value of 2 means word alignment, other values are invalid + */ + +/* * * * * * * * * * * * * * * * * * * * * * * II * * * * * * * * * * * * * * * * * * * * * * */ + + + +/************************************************************************************************/ +/****************** C O M P I L E R S P E C I F I C M A C R O S ***************************/ +/************************************************************************************************/ +/************************************************************************************************* +* +* The platforms supported by this version are: +* - Microsoft Visual C 1.5 (16 bits platform) +* - Microsoft Visual C 2.0 (32 bits platform) +* - Watcom C/C++ 9.5 +* - SCO UNIX +* +* In this version of hcfiocfg.tpl all macros except the MSVC 1.5 versions are either dependent on +* compiler/environment supplied macros (e.g. _MSC_VER or "def-ed out" +* +* By selecting the appropriate Macro definitions by means of modifying the +* "#ifdef 0/1" lines, the HCF can be adjusted for the I/O chararcteristics of +* a specific compiler +* +* If needed the macros can be modified or replaced with definitions appropriate +* for your personal platform +* If you need to make such changes it is appreciated if you inform Lucent Technologies WCND Utrecht +* That way the changes can become part of the next release of the WCI +* +* +* The prototypes and functional description of the macros are: +* +* hcf_8 IN_PORT_BYTE( hcf_16 port) +* Reads a byte (8 bits) from the specified port +* +* hcf_16 IN_PORT_WORD( hcf_16 port) +* Reads a word (16 bits) from the specified port +* +* void OUT_PORT_BYTE( hcf_16 port, hcf_8 value) +* Writes a byte (8 bits) to the specified port +* +* void OUT_PORT_WORD( hcf_16 port, hcf_16 value) +* Writes a word (16 bits) to the specified port +* +* void IN_PORT_STRING( port, dest, len) +* Reads len number of words from the specified port to the (FAR) address dest in PC-RAM +* Note that len specifies the number of words, NOT the number of bytes +* !!!NOTE, although len specifies the number of words, dest MUST be a char pointer NOTE!!! +* See also the common notes for IN_PORT_STRING and OUT_PORT_STRING +* +* void OUT_PORT_STRING( port, src, len) +* Writes len number of words from the (FAR) address src in PC-RAM to the specified port +* Note that len specifies the number of words, NOT the number of bytes. +* !!!NOTE, although len specifies the number of words, src MUST be a char pointer NOTE!!! +* +* The peculiar combination of word-length and char pointers for IN_PORT_STRING as well as +* OUT_PORT_STRING is justified by the assumption that it offers a more optimal algorithm +* +* Note to the HCF-implementor: +* Due to the passing of the parameters to compiler specific blabla......... +* do not use "expressions" as parameters, e.g. don't use "ifbp->IFB_IOBase + HREG_AUX_DATA" but +* assign this to a temporary variable. +* +* +* NOTE!! For convenience of the MSF-programmer, all {IN|OUT}_PORT_{BYTE|WORD|STRING} macros are allowed to +* modify their parameters (although some might argue that this would constitute bad coding +* practice). This has its implications on the HCF, e.g. as a consequence these macros should not +* be called with parameters which have side effects, e.g auto-increment. +* +* NOTE!! in the Micosoft implementation of inline assembly it is O.K. to corrupt all flags except +* the direction flag and to corrupt all registers except the segment registers and EDI, ESI, +* ESP and EBP (or their 16 bits equivalents). +* Other environments may have other constraints +* +* NOTE!! in the Intel environment it is O.K to have a word (as a 16 bits quantity) at a byte boundary, +* hence IN_/OUT_PORT_STRING can move words between PC-memory and NIC-memory with as only +* constraint that the the words are on a word boundary in NIC-memory. This does not hold true +* for all conceivalble environments, e.g. an Motorola 68xxx does not allow this, in other +* words whenever there is a move from address in 2*n in one memory type to address 2*m+1 in the +* other type, the current templates for IN_/OUT_PORT_STRING are unsuitable. Probably the +* boundary conditions imposed by these type of platforms prevent this case from materializing +* +*************************************************************************************************/ + +/************************************************************************************************/ +/**************************** N E T W A R E 3 8 6 *******************************************/ +/************************************************************************************************/ + +#if defined __NETWARE_386__ /* WATCOM */ + +#define MSF_COMPONENT_ID COMP_ID_ODI_32 +#define HCF_STA //station characteristics + +#include + +//#define CNV_LITTLE_TO_INT(x) (x) // No endianess conversion needed + +typedef unsigned char hcf_8; +typedef unsigned short hcf_16; +typedef unsigned long hcf_32; + +#define FAR // flat 32-bits code +#define BASED + +#define IN_PORT_BYTE(port) ((hcf_8)inp( (hcf_io)(port) )) //;?leave out cast to hcf_8;? +#define IN_PORT_WORD(port) (inpw( (hcf_io)(port) )) +#define OUT_PORT_BYTE(port, value) (outp( (hcf_io)(port), value )) +#define OUT_PORT_WORD(port, value) (outpw( (hcf_io)(port), value )) + +#define IN_PORT_STRING( prt, dst, n) while ( n-- ) { *(hcf_16 FAR*)dst = IN_PORT_WORD( prt ); dst += 2; } +#define OUT_PORT_STRING( prt, src, n) while ( n-- ) { OUT_PORT_WORD( prt, *(hcf_16 FAR*)src ) ; src += 2; } + +#endif // __NETWARE_386__ + + +// Note: +// Visual C++ 1.5 : _MSC_VER == 800 +// Visual C++ 4.0 : _MSC_VER == 1000 +// Visual C++ 4.2 : _MSC_VER == 1020 + + +/************************************************************************************************/ +/**************************** P A C K E T D R I V E R ***************************************/ +/********************************** D O S O D I *********************************************/ +/************************************************************************************************/ + +#if defined WVLAN_42 || defined WVLAN_43|| defined WVLAN43L + +#pragma warning ( disable: 4001 ) + +#define HCF_STA //station characteristics + +#if defined WVLAN_43 +#define MSF_COMPONENT_ID COMP_ID_ODI_16 +#define MSF_COMPONENT_VAR 1 +#define MSF_COMPONENT_MAJOR_VER 1 +#define MSF_COMPONENT_MINOR_VER 4 + +#elif defined WVLAN_42 +#define MSF_COMPONENT_ID COMP_ID_PACKET +#define MSF_COMPONENT_VAR 1 +#define MSF_COMPONENT_MAJOR_VER 1 +#define MSF_COMPONENT_MINOR_VER 24 + +#elif defined WVLAN43L +#define HCF_MAX_CONFIG 0 + +#define MSF_COMPONENT_MAJOR_VER 0 +#define MSF_COMPONENT_MINOR_VER 1 + +#endif //WVLAN_xx + +#define FAR __far // segmented 16 bits mode +#if defined _M_I86TM +#define BASED __based(__segname("_CODE")) +#else +#define BASED +#endif // _M_I86TM + +typedef unsigned char hcf_8; +typedef unsigned short hcf_16; +typedef unsigned long hcf_32; + +#include +#include +//#ifndef _DEBUG +#pragma intrinsic( _inp, _inpw, _outp, _outpw ) +//#endif // _DEBUG + +#define IN_PORT_BYTE(port) ((hcf_8)_inp( (hcf_io)(port) )) +#define IN_PORT_WORD(port) ((hcf_16)_inpw( (hcf_io)(port) )) +#define OUT_PORT_BYTE(port, value) ((void)_outp( (hcf_io)(port), value )) +#define OUT_PORT_WORD(port, value) ((void)_outpw( (hcf_io)(port), value )) + +#if defined HCF_STRICT +#define IN_PORT_STRING( prt, dst, n) { ips( prt, dst, n); } +#define OUT_PORT_STRING( prt, dst, n) { ops( prt, dst, n); } +#elif 0 // C implementation +#define IN_PORT_STRING( prt, dst, n) while ( n-- ) { *(hcf_16 FAR*)dst = IN_PORT_WORD( prt ); dst += 2; } +#define OUT_PORT_STRING( prt, src, n) while ( n-- ) { OUT_PORT_WORD( prt, *(hcf_16 FAR*)src ) ; src += 2; } +//;? WHY hcf_16 FAR*)src and not unsigned char FAR*)src +#else // Assembler implementation +#define IN_PORT_STRING( port, dest, len) __asm \ +{ \ + __asm push di \ + __asm push es \ + __asm mov cx,len \ + __asm les di,dest \ + __asm mov dx,port \ + __asm rep insw \ + __asm pop es \ + __asm pop di \ +} + +#define OUT_PORT_STRING( port, src, len) __asm \ +{ \ + __asm push si \ + __asm push ds \ + __asm mov cx,len \ + __asm lds si,src \ + __asm mov dx,port \ + __asm rep outsw \ + __asm pop ds \ + __asm pop si \ +} + +#endif // Asm or C implementation + +#endif /* WVLAN_43, WVLAN_42 (DOS ODI, Packet Driver) */ + + +/************************************************************************************************/ +/************************************* W C I T S T *********************************************/ +/************************************************************************************************/ + +#if defined WCITST + +#pragma warning ( disable: 4001 ) + +#define HCF_STA //station characteristics +#define HCF_AP //AccesPoint characteristics + +#if _CONSOLE +#define FAR // flat 32 bits mode (also defined in WINDEF.H) +#define BASED +#else +#define FAR __far // segmented 16 bits mode +#if defined _M_I86TM +#define BASED __based(__segname("_CODE")) +#else +#define BASED +#endif // _M_I86TM +#endif //_CONSOLE + +typedef unsigned char hcf_8; +typedef unsigned short hcf_16; +typedef unsigned long hcf_32; + +#include +#include +#ifndef _DEBUG +#pragma intrinsic( _inp, _inpw, _outp, _outpw ) +#endif // _DEBUG + +#ifdef LOG +extern FILE* utm_logfile; +hcf_16 ipw( hcf_16 port ); +hcf_8 ipb( hcf_16 port ); +void opw( hcf_16 port, hcf_16 value ); +void opb( hcf_16 port, hcf_8 value ); + +#define IN_PORT_BYTE(port) ipb( (hcf_io)(port) ) +#define IN_PORT_WORD(port) ipw( (hcf_io)(port) ) +#define OUT_PORT_BYTE(port, value) opb( (hcf_io)(port), (hcf_8)(value) ) +#define OUT_PORT_WORD(port, value) opw( (hcf_io)(port), (hcf_16)(value) ) +#else //LOG +#define IN_PORT_BYTE(port) ((hcf_8)_inp( (hcf_io)(port) )) +#define IN_PORT_WORD(port) ((hcf_16)_inpw( (hcf_io)(port) )) +#define OUT_PORT_BYTE(port, value) ((void)_outp( (hcf_io)(port), value )) +#define OUT_PORT_WORD(port, value) ((void)_outpw( (hcf_io)(port), value )) +#endif //LOG + +#define toch_maar_geen_asm +#if defined(toch_maar_asm) && !defined(__DA_C__) //;? temporary solution to satisfy DA-C +#define IN_PORT_STRING( port, dest, len) __asm \ +{ \ + __asm push di \ + __asm push es \ + __asm mov cx,len \ + __asm les di,dest \ + __asm mov dx,port \ + __asm rep insw \ + __asm pop es \ + __asm pop di \ +} + +#define OUT_PORT_STRING( port, src, len) __asm \ +{ \ + __asm push si \ + __asm push ds \ + __asm mov cx,len \ + __asm lds si,src \ + __asm mov dx,port \ + __asm rep outsw \ + __asm pop ds \ + __asm pop si \ +} + +#else //toch_maar_asm && !__DA_C__ +#define IN_PORT_STRING( prt, dst, n) while ( n-- ) { *(hcf_16 FAR*)dst = IN_PORT_WORD( prt ); dst += 2; } +#define OUT_PORT_STRING( prt, src, n) while ( n-- ) { OUT_PORT_WORD( prt, *(hcf_16 FAR*)src ) ; src += 2; } +//;? WHY hcf_16 FAR*)src and not unsigned char FAR*)src +#endif //toch_maar_asm && !__DA_C__ + +#endif /* WCITST */ + +/************************************************************************************************/ +/******************************************** W S U *******************************************/ +/************************************************************************************************/ + +#if 0 //;? conflicts with WIN_CE _MSC_VER >= 1000 /* Microsoft Visual C ++ 4.x, 5.x */ + +// Note: +// Visual C++ 4.0 : _MSC_VER == 1000 +// Visual C++ 4.2 : _MSC_VER == 1020 + + +#pragma warning ( disable: 4001 ) + +#define HCF_STA //station characteristics + +//#if defined WVLAN_43 +//#define MSF_COMPONENT_ID COMP_ID_ODI_16 +//#else if defined WVLAN_42 +//#define MSF_COMPONENT_ID COMP_ID_PACKET +//#endif //WVLAN_xx + +#if !defined FAR +//#if _CONSOLE +//#define FAR // flat 32 bits mode (also defined in WINDEF.H) +//#define BASED +//#else +#define FAR // far is an obsolete key in Visual C++ 4.x +//#if defined _M_I86TM +//#define BASED __based(__segname("_CODE")) +//#else +#define BASED +//#endif // _M_I86TM +//#endif //_CONSOLE +#endif //!defined FAR + +typedef unsigned char hcf_8; +typedef unsigned short hcf_16; +typedef unsigned long hcf_32; + +#include +#include + +#define IN_PORT_BYTE(port) ((hcf_8)_inp( (hcf_io)(port) )) +#define IN_PORT_WORD(port) ((hcf_16)_inpw( (hcf_io)(port) )) +#define OUT_PORT_BYTE(port, value) ((void)_outp( (hcf_io)(port), value )) +#define OUT_PORT_WORD(port, value) ((void)_outpw( (hcf_io)(port), value )) + +#define toch_maar_geen_asm +#if defined(toch_maar_asm) +#define IN_PORT_STRING( port, dest, len) __asm \ +{ \ + __asm push di \ + __asm push es \ + __asm mov cx,len \ + __asm les di,dest \ + __asm mov dx,port \ + __asm rep insw \ + __asm pop es \ + __asm pop di \ +} + +#define OUT_PORT_STRING( port, src, len) __asm \ +{ \ + __asm push si \ + __asm push ds \ + __asm mov cx,len \ + __asm lds si,src \ + __asm mov dx,port \ + __asm rep outsw \ + __asm pop ds \ + __asm pop si \ +} + +#else //toch_maar_asm +#define IN_PORT_STRING( prt, dst, n) while ( n-- ) { *(hcf_16 FAR*)dst = IN_PORT_WORD( prt ); dst += 2; } +#define OUT_PORT_STRING( prt, src, n) while ( n-- ) { OUT_PORT_WORD( prt, *(hcf_16 FAR*)src ) ; src += 2; } +//;? WHY hcf_16 FAR*)src and not unsigned char FAR*)src +#endif //toch_maar_asm + +#endif /* _MSC_VER >= 1000 (Microsoft Visual C++ 4.0 ) */ + + + + +/************************************************************************************************/ +/****************************************** L I N U X *****************************************/ +/************************************************************************************************/ + +#if defined __linux__ + +#define HCF_STA //station characteristics +#define MSF_COMPONENT_ID COMP_ID_LINUX +#define MSF_COMPONENT_VAR 1 +#define MSF_COMPONENT_MAJOR_VER 0 +#define MSF_COMPONENT_MINOR_VER 4 + +#include + +#define FAR // flat 32-bits code +#define BASED + +typedef unsigned char hcf_8; +typedef unsigned short hcf_16; +typedef unsigned long hcf_32; + +#define IN_PORT_BYTE(port) ((hcf_8)inb((hcf_io)(port))) +#define IN_PORT_WORD(port) ((hcf_16)inw((hcf_io)(port))) +#define OUT_PORT_BYTE(port, value) outb((hcf_8)(value), (hcf_io)(port)) +#define OUT_PORT_WORD(port, value) outw((hcf_16)(value), (hcf_io)(port)) + +#define IN_PORT_STRING insw +#define OUT_PORT_STRING outsw + +#endif /* LINUX */ + + + +/************************************************************************************************/ +/********************************** S C O U N I X ********************************************/ +/************************************************************************************************/ + +#if 0 + +#define HCF_STA //station characteristics +#define MSF_COMPONENT_ID + +//#define CNV_LITTLE_TO_INT(x) // No endianess conversion needed + +#define FAR // flat 32-bits code +#define BASED + +typedef unsigned char hcf_8; +typedef unsigned short hcf_16; +typedef unsigned long hcf_32; + +#define IN_PORT_BYTE(port) ((hcf_8)inb( (hcf_io)(port) )) +#define IN_PORT_WORD(port) ((hcf_16)inw( (hcf_io)(port) )) +#define OUT_PORT_BYTE(port, value) (outb( (hcf_io)(port), (hcf_8) (value) )) +#define OUT_PORT_WORD(port, value) (outw( (hcf_io)(port), (hcf_16) (value) )) + +#define FAR // flat 32-bits code +#define BASED + + +#endif /* SCO UNIX */ + + +/************************************************************************************************/ +/********************************* M I N I P O R T ********************************************/ +/************************************************************************************************/ + +#if 0 + +#define MSF_COMPONENT_ID COMP_ID_MINIPORT +#define HCF_STA //station characteristics + +#include +#include + +#define MSF_COMPONENT_VAR 1 +#define MSF_COMPONENT_MAJOR_VER TPI_MAJOR_VERSION +#define MSF_COMPONENT_MINOR_VER TPI_MINOR_VERSION + + +//#define CNV_LITTLE_TO_INT(x) // No endianess conversion needed + +#if !defined FAR +#define FAR // flat 32-bits code +#endif //!defined FAR + +#define BASED + +__inline UCHAR NDIS_IN_BYTE( ULONG port ) +{ + UCHAR value; + NdisRawReadPortUchar(port , &value); + return (value); +} + +__inline USHORT NDIS_IN_WORD( ULONG port ) +{ + USHORT value; + NdisRawReadPortUshort(port , &value); + return (value); +} + +#define IN_PORT_BYTE(port) NDIS_IN_BYTE( (ULONG) (port) ) +#define IN_PORT_WORD(port) NDIS_IN_WORD( (ULONG) (port) ) +#define OUT_PORT_BYTE(port, value) NdisRawWritePortUchar( (ULONG) (port) , (UCHAR) (value)) +#define OUT_PORT_WORD(port, value) NdisRawWritePortUshort((ULONG) (port) , (USHORT) (value)) + +#define IN_PORT_STRING(port, addr, len) NdisRawReadPortBufferUshort(port, addr, (len)); +#define OUT_PORT_STRING(port, addr, len) NdisRawWritePortBufferUshort(port, addr, (len)); + +typedef UCHAR hcf_8; +typedef USHORT hcf_16; +typedef ULONG hcf_32; + +#endif /* MINIPORT */ + +/************************************************************************************************/ +/********************************* W A V E P O I N T ******************************************/ +/************************************************************************************************/ + +#if defined WVLAN_81 /* BORLANDC */ + +#define HCF_AP //access point characteristics +#define MSF_COMPONENT_ID COMP_ID_AP1 +#define MSF_COMPONENT_VAR 1 +#define MSF_COMPONENT_MAJOR_VER 3 +#define MSF_COMPONENT_MINOR_VER 34 + +#include + +//#define CNV_LITTLE_TO_INT(x) // No endianess conversion needed + +typedef unsigned char hcf_8; +typedef unsigned short hcf_16; +typedef unsigned long hcf_32; + +//#define HCF_ASSERT 0 /* debug build only */ + +#if !defined FAR +#define FAR far // segmented 16 bits mode +#endif //!defined FAR +#define BASED + + +#define IN_PORT_BYTE(port) (inportb( (hcf_io)(port) )) +#define IN_PORT_WORD(port) (inport( (hcf_io)(port) )) +#define OUT_PORT_BYTE(port, value) (outportb( (hcf_io)(port), value )) +#define OUT_PORT_WORD(port, value) (outport( (hcf_io)(port), value )) + +#define IN_PORT_STRING(port, addr, len) \ + asm { push di; push es; mov cx,len; les di,addr; mov dx,port; rep insw; pop es; pop di } + +#define OUT_PORT_STRING(port, addr, len) \ + asm { push si; push ds; mov cx,len; lds si,addr; mov dx,port; rep outsw; pop ds; pop si } + +#endif /* WavePoint */ + +/************************************************************************************************/ +/************************** MPC860 - Diab or High C 29K **************************************/ +/************************************************************************************************/ + +#if defined(__ppc) || defined(_AM29K) //|| (CPU == PPC860) + +#define HCF_AP //AccesPoint characteristics +#define MSF_COMPONENT_VAR 0 +#define MSF_COMPONENT_ID 0 +#define MSF_COMPONENT_MAJOR_VER 1 +#define MSF_COMPONENT_MINOR_VER 0 + +#define FAR // flat 32-bits code +#define BASED + +typedef unsigned char hcf_8; +typedef unsigned short hcf_16; +typedef unsigned long hcf_32; + +#define SwapBytes(t) /*lint -e572*/(((t) >> 8) + (((t) & 0xff) << 8))/*lint +e572*/ + +#if defined(__ppc) || (CPU == PPC860) + #ifndef __GNUC__ + #define __asm__ asm + #endif + + #if !defined(_lint) + #define EIEIO() __asm__(" eieio") + #else + #define EIEIO() + #endif + + hcf_8 IN_PORT_BYTE(int port) { + hcf_8 value = *(volatile hcf_8 *)(port); EIEIO(); + return value; + } + + hcf_16 IN_PORT_WORD(int port) { + hcf_16 value = *(volatile hcf_16 *)(port); EIEIO(); + value = SwapBytes(value); + return value; + } + + #define OUT_PORT_BYTE(port, value) { *(volatile hcf_8 *)(port) = (value); EIEIO(); } + #define OUT_PORT_WORD(port, value) \ + { *(volatile hcf_16 *)(port) = SwapBytes(value); EIEIO(); } +#else + #define IN_PORT_BYTE(port) (*(volatile hcf_8 *)(port)) + #define IN_PORT_WORD(port) (*(volatile hcf_16 *)(port)) + #define OUT_PORT_BYTE(port, value) (*(volatile hcf_8 *)(port) = (value)) + #define OUT_PORT_WORD(port, value) (*(volatile hcf_16 *)(port) = (value)) +#endif + +/***************************************************************************/ + +#define IN_PORT_STRING( port, dest, len) { \ + unsigned l = (len); \ + hcf_16 t, *d = (volatile hcf_16 *)(dest); \ + while (l--) { \ + t = IN_PORT_WORD(port); \ + *d++ = SwapBytes(t); \ + } \ + } + +#define OUT_PORT_STRING( port, src, len) { \ + unsigned l = (len); \ + hcf_16 t, *s = (volatile hcf_16 *)(src); \ + while (l--) { \ + t = *s++; \ + OUT_PORT_WORD(port, SwapBytes(t)); \ + } \ + } + +#if PRODUCT == 9150 + #define HCF_AP + #define HCF_ASSERT + #undef MSF_COMPONENT_ID +#endif + +#endif /* MPC860 - Diab or High C 29K */ + +/************************************************************************************************/ +/*********************************** M A C O S **********************************************/ +/************************************************************************************************/ + + /**********/ +#if 0 /* MAC_OS */ + /**********/ + +#define HCF_STA //station characteristics +#define MSF_COMPONENT_ID COMP_ID_MAC_OS +#define MSF_COMPONENT_VAR 0 +#define MSF_COMPONENT_MAJOR_VER 3 +#define MSF_COMPONENT_MINOR_VER 0 + +#define MAC_OS 1 +#define FAR // flat 32-bits code +#define BASED + +#undef HCF_LITTLE_ENDIAN // selects Little Endian (a.k.a. Intel), least significant byte first +#define HCF_BIG_ENDIAN // selects Big Endian (a.k.a. Motorola), most significant byte first + +#if defined(DEBUG) +#define HCF_ASSERT 1 +#endif // DEBUG + +typedef unsigned char hcf_8; +typedef unsigned short hcf_16; +typedef unsigned long hcf_32; + +#ifdef __cplusplus +extern "C" { +#endif +extern volatile unsigned char *MacIOaddr; +extern hcf_8 IN_PORT_BYTE(hcf_16 port); +extern void OUT_PORT_BYTE(hcf_16 port, hcf_8 value); +extern hcf_16 IN_PORT_WORD(hcf_16 port); +extern void OUT_PORT_WORD(hcf_16 port, hcf_16 value); +extern void IN_PORT_STRING(hcf_16 port, void *dest, hcf_16 len); +extern void OUT_PORT_STRING(hcf_16 port, void *src, hcf_16 len); + +#define SwapBytes(t) (((t) >> 8) + (((t) & 0xff) << 8)) + +#ifdef __cplusplus +} +#endif + +#endif /* MAC_OS */ + +/************************************************************************************************/ +/*********************************** W I N C E *************************************************/ +/************************************************************************************************/ + + /*********/ +#ifdef _WIN32_WCE /* WINCE */ + /*********/ + +#define MSF_COMPONENT_ID COMP_ID_WIN_CE +#define HCF_STA //station characteristics + +#include +#include +#include + +#define MSF_COMPONENT_VAR 1 +#define MSF_COMPONENT_MAJOR_VER TPI_MAJOR_VERSION +#define MSF_COMPONENT_MINOR_VER TPI_MINOR_VERSION + +#define BASED + +#undef HCF_LITTLE_ENDIAN // selects Little Endian (a.k.a. Intel), least significant byte first +#undef HCF_BIG_ENDIAN // selects Big Endian (a.k.a. Motorola), most significant byte first + +#if defined(_SH3_) || defined (_SHx_) || defined(_MIPS_) || defined(_X86_) +#define HCF_LITTLE_ENDIAN +#endif + +#if defined(DEBUG) || defined(_WIN32_WCE_DEBUG) +#define HCF_ASSERT 1 +#endif // DEBUG + +typedef UCHAR hcf_8; +typedef USHORT hcf_16; +typedef ULONG hcf_32; + +#ifdef __cplusplus +extern "C" { +#endif + +#define WCE_IO_OFFSET 0x40 +extern ULONG WceIoAddr; + +extern hcf_8 IN_PORT_BYTE(hcf_16 port); +extern void OUT_PORT_BYTE(hcf_16 port, hcf_8 value); +extern hcf_16 IN_PORT_WORD(hcf_16 port); +extern void OUT_PORT_WORD(hcf_16 port, hcf_16 value); +extern void IN_PORT_STRING(hcf_16 port, void *dest, hcf_16 len); +extern void OUT_PORT_STRING(hcf_16 port, void *src, hcf_16 len); + +#ifdef __cplusplus +} +#endif + +#endif /* _WIN32_WCE */ + + +/* * * * * * * * * * * * * * * * * * * * * * * IV * * * * * * * * * * * * * * * * * * * * * * */ + +/***************************************Compiler specific ****************************************/ + +#ifdef __cplusplus +#define EXTERN_C extern "C" +#else +#define EXTERN_C +#endif //__cplusplus + + +/************************************************************************************************/ +/********** M A C R O S derived of C O M P I L E R S P E C I F I C M A C R O S *************/ +/************************************************************************************************/ + +typedef hcf_8 FAR *wci_bufp; // segmented 16-bits or flat 32-bits pointer to 8 bits unit +typedef hcf_16 FAR *wci_recordp; // segmented 16-bits or flat 32-bits pointer to 16 bits unit + +typedef hcf_8 FAR *hcf_8fp; // segmented 16-bits or flat 32-bits pointer to 8 bits unit +typedef hcf_16 FAR *hcf_16fp; // segmented 16-bits or flat 32-bits pointer to 16 bits unit +typedef hcf_32 FAR *hcf_32fp; // segmented 16-bits or flat 32-bits pointer to 8 bits unit + + +#if defined HCF_STA && defined HCF_AP +#if defined WCITST +#pragma message( "you should also test both in isolation" ) +#else +error; define exactly one of these terms; +#endif +#endif +#if ! defined HCF_STA && ! defined HCF_AP +error; define exactly one of these terms; +#endif + +/* * * * * * * * * * * * * * * * * * * * * * * V * * * * * * * * * * * * * * * * * * * * * * */ + +#if defined HCF_PORT_IO +#if defined HCF_MEM_IO +error; +#else +typedef hcf_16 hcf_io; +#endif //HCF_MEM_IO +#endif //HCF_PORT_IO +#if defined HCF_MEM_IO +#if defined HCF_PORT_IO +error; +#else +typedef hcf_32 hcf_io; +#endif //HCF_PORT_IO +#endif //HCF_MEM_IO + + + +/* MSF_COMPONENT_ID is used to define the CFG_IDENTITY_STRCT in HCF.C + * CFG_IDENTITY_STRCT is defined in HCF.C purely based on convenience arguments + * The HCF can not have the knowledge to determine the ComponentId field of the + * Identity record (aka as Version Record), therefore the MSF part of the Drivers + * must supply this value via the System Constant MSF_COMPONENT_ID + * There is a set of values predefined in MDD.H (format COMP_ID_.....) + */ + +#if defined MSF_COMPONENT_ID +#define DUI_COMPAT_VAR MSF_COMPONENT_ID +#define DUI_COMPAT_BOT 4 +#define DUI_COMPAT_TOP 4 +#endif // MSF_COMPONENT_ID + +/************************************************************************************************/ +/*************** E N V I R O N M E N T S P E C I F I C M A C R O S ************************/ +/************************************************************************************************/ + + +#if defined HCF_ASSERT //the next line may need modification for a specific environment +extern int BASED HCF_VAR_0; //Revision 2.0 gives an explanation of the need for HCF_VAR_0 +#endif + +#if defined HCF_PROFILING +#endif + +/************************************************************************************************/ +/****** M S F S U P P O R T F U N C T I O N S P R O T O T Y P E S *******************/ +/************************************************************************************************/ + +EXTERN_C void msf_assert ( wci_bufp file_namep, unsigned int line_number, hcf_16 trace, int qual ); + +/* To increase portability, use unsigned char and unsigned char * when accessing parts of larger + * types to convert their Endianess + */ + +#if defined HCF_BIG_ENDIAN +#if defined HCF_LITTLE_ENDIAN + error +#else //************************************* B I G E N D I A N ******************************************* +#define CNV_LITTLE_TO_INT(w) ( ((hcf_16)(w) & 0x00ff) << 8 | ((hcf_16)(w) & 0xff00) >> 8 ) +#define CNV_BIG_TO_INT(w) (w) // No endianess conversion needed + +#define CNV_INT_TO_BIG_NP(addr) +#define CNV_LITTLE_TO_INT_NP(addr) { \ + hcf_8 temp; \ + temp = ((hcf_8 FAR *)(addr))[0]; \ + ((hcf_8 FAR *)(addr))[0] = ((hcf_8 FAR *)(addr))[1]; \ + ((hcf_8 FAR *)(addr))[1] = temp; \ +} + +#endif // HCF_LITTLE_ENDIAN +#endif // HCF_BIG_ENDIAN + +#if defined HCF_LITTLE_ENDIAN +#if defined HCF_BIG_ENDIAN + error; +#else //************************************* L I T T L E E N D I A N ************************************* + +#define CNV_LITTLE_TO_INT(w) (w) // No endianess conversion needed +#define CNV_BIG_TO_INT(w) ( ((hcf_16)(w) & 0x00ff) << 8 | ((hcf_16)(w) & 0xff00) >> 8 ) + +#define CNV_INT_TO_BIG_NP(addr) { \ + hcf_8 temp; \ + temp = ((hcf_8 FAR *)(addr))[0]; \ + ((hcf_8 FAR *)(addr))[0] = ((hcf_8 FAR *)(addr))[1]; \ + ((hcf_8 FAR *)(addr))[1] = temp; \ +} +#define CNV_LITTLE_TO_INT_NP(addr) + +#endif // HCF_BIG_ENDIAN +#endif // HCF_LITTLE_ENDIAN + +// conversion macros which can be expressed in other macros +#define CNV_INT_TO_LITTLE(w) CNV_LITTLE_TO_INT(w) +#define CNV_INT_TO_BIG(w) CNV_BIG_TO_INT(w) + +#endif //HCFCFG_H + +//******************************************* A L I G N M E N T ********************************************** +#if defined HCF_ALIGN +#if HCF_ALIGN != 0 && HCF_ALIGN != 2 + error; +#endif // HCF_ALIGN != 0 && HCF_ALIGN != 2 +#else +#define HCF_ALIGN 0 //default to no alignment +#endif // HCF_ALIGN + + + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * The routines ips and ops (short for InPutString and OutPutString) are created to use the + * compiler to do the type checking. It turned out that it is too easy to accidentally pass + * a word pointer to the the macros IN_PORT_STRING and OUT_PORT_STRING rather than a byte pointer. + * The "+2" as some macro implementations use, does not have the intended effect in those cases. + * The HCF_STRICT business can be ignored by MSF programmers. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + +#if defined HCF_STRICT +void ips( hcf_io prt, wci_bufp dst, int n); +void ops( hcf_io prt, wci_bufp src, int n); +#endif //HCF_STRICT + + + + + + + +#if !defined HCF_MAX_CONFIG +#define HCF_MAX_CONFIG 256 // maximum accumulated size in hcf_16 of LTV records used in hcf_put_config +#endif + +#if !defined HCF_MAX_MSG +#define HCF_MAX_MSG 2304 /* WaveLAN Pakket Size */ +#endif + +#if !defined HCF_MAX_NOTIFY +#define HCF_MAX_NOTIFY 6 // maximum size in bytes of "real" data in Notify command +#endif diff -urN orig/drivers/net/pcmcia/wvlan_hcfdef.h linux/drivers/net/pcmcia/wvlan_hcfdef.h --- orig/drivers/net/pcmcia/wvlan_hcfdef.h Thu Jan 1 01:00:00 1970 +++ linux/drivers/net/pcmcia/wvlan_hcfdef.h Wed Oct 9 19:03:09 2002 @@ -0,0 +1,375 @@ +/* This file is part of the Hardware Control Functions Light (HCF-light) library + to control the Lucent Technologies WaveLAN/IEEE Network I/F Card. + The HCF is the implementation of the Wireless Connection I/F (WCI). + + The HCF-light files are a subset of the HCF files. The complete set offers a + number of additional facilities, e.g. firmware download, Etherner-II encapsulation, + additional diagnostic facilities, ASSERT logic to support debugging, 802.11 support, + Configuration Management. + This complete set is explicitely not in the Public Domain but can be made + available under certain restriction. (see the pointer below for support) + + The HCF-light files are 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. + + At the time of this writing, you can request for support at: + betasupport@wavelan.com + + Documentation is expected to be available in the week of 8 Februari 1999 + +*/ + + +#ifndef HCFDEFC_H +#define HCFDEFC_H 1 + + +/************************************************************************************************* +* +* FILE : HCFDEFC.H *************** 2.0 ********************* +* +* DATE : 2000/01/06 23:30:53 1.2 +* +* AUTHOR : Nico Valster +* +* DESC : Definitions and Prototypes for HCF only +* +************************************************************************************************** +* COPYRIGHT (c) 1996, 1997, 1998 by Lucent Technologies. All Rights Reserved. +*************************************************************************************************/ + + +/**************************************************************************** +wvlan_hcfdef.h,v +Revision 1.2 2000/01/06 23:30:53 root +*** empty log message *** + + * + * Rev 1.0 02 Feb 1999 14:32:34 NVALST + * Initial revision. +Revision 1.1 1999/01/30 19:24:39 nico +Initial revision + +Revision 1.1 1999/01/30 19:07:57 nico +Initial revision + + * + * Rev 1.110 29 Jan 1999 15:52:44 NVALST + * intermediate, maybe working but seems to need two times to load in + * light-version + * + * Rev 2.12 29 Jan 1999 10:48:44 NVALST + * + * Rev 1.108 28 Jan 1999 14:43:24 NVALST + * intermediate, once more correction of loop in hcf_service_nic + download + * passed to Marc + * + * Rev 2.11 27 Jan 1999 16:57:42 NVALST + * + * Rev 1.107 27 Jan 1999 13:53:24 NVALST + * intermediate, once more correction of loop in hcf_service_nic + * + * Rev 1.106 26 Jan 1999 16:42:46 NVALST + * intermediate, corrected loop in hcf_service_nic (which was as result of a + * walkthrough, changed from a bug without consequences into one with consequences + * + * Rev 1.105 25 Jan 1999 14:24:46 NVALST + * intermediate, hopefully suitable for release + * + * Rev 1.104 22 Jan 1999 16:59:34 NVALST + * intermediate, minor corrections + some HCF-L stuff + * + * Rev 1.103 15 Jan 1999 15:14:46 NVALST + * intermediate, deposited as HCF2.10 + * + * Rev 2.10 15 Jan 1999 14:54:34 NVALST + * +****************************************************************************/ + + +/**************************************************************************** +* +* CHANGE HISTORY +* + 961018 - NV + Original Entry + +**************************************************************************************************/ + +/************************************************************************************************/ +/********************************* P R E F I X E S ********************************************/ +/************************************************************************************************/ +//IFB_ Interface Block +//HCMD_ Hermes Command +//HFS_ Hermes (Transmit/Receive) Frame Structure +//HREG_ Hermes Register + +/*************************************************************************************************/ + + +/************************************************************************************************/ +/********************************* GENERAL EQUATES **********************************************/ +/************************************************************************************************/ +//#define STATIC //;?cheap way out to get things compiled while intransition for ObjectOutline +//#if ! defined STATIC //;? change to HCF_STATIC some day +//#if defined _DEBUG || defined OOL +#define STATIC EXTERN_C +//#else +//#define STATIC static +//#endif //_DEBUG +//#endif // STATIC + + +#define AUX_MAGIC_0 0xFE01 +#define AUX_MAGIC_1 0xDC23 +#define AUX_MAGIC_2 0xBA45 +#define HCF_MAGIC 0x7D37 // "}7" Handle validation +#define DIAG_MAGIC 0x5A5A + +#define PLUG_DATA_OFFSET 0x3F0000L + + +#define ONE_SECOND 977 // 977 times a Hermes Timer Tick of 1K microseconds ~ 1 second +#define INI_TICK_INI 0x20000L + +#define IO_IN 0 //hcfio_in_string +#define IO_OUT 1 //hcfio_out_string +#define IO_OUT_CHECK 2 //enable Data Corruption Detect on hcfio_out_string + +#define CARD_STAT_ENA_PRES (CARD_STAT_ENABLED|CARD_STAT_PRESENT) +#define CARD_STAT_PRI_PRES (CARD_STAT_PRESENT|CARD_STAT_INCOMP_PRI) +#define CARD_STAT_PRI_STA_PRES (CARD_STAT_PRI_PRES|CARD_STAT_INCOMP_STA) + +#define DO_ASSERT ( ifbp->IFB_Magic != HCF_MAGIC && ifbp->IFB_Magic == HCF_MAGIC ) //FALSE without the nasty compiler warning + +#define HCF_ASSERT_ACTION 0x0001 +//#define HCF_ASSERT_CONNECT no use to trace this +#define HCF_ASSERT_DISABLE 0x0002 +#define HCF_ASSERT_DISCONNECT 0x0004 +#define HCF_ASSERT_ENABLE 0x0008 +#define HCF_ASSERT_GET_DATA 0x0010 +#define HCF_ASSERT_GET_INFO 0x0020 +#define HCF_ASSERT_INITIALIZE 0x0040 +#define HCF_ASSERT_RESEND 0x0080 +#define HCF_ASSERT_SERVICE_NIC 0x0100 +#define HCF_ASSERT_PUT_DATA 0x0200 +#define HCF_ASSERT_PUT_INFO 0x0400 +#define HCF_ASSERT_PUT_HDR 0x0800 +#define HCF_ASSERT_SEND 0x1000 +#define HCF_ASSERT_SEND_DIAG_MSG 0x2000 +#define HCF_ASSERT_INT_OFF 0x4000 +#define HCF_ASSERT_MISC 0x8000 + + +#define CFG_CONFIG_RID_MASK 0xFC00 //CONFIGURATION RECORDS + +#define BAP_0 HREG_DATA_0 //Tx-related register set for WMAC buffer access +#define BAP_1 HREG_DATA_1 //non Tx-related register set for WMAC buffer access +/************************************************************************************************/ +/***************************** STRUCTURES *******************************************************/ +/************************************************************************************************/ + + +//************************* Hermes Receive/Transmit Frame Structures +//HFS_STAT +//see MMD.H for HFS_STAT_ERR +#define HFS_STAT_MSG_TYPE 0xE000 //Hermes reported Message Type +#define HFS_STAT_1042 0x2000 //RFC1042 Encoded +#define HFS_STAT_TUNNEL 0x4000 //Bridge-Tunnel Encoded +#define HFS_STAT_WMP_MSG 0x6000 //WaveLAN-II Management Protocol Frame + +//HFS_TX_CNTL +//see ENC_802_3/ENC_802_11 definition + + +//************************* Hermes Register Offsets and Command bits +#define HREG_IO_RANGE 0x40 //I/O Range used by Hermes + + +//************************* Command/Status +#define HREG_CMD 0x00 // +#define HCMD_CMD_CODE 0x3F +#define HREG_PARAM_0 0x02 // +#define HREG_PARAM_1 0x04 // +#define HREG_PARAM_2 0x06 // +#define HREG_STAT 0x08 // +#define HREG_STAT_CMD_CODE 0x003F // +#define HREG_STAT_DIAG_ERR 0x0100 +#define HREG_STAT_INQUIRE_ERR 0x0500 +#define HREG_STAT_CMD_RESULT 0x7F00 // +#define HREG_RESP_0 0x0A // +#define HREG_RESP_1 0x0C // +#define HREG_RESP_2 0x0E // + + +//************************* FID Management +#define HREG_INFO_FID 0x10 // +#define HREG_RX_FID 0x20 // +#define HREG_ALLOC_FID 0x22 // +//rsrvd #define HREG_TX_COMPL_FID 0x24 // + + +//************************* BAP +#define HREG_SELECT_0 0x18 // +#define HREG_OFFSET_0 0x1C // +//#define HREG_OFFSET_BUSY 0x8000 // use HCMD_BUSY +#define HREG_OFFSET_ERR 0x4000 // +//rsrvd #define HREG_OFFSET_DATA_OFFSET 0x0FFF // + +#define HREG_DATA_0 0x36 // +//rsrvd #define HREG_SELECT_1 0x1A // +//rsrvd #define HREG_OFFSET_1 0x1E // + +#define HREG_DATA_1 0x38 // + + +//************************* Event +#define HREG_EV_STAT 0x30 // +#define HREG_INT_EN 0x32 // +#define HREG_EV_ACK 0x34 // + + +//************************* Host Software +#define HREG_SW_0 0x28 // +#define HREG_SW_1 0x2A // +#define HREG_SW_2 0x2C // +//rsrvd #define HREG_SW_3 0x2E // +//************************* Control and Auxiliary Port + +#define HREG_CNTL 0x14 // +#define HREG_CNTL_AUX_ENA 0xC000 +#define HREG_CNTL_AUX_ENA_STAT 0xC000 +#define HREG_CNTL_AUX_DIS_STAT 0x0000 +#define HREG_CNTL_AUX_ENA_CNTL 0x8000 +#define HREG_CNTL_AUX_DIS_CNTL 0x4000 +#define HREG_AUX_PAGE 0x3A // +#define HREG_AUX_OFFSET 0x3C // +#define HREG_AUX_DATA 0x3E // + + +/************************************************************************************************/ +/***************************** END OF STRUCTURES ***********************************************/ +/************************************************************************************************/ + + +/************************************************************************************************/ +/********************************** EQUATES ***************************************************/ +/************************************************************************************************/ + +// Tx/Rx frame Structure +// +#define HFS_STAT_ABS (0x2E + HFS_STAT) //0x0000 +#define HFS_Q_INFO_ABS (0x2E + HFS_Q_INFO) //0x0006 +#define HFS_TX_CNTL_ABS (0x2E + HFS_TX_CNTL) //0x000C +#define HFS_FRAME_CNTL_ABS (0x2E + HFS_FRAME_CNTL) //0X000E +#define HFS_ID_ABS (0x2E + HFS_ID) //0X0010 + +#define HFS_ADDR_1_ABS (0x12 + HFS_ADDR_1) //0x0012 +#define HFS_ADDR_2_ABS (0x12 + HFS_ADDR_2) //0x0018 +#define HFS_ADDR_3_ABS (0x12 + HFS_ADDR_3) //0x001E +#define HFS_SEQ_CNTL_ABS (0x12 + HFS_SEQ_CNTL) //0x0024 +#define HFS_ADDR_4_ABS (0x12 + HFS_ADDR_4) //0x0026 +#define HFS_DAT_LEN_ABS (0x12 + HFS_DAT_LEN) //0x002C + +#define HFS_ADDR_DEST_ABS (0x2E + HFS_ADDR_DEST) //0x002E +#define HFS_ADDR_SRC_ABS (0x2E + HFS_ADDR_SRC) //0x0034 +#define HFS_LEN_ABS (0x2E + HFS_LEN) //0x003A +#define HFS_DAT_ABS (0x2E + HFS_DAT) //0x003C +#define HFS_TYPE_ABS (0x2E + HFS_TYPE) //0x0042 Eternet-II type in 1042/Bridge-Tunnel encapsulated frame + +#define HFS_802_11_GAP (HFS_DAT_ABS - HFS_ADDR_DEST_ABS) +#define HFS_E_II_GAP (HFS_TYPE_ABS - HFS_LEN_ABS) + +#define KLUDGE_MARGIN 8 //safety margin for Tx Data Corruption workaround + +#define HFS_TX_ALLOC_SIZE HCF_MAX_MSG + HFS_DAT_ABS + KLUDGE_MARGIN + +// IFB field related +// IFB_TxFrameType +//#define ENC_TX_802_3 0x00 +//#define ENC_TX_802_11 0x11 +#define ENC_TX_E_II 0x0E //encapsulation flag + +// SNAP header for E-II Encapsulation +#define ENC_TX_1042 0x00 +#define ENC_TX_TUNNEL 0xF8 + +// Hermes Command Codes and Qualifier bits +#define HCMD_BUSY 0x8000 // Busy bit, applicable for all commands +#define HCMD_RECL 0x0100 // Reclaim bit, applicable for Tx and Inquire + +#define HCMD_INI 0x0000 // +#define HCMD_ENABLE 0x0001 // +#define HCMD_DISABLE 0x0002 // +#define HCMD_DIAG 0x0003 // +#define HCMD_ALLOC 0x000A // +#define HCMD_TX 0x000B // +#define HCMD_NOTIFY 0x0010 // +#define HCMD_INQUIRE 0x0011 // +#define HCMD_ACCESS 0x0021 // +#define HCMD_ACCESS_WRITE 0x0100 // +#define HCMD_PROGRAM 0x0022 // +#define HCMD_PROGRAM_DISABLE 0x0000 // +#define HCMD_PROGRAM_ENABLE_VOLATILE 0x0100 // +#define HCMD_PROGRAM_ENABLE_NON_VOLATILE 0x0200 // +#define HCMD_PROGRAM_NON_VOLATILE 0x0300 // + +// Miscellanuos +// +#define REV_OFFSET 16 // offset of Major version within the PVCS generated Version String + + +/************************************************************************************************/ +/********************************** END OF EQUATES ********************************************/ +/************************************************************************************************/ + + +/************************************************************************************************/ +/************************************** MACROS ************************************************/ +/************************************************************************************************/ + +/************************************************************************************************ + DEBUG_INT is an undocumented feature to assist the HCF debugger + By expanding INT_3 to either an "int 3" or a NOP, it is very easy to check + by means of a binary file compare whether a "debug" version really corresponds + with a "non-debug" version. + ;? is is currently unknown whether there is a real reason to restrict this + implemenation to the MSVC environment +*/ +#if defined (_MSC_VER) +#ifdef DEBUG_INT +//#pragma message( Reminder "int 3, should be removed before releasing" ) +#define INT_3 __asm int 3 +#else +#define INT_3 __asm nop +#endif /*DEBUG_INT*/ +#else +#define INT_3 +#endif /*_MSC_VER*/ + +#define MUL_BY_2( x ) ( (x) << 1 ) //used to multiply by 2 +#define DIV_BY_2( x ) ( (x) >> 1 ) //used to divide by 2 + +/************************************************************************************************/ +/************************************** END OF MACROS *****************************************/ +/************************************************************************************************/ + +/************************************************************************************************/ +/*************************************** PROTOTYPES *******************************************/ +/************************************************************************************************/ + +int hcfio_string( IFBP ifbp, int bap, int fid, int offset, wci_bufp pc_addr, int wlen, int blen, int type ); + + +#endif //HCFDEFC_H diff -urN orig/drivers/net/pcmcia/wvlan_hcfio.c linux/drivers/net/pcmcia/wvlan_hcfio.c --- orig/drivers/net/pcmcia/wvlan_hcfio.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/net/pcmcia/wvlan_hcfio.c Wed Oct 9 19:03:09 2002 @@ -0,0 +1,432 @@ +/* This file is part of the Hardware Control Functions Light (HCF-light) library + to control the Lucent Technologies WaveLAN/IEEE Network I/F Card. + The HCF is the implementation of the Wireless Connection I/F (WCI). + + The HCF-light files are a subset of the HCF files. The complete set offers a + number of additional facilities, e.g. firmware download, Etherner-II encapsulation, + additional diagnostic facilities, ASSERT logic to support debugging, 802.11 support, + Configuration Management. + This complete set is explicitely not in the Public Domain but can be made + available under certain restriction. (see the pointer below for support) + + The HCF-light files are 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. + + At the time of this writing, you can request for support at: + betasupport@wavelan.com + + Documentation is expected to be available in the week of 8 Februari 1999 + +*/ + + + +/************************************************************************************************************** +* +* FILE : hcfio.cpp +* +* DATE : 2000/01/06 23:30:53 1.2 +* +* AUTHOR : Nico Valster +* +* DESC : WCI-II HCF I/O Support Routines +* These routines are isolated in their own *.CPP file to facilitate porting +* +* Customizable via HCFCFG.H which is included by HCF.H +* +*************************************************************************************************************** +* COPYRIGHT (c) 1994, 1995 by AT&T. All Rights Reserved. +* COPYRIGHT (c) 1996, 1997, 1998 by Lucent Technologies. All Rights Reserved. +**************************************************************************************************************/ + +/**************************************************************************** +wvlan_hcfio.c,v +Revision 1.2 2000/01/06 23:30:53 root +*** empty log message *** + + * + * Rev 1.0 02 Feb 1999 14:32:30 NVALST + * Initial revision. +Revision 1.1 1999/01/30 19:34:40 nico +Initial revision + +Revision 1.1 1999/01/30 19:24:39 nico +Initial revision + +Revision 1.1 1999/01/30 19:07:33 nico +Initial revision + + * + * Rev 1.110 29 Jan 1999 15:52:40 NVALST + * intermediate, maybe working but seems to need two times to load in + * light-version + * + * Rev 2.12 29 Jan 1999 10:48:46 NVALST + * + * Rev 1.108 28 Jan 1999 14:43:18 NVALST + * intermediate, once more correction of loop in hcf_service_nic + download + * passed to Marc + * + * Rev 2.11 27 Jan 1999 16:57:42 NVALST + * + * Rev 1.107 27 Jan 1999 13:53:22 NVALST + * intermediate, once more correction of loop in hcf_service_nic + * + * Rev 1.106 26 Jan 1999 16:42:44 NVALST + * intermediate, corrected loop in hcf_service_nic (which was as result of a + * walkthrough, changed from a bug without consequences into one with consequences + * + * Rev 1.105 25 Jan 1999 14:24:46 NVALST + * intermediate, hopefully suitable for release + * + * Rev 1.104 22 Jan 1999 16:59:30 NVALST + * intermediate, minor corrections + some HCF-L stuff + * + * Rev 1.103 15 Jan 1999 15:14:40 NVALST + * intermediate, deposited as HCF2.10 + * +****************************************************************************/ + + +/**************************************************************************** +* +* CHANGE HISTORY +* + 961121 - NV + Original Entry + +**************************************************************************************************************/ + +/* ToDo + * the CNV_LITTLE_TO_INT does have the desired effect on all platforms, but it's naming is + * misleading, so revisit all these CNV macros to assure the right name is used at the right + * place. Hint: introduce CNV_HOST_TO_NETWORK names if appropriate + */ + + +#include "wvlan_hcf.h" +#include "wvlan_hcfdef.h" + +#ifdef HCF_ASSERT +static char BASED HCF__FILE__[] = { " " __FILE__}; /* 6 spaces to supply room to build an LTV record for + * runtime HCF_ASSERTs. This record is constructed as: + * - L: self explanatory + * - T: CFG_MB_ASSERT + * - V[0]: line_number + * - V[1..]: (unchanged) file name */ +#endif + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * Refer to HCFCFG.H for more information on the routines ips and ops (short for InPutString + * and OutPutString) + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#if defined HCF_STRICT +void ips( hcf_io prt, wci_bufp dst, int n) { + + while ( n-- ) { + *(hcf_16 FAR*)dst = IN_PORT_WORD( prt ); + dst += 2; + } +} // ips + +void ops( hcf_io prt, wci_bufp src, int n) { + + while ( n-- ) { + OUT_PORT_WORD( prt, *(hcf_16 FAR*)src ); + src += 2; + } +} // ops +#endif // HCF_STRICT + +/***************************************** DOCZ Header ******************************************************** + + +.MODULE hcfio_string +.LIBRARY HCF_SUP +.TYPE function +.SYSTEM msdos +.SYSTEM unix +.SYSTEM NW4 +.APPLICATION I/O Support for HCF routines +.DESCRIPTION read/write string with specified length from/to WaveLAN NIC RAM to/from PC RAM + + +int hcfio_string( IFBP ifbp, int bap, int fid, + int offset, wci_bufp pc_addr, int word_len, int tot_len, int type ) { +.ARGUMENTS + IFBP ifbp I/F Block + int bap BAP0/1 + int fid FID/RID + int offset offset in FID/RID + wci_bufp pc_addr begin address in PC RAM to write to or read from + int word_len number of leading words of which the Endianess must be converted + int tot_len number of bytes to write or read + int type action code + - IO_IN read from NIC RAM + - IO_OUT write to NIC RAM + - IO_OUT_CHECK Data Corruption Detect + +.RETURNS + int + HCF_SUCCESS O.K + HCF_ERR_TIME_OUT BAP can not be initialized + HCF_ERR_NO_NIC card is removed + HCF_FAILURE Data Corruption Detection catched + +.NARRATIVE + + hcfio_string has the following tasks: + - copy data from NIC RAM to Host RAM or vice versa + - optionally convert the data or part of the data from/to Little Endian format (as used by the NIC) + to/from the Native Endian format (as used by the Host) + - check for Data Corruption in the data written to the NIC + + Data is a string with specified length copied from/to a specified offset in a specified Receive Frame + Structure (FID), Transmit Frame Structure (FID) or Record (RID) to/from a Host RAM buffer with a specified + begin address. + A length of 0 can be specified, resulting in no data transfer. This feature accomodates MSFs in certain + Host environments (i.e. ODI) and it is used in the Data Corruption detection. + Which Buffer Acces Path (BAP0 or BAP1) is used, is defined by a parameter. + A non-zero return status indicates: + - the selected BAP could not properly be initialized + - the card is removed before completion of the data transfer + - the Data Corruption Detection triggered + - the NIC is considered inoperational due to a time-out of some Hermes activity in the past + In all other cases, a zero is returned. + If a card removal is returned, the MSF has the option to drop the message or recover in any other way it + sees fit. + BAP Initialization failure indicates an H/W error which is very likely to signal complete H/W failure. Once + a BAP Initialization failure has occured all subsequent interactions with the Hermes will return a time out + status till the Hermes is re-initialized by means of an hcf_disable (at all ports in case of a multi-port + environment) + +.DIAGRAM + + 1: the test on rc checks whether a BAP initialization or a call to cmd_wait did ever fail. If so, the Hermes + is assumed inoperable/defect, and all subsequent bap_ini/cmd_wait calls are nullified till hcf_disable + clears the IFB_TimStat field. + 2: The PCMCIA card can be removed in the middle of the transfer. By depositing a "magic number" in the + HREG_SW_0 register of the Hermes at initialization time and by verifying this location after + reading the string, it can be determined whether the card is still present and the return status is + set accordingly. + 3: The test on offset and fid in the IFB_BAP_ structure corresponding with the BAP entry parameter, + assures that the BAP is only initialized if the current set of parameters specifies a location wich is + not consecutive with the last read/write access. If initialization is needed, then: + - the select register is set + - the offset register is set + - the IFB_BAP_ structure is initialized + - the offset register is monitored till a successful condition (no busy bit and no error bit) is + detected or till the protection counter (calibrated at approx 1 second) expires + If the counter expires, this is reflected in IFB_TimStat, so all subsequent calls to hcfio_string + fail immediately ( see step 1) + 4: the offset register in the IFB_BAP_ structure is updated to be used as described in item 3 above + on the next call +10: The NIC I/F is optimized for word transfer but it can only handle word transfer at a word boundary. + Therefore an additional test must be done to handle the read preparation in case the begin address in + NIC RAM is odd. + This situation is handled by first reading a single byte and secondly reading a string of WORDS with a + BYTE length of the requested length minus 1. + NOTE: MACRO IN_PORT_STRING possibly modifies p (depending on how the MSF-programmer chooses to construct + this macro, so pc_addr can not be used as parameter +11: At completion of the word based transfer, a test is made to determine whether 1 additional byte must be + read (odd length starting at even address or even length starting at odd boundary) +12: finally the optionally conversion of the first words from Little Endian to Native Endian format is done. +20: first the optionally conversion of the first words from Native Endian to Little Endian format is done. + This implies that Endian conversion can ONLY take place at word boundaries. + Note that the difference with the IO_IN part of the logic is based on optimization considerations (both + speed and size) and the boundary condition to return the output buffer unchanged to the caller + Note also that the check on zero-length for output can not be the simple "skip all" as used for input, + because the Data Corruption Detection needs some of the side effects of this code, specifically the + BAP initialization +21: As for the IO_IN part, the logic must first cope with odd begin addresses in NIC RAM and the bulk of the + transfer is done via OUT_PORT_STRING. Due to a flaw in the Hermes, writing the high byte corrupts the + low byte. As a work around, the HCF reads the low byte deposited in NIC RAM by the previous + hcfio_string, merges that byte with the first byte of the current Host RAM buffer into a word and + writes that word to NIC RAM via OUT_PORT_WORD. Since OUT_PORT_WORD converts from Native Endian to + Little Endian, while at this point of the procedure the Host buffer must have the correct Endianess, + the macro CNV_LITTLE_TO_INT counteracts this unwanted adjustment of OUT_PORT_WORD. +22: At completion of the word based transfer, a test is made to determine whether 1 additional byte must be + written +30: The case of Data Corruption Detection: + First the NIC RAM pointer is aligned on a word boundary to cope with the problem of an odd number of + bytes written before. This is done by skipping one additional byte before the Data Corruption + Detection Pattern is appended to the data already written to the NIC RAM. + Then 8 bytes fixed pattern is written. The justification of this is given in the NOTICE section below +31: In order to read the pattern back, the BAP must be initialized to address the begin of the pattern. + The select register does not change, so only the offset register needs to be written, followed by + a wait for successful completion. +40: To recognize the case that the NIC is removed during execution of hcfio_string, the same check as in + step 2 is done again. +99: In the past, one of the asserts in bap_ini (which no longer exists now it is assimilated in hcfio_string) + catched if "offset" was invalid. By calling bap_ini with the original (offset + length), bap_ini would + catch when the MSF passes the RID/FID boundary during the read process. It turned out that this feature + did obscure the tracing during debugging so much that its total effect on the debugging process was + considered detrimental, however re-institution can be considered depending on the bug you are chasing + + +.NOTICE +The problem is that without known cause, the Hermes internal NIC RAM pointer misses its auto-increment causing +two successive writes to NIC RAM to address the same location. As a consequence word is overwritten and +a word is written at position . Since the Hermes is unaware of this, nothing in the system is +going to catch this, e.g. the frame is received on the other side with correct FCS. As a workaround, the HCF +keeps track of where the NIC RAM pointer SHOULD be. After all hcf_put_data calls are done, in other words, +when hcf_send is called, the HCF writes a number of words - the kludge pattern - after the MSF-data. Then it +sets the NIC RAM pointer by means of a re-initialization of the BAP to where this kludge pattern SHOULD be and +reads the first word. This first word must match the kludge pattern, otherwise, apparently, the auto-increment +failed. We need to write more than 1 word otherwise if the previous use of that piece of NIC RAM would have +left by chance the right "kludge" value just after the newly but incorrectly put data, the test would not +trigger. By overwriting the next 3 words as well, we assume the likelihood of this to be sufficiently small. +The frequency observed of this problem varies from 1 out of 1000 frames till to low to be noticeable. Just to +be on the safe side we assume that the chance of an error is 10E-3. By writing 4 words we reduce the +likelihood of an unnoticed problem to 10E-12, but of course this becomes tricky because we don't know whether +the chances are independent. Note that the HCF notion of the NIC RAM pointer is a "logical" view in the +RID/FID address space, while the Hermes has a completely different physical address value for this pointer, +however that difference does not influence above reasoning. + +.NOTE + Depending on the selected optimization options, using "register int reg" causes some obscure + optimization with si. As a result the code is longer. Therefore, do not invest time to optimize this code + that way during a "maintenance cycle". + +.NOTE + The IN_/OUT_PORT_WORD_/STRING macros are MSF-programmer defined and may or may not have side effects + on their parameters, therefore they can not handle expressions like "len/2". As a solution all these macros + must be called via "simple" variables, with no side effects like ++ and their contents is unpredictable + at completion of the macro + +.NOTE + The implementation is choosen to have input, output and BAP setup all rolled into one monolithic + function rather than a more ameniable hcfio_in_string, hcfio_out_string and bap_ini to minimize the + stack usage during Interrupt Service (especially relevant for DOS drivers) + +.NOTE + The local variable reg corresponds with a register of the appropriate BAP. This is possible by the + intentional choice of the addresses of the individual registers of the two BAPs and the macro used to + specify whether BAP_0 or BAP_1 should be used. The value of reg is changed in the flow of hcfio_string + because, depending on the context, reg is most optimal addressing the offset register or the data register. + + + + .ENDOC END DOCUMENTATION + +-------------------------------------------------------------------------------------------------------------*/ + + +int hcfio_string( IFBP ifbp, int bap, int fid, + int offset, wci_bufp pc_addr, int word_len, int tot_len, int type ) { + +hcf_io reg = ifbp->IFB_IOBase + bap - HREG_DATA_0 + HREG_OFFSET_0; //reg = offset register +hcf_32 prot_cnt = ifbp->IFB_TickIni; +hcf_16 *p1 = bap == BAP_0 ? ifbp->IFB_BAP_0 : ifbp->IFB_BAP_1; +wci_bufp cp; +wci_recordp wp = (wci_recordp)pc_addr; +int rc; +int tlen; + +#if HCF_ALIGN != 0 +#endif // HCF_ALIGN + /* assumption, writing words takes place only initial, never at odd NIC RAM addresses nor odd PC + * addresses */ + + if ( ( rc = ifbp->IFB_TimStat ) == HCF_SUCCESS ) { /* 1 */ + if ( IN_PORT_WORD( ifbp->IFB_IOBase + HREG_SW_0 ) != HCF_MAGIC ) rc = HCF_ERR_NO_NIC; /* 2 */ + } + if ( rc == HCF_SUCCESS ) { + + /* make sure all preceeding BAP manipulation is settled */ + while ( prot_cnt && IN_PORT_WORD( reg ) & (HCMD_BUSY|HREG_OFFSET_ERR) ) prot_cnt--; + + if ( offset != (int)*p1 || fid != (int)*(p1+1) ) { /* 3 */ + OUT_PORT_WORD( reg - HREG_OFFSET_0 + HREG_SELECT_0, fid ); + OUT_PORT_WORD( reg, offset & 0xFFFE ); + *p1 = (hcf_16)offset; + *(p1+1) = (hcf_16)fid; + /* use type == IO_IN and len == 0 as a way to set the BAP for the futute, e.g. at the end of hcf_send */ +// while ( prot_cnt-- && IN_PORT_WORD( reg ) & (HCMD_BUSY|HREG_OFFSET_ERR) ) /*NOP*/; + while ( tot_len && prot_cnt && IN_PORT_WORD( reg ) & (HCMD_BUSY|HREG_OFFSET_ERR) ) prot_cnt--; + if ( prot_cnt == 0 ) { + /* ;? It could be discussed whether the HREG_OFFSET_ERR bit should result in blocking NIC access + * till next initialize */ + rc = ifbp->IFB_TimStat = HCF_ERR_TIME_OUT; + } + } + *p1 += (hcf_16)tot_len; /* 4 */ + } + reg += HREG_DATA_0 - HREG_OFFSET_0; // reg = data register + if ( rc == HCF_SUCCESS && type == IO_IN ) { //input + if ( tot_len ) { + if ( offset & 0x01 ) { /*odd */ /* 10*/ + *pc_addr++ = IN_PORT_BYTE( reg+1 ); + tot_len--; + } + cp = pc_addr; + tlen = DIV_BY_2( tot_len ); + IN_PORT_STRING( reg, cp, tlen ); + if ( tot_len & 1 ) *(pc_addr + tot_len - 1) = IN_PORT_BYTE( reg ); /* 11*/ + while ( word_len-- ) { + CNV_LITTLE_TO_INT_NP( wp ); /* 12*/ + wp++; + } + } + } + if ( rc == HCF_SUCCESS && type != IO_IN ) { //output and/or check + tlen = word_len; /* 20*/ + while ( tlen-- ) { /* 20*/ + OUT_PORT_WORD( reg, *(wci_recordp)pc_addr ); + pc_addr += 2; + } +// tlen = offset + tot_len; + if ( tot_len && offset & 0x01 ) { /* 21*/ + OUT_PORT_WORD( reg, CNV_LITTLE_TO_INT( (*pc_addr <<8) + IN_PORT_BYTE( reg ) ) ); + pc_addr++; + tot_len--; + } + word_len = DIV_BY_2( tot_len ) - word_len; //misuse no longer needed parameter as temporary variable + cp = pc_addr; + OUT_PORT_STRING( reg, cp, word_len ); + if ( tot_len & 1 ) OUT_PORT_BYTE( reg, *(pc_addr + tot_len - 1) ); /* 22*/ + + + if ( type == IO_OUT_CHECK /*&& *p1 != ifbp->IFB_FSBase */) { //;? should BE HARD CODED /* 30*/ + if ( *p1 & 0X01 ) (void)IN_PORT_WORD( reg ); //align on word boundary + OUT_PORT_WORD( reg, 0xCAFE ); + OUT_PORT_WORD( reg, 0xABBA ); + OUT_PORT_WORD( reg, 0xDEAD ); + OUT_PORT_WORD( reg, 0xD00F ); +//!! OUT_PORT_WORD( bap - HREG_OFFSET_0 + HREG_SELECT_0, fid ); /* 31*/ + OUT_PORT_WORD( reg - HREG_DATA_0 + HREG_OFFSET_0, (*p1 + 1)&0xFFFE ); + prot_cnt = ifbp->IFB_TickIni; + while ( prot_cnt && IN_PORT_WORD(reg - HREG_DATA_0 + HREG_OFFSET_0) & (HCMD_BUSY|HREG_OFFSET_ERR) ) prot_cnt--; + if ( prot_cnt == 0 ) { + rc = ifbp->IFB_TimStat = HCF_ERR_TIME_OUT; + } + if ( IN_PORT_WORD( reg ) != 0xCAFE ) { + rc = HCF_FAILURE; + ifbp->IFB_PIFRscInd = 1; +//! } else { +//! rc = HCF_SUCCESS; + } + } + } + if ( rc == HCF_SUCCESS ) { /* 40*/ + if ( IN_PORT_WORD( ifbp->IFB_IOBase + HREG_SW_0 ) != HCF_MAGIC ) rc = HCF_ERR_NO_NIC; + } + +//! ASSERT( bap_ini( ifbp, bap, fid, (offset + len) & 0xFFFE) == HCF_SUCCESS ) /*99 */ + return rc; +}/* hcfio_string */ + diff -urN orig/drivers/net/pcmcia/wvlan_mdd.h linux/drivers/net/pcmcia/wvlan_mdd.h --- orig/drivers/net/pcmcia/wvlan_mdd.h Thu Jan 1 01:00:00 1970 +++ linux/drivers/net/pcmcia/wvlan_mdd.h Wed Oct 9 19:03:09 2002 @@ -0,0 +1,472 @@ +/* This file is part of the Hardware Control Functions Light (HCF-light) library + to control the Lucent Technologies WaveLAN/IEEE Network I/F Card. + The HCF is the implementation of the Wireless Connection I/F (WCI). + + The HCF-light files are a subset of the HCF files. The complete set offers a + number of additional facilities, e.g. firmware download, Etherner-II encapsulation, + additional diagnostic facilities, ASSERT logic to support debugging, 802.11 support, + Configuration Management. + This complete set is explicitely not in the Public Domain but can be made + available under certain restriction. (see the pointer below for support) + + The HCF-light files are 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. + + At the time of this writing, you can request for support at: + betasupport@wavelan.com + + Documentation is expected to be available in the week of 8 Februari 1999 + +*/ + + +#ifndef MDD_H +#define MDD_H 1 + +/************************************************************************************************************* +* +* FILE : mdd.h +* +* DATE : 2000/07/14 23:27:51 1.4 +* +* AUTHOR : Nico Valster +* +* DESC : Definitions and Prototypes for HCF, MSF, UIL as well as USF sources +* +* +* +* Implementation Notes +* + - Typ rather than type is used as field names in structures like CFG_CIS_STRCT because type leads to + conflicts with MASM when the H-file is converted to an INC-file +* +************************************************************************************************************** +Instructions to convert MDD.H to MDD.INC by means of H2INC + +Use a command line which defines the specific macros and command line options +needed to build the C-part, e.g. for the DOS ODI driver + `h2inc /C /Ni /Zp /Zn mdd mdd.h` + + +************************************************************************************************************** +* COPYRIGHT (c) 1998 by Lucent Technologies. All Rights Reserved. +*************************************************************************************************************/ + +/**************************************************************************** +wvlan_mdd.h,v +Revision 1.4 2000/07/14 23:27:51 root +*** empty log message *** + +Revision 1.3 2000/02/28 23:09:38 root +*** empty log message *** + +Revision 1.2 2000/01/06 23:30:53 root +*** empty log message *** + + * + * Rev 1.0 02 Feb 1999 14:32:36 NVALST + * Initial revision. +Revision 1.3 1999/02/01 22:58:35 nico +*** empty log message *** + +Revision 1.2 1999/02/01 21:01:41 nico +*** empty log message *** + +Revision 1.1 1999/01/30 19:24:39 nico +Initial revision + +Revision 1.1 1999/01/30 19:07:57 nico +Initial revision + + * + * Rev 1.110 29 Jan 1999 15:52:44 NVALST + * intermediate, maybe working but seems to need two times to load in + * light-version + * + * Rev 2.12 29 Jan 1999 10:48:46 NVALST + * + * Rev 1.108 28 Jan 1999 14:43:24 NVALST + * intermediate, once more correction of loop in hcf_service_nic + download + * passed to Marc + * + * Rev 2.11 27 Jan 1999 16:57:42 NVALST + * + * Rev 1.107 27 Jan 1999 13:53:24 NVALST + * intermediate, once more correction of loop in hcf_service_nic + * + * Rev 1.106 26 Jan 1999 16:42:46 NVALST + * intermediate, corrected loop in hcf_service_nic (which was as result of a + * walkthrough, changed from a bug without consequences into one with consequences + * + * Rev 1.105 25 Jan 1999 14:24:48 NVALST + * intermediate, hopefully suitable for release + * + * Rev 1.104 22 Jan 1999 16:59:34 NVALST + * intermediate, minor corrections + some HCF-L stuff + * + * Rev 1.103 15 Jan 1999 15:14:46 NVALST + * intermediate, deposited as HCF2.10 + * + * Rev 2.10 15 Jan 1999 14:54:36 NVALST + * + * +****************************************************************************/ + + +/**************************************************************************** +* +* CHANGE HISTORY +* + 961018 - NV + Original Entry, split of from HCF.H + +*************************************************************************************************************/ + +/****************************** M A C R O S ********************************************************/ + +/* min and max macros */ +#if !defined(max) +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif +#if !defined(min) +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + + +/*************************************************************************************************************/ + +/****************************** General define ***************************************************************/ + +#define MAC_ADDR_SIZE 6 +#define GROUP_ADDR_SIZE (32 * MAC_ADDR_SIZE) +#define STAT_NAME_SIZE 32 + + + +//IFB field related +// IFB_CardStat +#define CARD_STAT_PRESENT 0x8000U /* MSF defines card as being present + * controls whether hcf-function is allowed to do I/O */ +#define CARD_STAT_ENABLED 0x4000U // one or more MAC Ports enabled +#define CARD_STAT_INI 0x0800U // Hermes Initiliazed + +// IFB_RxStat +#define RX_STAT_ERR 0x0003U //Error mask +#define RX_STAT_UNDECR 0x0002U //Non-decryptable encrypted message +#define RX_STAT_FCS_ERR 0x0001U //FCS error + +/****************************** Xxxxxxxx *********************************************************************/ + +enum /*hcf_stat*/ { + HCF_FAILURE = 0xFF, /* An (unspecified) failure, 0xFF is choosen to have a non-ubiquitous value + * Note that HCF_xxxx errors which can end up in the CFG_DIAG LTV should + * never exceed 0xFF, because the high order byte of VAL[0] is reserved + * for Hermes errors + */ + HCF_SUCCESS = 0x00, // 0x00: OK + //gap for ODI related status + HCF_ERR_DIAG_0 = 0x02, // 0x02: HCF noticed an error after hcf_disable, before diagnose command + HCF_ERR_DIAG_1, // 0x03: HCF noticed an error after succesful diagnose command + HCF_ERR_TIME_OUT, // 0x04: Expected Hermes event did not occure in expected time + HCF_ERR_NO_NIC, // 0x05: card not found (usually yanked away during hcfio_in_string + HCF_ERR_BUSY, // 0x06: ;?Inquire cmd while another Inquire in progress + HCF_ERR_SEQ_BUG, // 0x07: other cmd than the expected completed, probably HCF-bug + HCF_ERR_LEN, // 0x08: buffer size insufficient + // - hcf_get_info buffer has a size of 0 or 1 or less than needed + // to accomodate all data +}; + +#define HCF_INT_PENDING 1 // (ODI initiated) return status of hcf_act( HCF_ACT_INT_OFF ) + + + +/* hard coded values (e.g. for HCF_ACT_TALLIES and HCF_ACT_INT_OFF) are needed for HCFL */ +typedef enum { /*hcf_action_cmd*/ + /* gap left over by swapping 3 frame mode action with 4 INT_OFF/_ON + * CARD_IN/_OUT. This was done to have HCFL default automagically + * to HCF_ACT_802_3_PURE + * This gap available for future features */ + HCF_ACT_SPARE_03, //03 gap available for future features + /* DUI code 0x04 -> DON'T EVER MOVE */ + /* DUI code 0x05 -> DON'T EVER MOVE */ + HCF_ACT_TALLIES = 0x05, //05 Hermes Inquire Tallies (F100) command +#if defined HCF_ASSERT + HCF_ACT_ASSERT_OFF, //09 de-activate Assert reporting + HCF_ACT_ASSERT_ON, //0A activate Assert reporting +#else +#endif // HCF_ASSERT + /* DUI code 0x0B -> DON'T EVER MOVE */ + /* DUI code 0x0C -> DON'T EVER MOVE */ + HCF_ACT_INT_OFF = 0x0D, //0D Disable Interrupt generation + HCF_ACT_INT_ON, //0E Enable Interrupt generation + HCF_ACT_CARD_IN, //0F MSF reported Card insertion + HCF_ACT_CARD_OUT, //10 MSF reported Card removal +/* HCF_ACT_MAX // xxxx: start value for UIL-range, NOT to be passed to HCF + * Too bad, there was originally no spare room created to use + * HCF_ACT_MAX as an equivalent of HCF_ERR_MAX. Since creating + * this room in retrospect would create a backward incompatibilty + * we will just have to live with the haphazard sequence of + * UIL- and HCF specific codes. Theoretically this could be + * corrected when and if there will ever be an overall + * incompatibilty introduced for another reason + */ +} hcf_action_cmd; + + + + + + + + +/*============================================================= HCF Defined RECORDS =========================*/ +/*============================================================= INFORMATION FRRAMES =====================*/ +#define CFG_INFO_FRAME_MIN 0xF000 //lowest value representing an Informatio Frame + +#define CFG_TALLIES 0xF100 //Communications Tallies +#define CFG_SCAN 0xF101 //Scan results + +#define CFG_LINK_STAT 0xF200 //Link Status + +/*============================================================= CONFIGURATION RECORDS =====================*/ +/*============================================================= mask 0xFCxx =====================*/ +// NETWORK PARAMETERS, STATIC CONFIGURATION ENTITIES +//FC05, FC0A, FC0B, FC0C, FC0D: SEE W2DN149 + +#define CFG_RID_CFG_MIN 0xFC00 //lowest value representing a Configuration RID +#define CFG_CNF_PORT_TYPE 0xFC00 //[STA] Connection control characteristics +#define CFG_CNF_OWN_MAC_ADDR 0xFC01 //[STA] MAC Address of this node +#define CFG_CNF_DESIRED_SSID 0xFC02 //[STA] Service Set identification for connection +#define CFG_CNF_OWN_CHANNEL 0xFC03 //Communication channel for BSS creation +#define CFG_CNF_OWN_SSID 0xFC04 //IBSS creation (STA) or ESS (AP) Service Set Ident +#define CFG_CNF_OWN_ATIM_WINDOW 0xFC05 //[STA] ATIM Window time for IBSS creation +#define CFG_CNF_SYSTEM_SCALE 0xFC06 //System Scale that specifies the AP density +#define CFG_CNF_MAX_DATA_LEN 0xFC07 //Maximum length of MAC Frame Body data +#define CFG_CNF_WDS_ADDR 0xFC08 //[STA] MAC Address of corresponding WDS Link node +#define CFG_CNF_PM_ENABLED 0xFC09 //[STA] Switch for ESS Power Management (PM) On/Off +#define CFG_CNF_PM_EPS 0xFC0A //[STA] Switch for ESS PM EPS/PS Mode +#define CFG_CNF_MCAST_RX 0xFC0B //[STA] Switch for ESS PM Multicast reception On/Off +#define CFG_CNF_MAX_SLEEP_DURATION 0xFC0C //[STA] Maximum sleep time for ESS PM +#define CFG_CNF_HOLDOVER_DURATION 0xFC0D //[STA] Holdover time for ESS PM +#define CFG_CNF_OWN_NAME 0xFC0E //Identification text for diagnostic purposes + +#define CFG_CNF_ENCRYPTION 0xFC20 //select en/de-cryption of Tx/Rx messages +#define CFG_CNF_MICRO_WAVE 0xFC25 //MicroWave (Robustness) + + +// NETWORK PARAMETERS, DYNAMIC CONFIGURATION ENTITIES +#define CFG_GROUP_ADDR 0xFC80 //[STA] Multicast MAC Addresses for Rx-message +#define CFG_CREATE_IBSS 0xFC81 //[STA] Switch for IBSS creation On/Off +#define CFG_FRAGMENTATION_THRH 0xFC82 //[STA] Fragment length for unicast Tx-message +#define CFG_RTS_THRH 0xFC83 //[STA] Frame length used for RTS/CTS handshake +#define CFG_TX_RATE_CONTROL 0xFC84 //[STA] Data rate control for message transmission +#define CFG_PROMISCUOUS_MODE 0xFC85 //[STA] Switch for Promiscuous mode reception On/Off + +#define CFG_CNF_DEFAULT_KEYS 0xFCB0 //defines set of encryption keys +#define CFG_CNF_TX_KEY_ID 0xFCB1 //select key for encryption of Tx messages + + +// BEHAVIOR PARAMETERS +#define CFG_TICK_TIME 0xFCE0 //[PRI] Auxiliary Timer tick interval +#define CFG_RID_CFG_MAX 0xFCFF //highest value representing an Configuration RID + + +/*============================================================= INFORMATION RECORDS =====================*/ +/*============================================================= mask 0xFDxx =====================*/ +// NIC INFORMATION +#define CFG_RID_INF_MIN 0xFD00 //lowest value representing an Information RID +#define CFG_PRI_IDENTITY 0xFD02 +#define CFG_PRI_SUP_RANGE 0xFD03 //Primary supplier range +#define CFG_CFI_ACT_RANGES_PRI 0xFD04 + +#define CFG_HSI_SUP_RANGE 0xFD09 //H/W - S/W I/F supplier range +#define CFG_NIC_SERIAL_NUMBER 0xFD0A +#define CFG_NIC_IDENTITY 0xFD0B +#define CFG_MFI_SUP_RANGE 0xFD0C +#define CFG_CFI_SUP_RANGE 0xFD0D + +#define CFG_CHANNEL_LIST 0xFD10 //Allowed communication channels +#define CFG_REG_DOMAINS 0xFD11 //List of intended regulatory domains +#define CFG_TEMP_TYPE 0xFD12 //Hardware temperature range code +#define CFG_CIS 0xFD13 //PC Card Standard Card Information Structure + +#define CFG_STA_IDENTITY 0xFD20 +#define CFG_STA_SUP_RANGE 0xFD21 //Station supplier range +#define CFG_MFI_ACT_RANGES_STA 0xFD22 +#define CFG_CFI_ACT_RANGES_STA 0xFD23 + +// MAC INFORMATION +#define CFG_PORT_STAT 0xFD40 //[STA] Actual MAC Port connection control status +#define CFG_CURRENT_SSID 0xFD41 //[STA] Identification of the actually connected SS +#define CFG_CURRENT_BSSID 0xFD42 //[STA] Identification of the actually connected BSS +#define CFG_COMMS_QUALITY 0xFD43 //[STA] Quality of the Basic Service Set connection +#define CFG_CURRENT_TX_RATE 0xFD44 //[STA] Actual transmit data rate +#define CFG_OWN_BEACON_INTERVAL 0xFD45 //Beacon transmit interval time for BSS creation +#define CFG_CUR_SCALE_THRH 0xFD46 //Actual System Scale thresholds settings +#define CFG_PROTOCOL_RSP_TIME 0xFD47 //Max time to await a response to a request message +#define CFG_SHORT_RETRY_LIMIT 0xFD48 //Max number of transmit attempts for short frames +#define CFG_LONG_RETRY_LIMIT 0xFD49 //Max number of transmit attempts for long frames +#define CFG_MAX_TX_LIFETIME 0xFD4A //Max transmit frame handling duration +#define CFG_MAX_RX_LIFETIME 0xFD4B //Max received frame handling duration +#define CFG_CF_POLLABLE 0xFD4C //[STA] Contention Free pollable capability indication +#define CFG_AUTHENTICATION_ALGORITHMS 0xFD4D //Available Authentication Algorithms indication +#define CFG_AUTHENTICATION_TYPE 0xFD4E //Available Authentication Types indication +#define CFG_PRIVACY_OPTION_IMPLEMENTED 0xFD4F //WEP Option availability indication + + +// MODEM INFORMATION +#define CFG_PHY_TYPE 0xFDC0 // // Physical layer type indication +#define CFG_CURRENT_CHANNEL 0xFDC1 //Actual frequency channel used for transmission +#define CFG_CURRENT_POWER_STATE 0xFDC2 //Actual power consumption status +#define CFG_CCAMODE 0xFDC3 //Clear channel assessment mode indication +#define CFG_CCATIME 0xFDC4 //Clear channel assessment time +#define CFG_MAC_PROCESSING_DELAY 0xFDC5 //MAC processing delay time +#define CFG_SUPPORTED_DATA_RATES 0xFDC6 //Data rates capability information + +#define CFG_RID_INF_MAX 0xFDFF //highest value representing an Information RID + +//} hcf_info_type; + + + + +/*************************************************************************************************************/ + +/****************************** S T R U C T U R E D E F I N I T I O N S ************************************/ + +typedef struct LTV_STRCT { //used for all "minimal" LTV records + hcf_16 len; //default length of RID + hcf_16 typ; //RID identification as defined by Hermes + hcf_16 val[1]; //do not change this, some dynamic structures are defined based on this !! +}LTV_STRCT; + +typedef LTV_STRCT FAR * LTVP; + + + + + +#define COMP_ID_MINIPORT 41 //Windows 9x/NT Miniport +#define COMP_ID_PACKET 42 //Packet +#define COMP_ID_ODI_16 43 //DOS ODI +#define COMP_ID_ODI_32 44 //32-bits ODI +#define COMP_ID_MAC_OS 45 //Macintosh OS +#define COMP_ID_WIN_CE 46 //Windows CE Miniport +#define COMP_ID_LINUX 47 //You never guessed, Linux +#define COMP_ID_AP1 81 //WaveLAN/IEEE AP + + + +#define COMP_ROLE_SUPL 00 //supplier +#define COMP_ROLE_ACT 01 //actor + +#define COMP_ID_MFI 01 //Modem - Firmware I/F +#define COMP_ID_CFI 02 //Controller - Firmware I/F +#define COMP_ID_PRI 03 //Primary Firmware - Driver I/F +#define COMP_ID_STA 04 //Station Firmware - Driver I/F +#define COMP_ID_DUI 05 //Driver - Utility I/F +#define COMP_ID_HSI 06 //H/W - Driver I/F + +typedef struct KEY_STRCT { + hcf_16 len; //length of key + hcf_8 key[14]; //encryption key +} KEY_STRCT; + +typedef struct CFG_CNF_DEFAULT_KEYS_STRCT { //CFG_CNF_DEFAULT_KEYS (0xFCB0) defines set of encrypti + hcf_16 len; //default length of RID + hcf_16 typ; //RID identification as defined by Hermes + KEY_STRCT key[4]; //encryption keys +} CFG_CNF_DEFAULT_KEYS_STRCT; + + +typedef struct CFG_REG_DOMAINS_STRCT { //CFG_REG_DOMAINS (0xFD11) List of intended regulatory domains. + hcf_16 len; //length of RID + hcf_16 typ; //RID identification as defined by Hermes + hcf_16 domains[6]; +}CFG_REG_DOMAINS_STRCT; + +typedef struct CFG_CIS_STRCT { //CFG_CIS (0xFD13) PC Card Standard Card Information Structure + hcf_16 len; //length of RID + hcf_16 typ; //RID identification as defined by Hermes + hcf_16 cis[240]; //Compact CIS Area, a linked list of tuples +}CFG_CIS_STRCT; + + +typedef struct CFG_COMMS_QUALITY_STRCT {//CFG_COMMS_QUALITY (0xFD43) Quality of the Basic Service Set connection [STA] + hcf_16 len; //length of RID + hcf_16 typ; //RID identification as defined by Hermes + hcf_16 coms_qual; //Communication Quality of the BSS the station is connected to + hcf_16 signal_lvl; //Average Signal Level of the BSS the station is connected to + hcf_16 noise_lvl; //Average Noise Level of the currently used Frequency Channel +}CFG_COMMS_QUALITY_STRCT; + + + +typedef struct CFG_CUR_SCALE_THRH_STRCT {//CFG_CUR_SCALE_THRH (0xFD46) Actual System Scale thresholds + hcf_16 len; //default length of RID [STA: 6 AP: 4] + hcf_16 typ; //RID identification as defined by Hermes + hcf_16 energy_detect_thrh; //Receiver H/W Energy Detect Threshold + hcf_16 carrier_detect_thrh; //Receiver H/W Carrier Detect Threshold + hcf_16 defer_thrh; //Receiver H/W Defer Threshold + hcf_16 cell_search_thrh; //Firmware Roaming Cell Search Threshold [STA] + hcf_16 out_of_range_thrh; //Firmware Roaming Out of Range Threshold [STA] + hcf_16 delta_snr; //Firmware Roaming Delta SNR value [STA] +}CFG_CUR_SCALE_THRH_STRCT; + + +typedef struct CFG_PCF_INFO_STRCT { //CFG_PCF_INFO (0xFD87) Point Coordination Function capability info [AP] + hcf_16 len; //default length of RID + hcf_16 typ; //RID identification as defined by Hermes + hcf_16 energy_detect_thrh; + hcf_16 carrier_detect_thrh; + hcf_16 defer_thrh; + hcf_16 cell_search_thrh; + hcf_16 range_thrh; +}CFG_PCF_INFO_STRCT; + + +typedef struct CFG_MAC_ADDR_STRCT{ //0xFC01 [STA] MAC Address of this node. + //0xFC08 STA] MAC Address of corresponding WDS Link node. + //0xFC11 [AP] Port 1 MAC Adrs of corresponding WDS Link node + //0xFC12 [AP] Port 2 MAC Adrs of corresponding WDS Link node + //0xFC13 [AP] Port 3 MAC Adrs of corresponding WDS Link node + //0xFC14 [AP] Port 4 MAC Adrs of corresponding WDS Link node + //0xFC15 [AP] Port 5 MAC Adrs of corresponding WDS Link node + //0xFC16 [AP] Port 6 MAC Adrs of corresponding WDS Link node + hcf_16 len; //default length of RID + hcf_16 typ; //RID identification as defined by Hermes + hcf_16 mac_addr[3]; +}CFG_MAC_ADDR_STRCT; + +typedef struct CFG_GROUP_ADDR_STRCT{ //0xFC80 //[STA] Multicast MAC Addresses for + hcf_16 len; //default length of RID + hcf_16 typ; //RID identification as defined by Hermes + hcf_16 mac_addr[GROUP_ADDR_SIZE/6][3]; +}CFG_GROUP_ADDR_STRCT; + + +typedef struct CFG_ID_STRCT { //0xFC02 [STA] Service Set identification for connection. + //0xFC04 IBSS creation (STA) or ESS (AP) Service Set Ident + //0xFC0E Identification text for diagnostic purposes. + hcf_16 len; //default length of RID + hcf_16 typ; //RID identification as defined by Hermes + hcf_16 id[17]; +}CFG_ID_STRCT; + + +typedef void * DUIP; + +#endif // MDD_H + + diff -urN orig/drivers/net/smc9194.c linux/drivers/net/smc9194.c --- orig/drivers/net/smc9194.c Mon May 5 17:39:23 2003 +++ linux/drivers/net/smc9194.c Tue May 6 15:56:14 2003 @@ -12,8 +12,8 @@ . AUI/TP selection ( mine has 10Base2/10BaseT select ) . . Arguments: - . io = for the base address - . irq = for the IRQ + . io = for the base address + . irq = for the IRQ . ifport = 0 for autodetect, 1 for TP, 2 for AUI ( or 10base2 ) . . author: @@ -51,12 +51,21 @@ . allocation . 08/20/00 Arnaldo Melo fix kfree(skb) in smc_hardware_send_packet . 12/15/00 Christian Jullien fix "Warning: kfree_skb on hard IRQ" + . 06/23/01 Russell King Separate out IO functions for different bus + . types. + . Use dev->name instead of CARDNAME for printk + . Add ethtool support, full duplex support + . Add LAN91C96 support. . 11/08/01 Matt Domsch Use common crc32 function ----------------------------------------------------------------------------*/ +#define DRV_NAME "smc9194" +#define DRV_VERSION "0.15" + static const char version[] = - "smc9194.c:v0.14 12/15/00 by Erik Stahlman (erik@vt.edu)\n"; + DRV_NAME ".c:v" DRV_VERSION " 12/15/00 by Erik Stahlman (erik@vt.edu)\n"; +#include #include #include #include @@ -67,15 +76,27 @@ #include #include #include +#include #include #include #include +#include + #include #include #include #include +#include #include +#include + +#ifdef CONFIG_ARCH_SA1100 +#include +#include +#include +#include +#endif #include "smc9194.h" @@ -151,29 +172,27 @@ -------------------------------------------------------------------------*/ #define CARDNAME "SMC9194" +static const char *chip_ids[15] = { + NULL, + NULL, + NULL, + "SMC91C90/91C92", /* 3 */ + "SMC91C94/91C96", /* 4 */ + "SMC91C95", /* 5 */ + NULL, + "SMC91C100", /* 7 */ + "SMC91C100FD", /* 8 */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; -/* store this information for the driver.. */ -struct smc_local { - /* - these are things that the kernel wants me to keep, so users - can find out semi-useless statistics of how well the card is - performing - */ - struct net_device_stats stats; - - /* - If I have to wait until memory is available to send - a packet, I will store the skbuff here, until I get the - desired memory. Then, I'll send it out and free it. - */ - struct sk_buff * saved_skb; - - /* - . This keeps track of how many packets that I have - . sent out. When an TX_EMPTY interrupt comes, I know - . that all of these have been sent. - */ - int packets_waiting; +static const char * interfaces[2] = { + "TP", + "AUI" }; @@ -201,6 +220,11 @@ static int smc_open(struct net_device *dev); /* + . This handles the ethtool interface +*/ +static int smc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); + +/* . Our watchdog timed out. Called by the networking layer */ static void smc_timeout(struct net_device *dev); @@ -216,11 +240,11 @@ . This routine allows the proc file system to query the driver's . statistics. */ -static struct net_device_stats * smc_query_statistics( struct net_device *dev); +static struct net_device_stats * smc_query_statistics(struct net_device *dev); /* - . Finally, a call to set promiscuous mode ( for TCPDUMP and related - . programs ) and multicast modes. + . Finally, a call to set promiscuous mode (for TCPDUMP and related + . programs) and multicast modes. */ static void smc_set_multicast_list(struct net_device *dev); @@ -239,12 +263,12 @@ . This is a separate procedure to handle the receipt of a packet, to . leave the interrupt code looking slightly cleaner */ -static inline void smc_rcv( struct net_device *dev ); +static inline void smc_rcv(struct net_device *dev); /* . This handles a TX interrupt, which is only called when an error . relating to a packet is sent. */ -static inline void smc_tx( struct net_device * dev ); +static inline void smc_tx(struct net_device * dev); /* ------------------------------------------------------------ @@ -260,39 +284,287 @@ */ static int smc_probe(struct net_device *dev, int ioaddr); -/* - . A rather simple routine to print out a packet for debugging purposes. -*/ -#if SMC_DEBUG > 2 -static void print_packet( byte *, int ); -#endif - -#define tx_done(dev) 1 - /* this is called to actually send the packet to the chip */ -static void smc_hardware_send_packet( struct net_device * dev ); +static void smc_hardware_send_packet(struct net_device * dev); /* Since I am not sure if I will have enough room in the chip's ram . to store the packet, I call this routine, which either sends it . now, or generates an interrupt when the card is ready for the . packet */ -static int smc_wait_to_send_packet( struct sk_buff * skb, struct net_device *dev ); +static int smc_wait_to_send_packet(struct sk_buff * skb, struct net_device *dev); /* this does a soft reset on the device */ -static void smc_reset( int ioaddr ); +static void smc_reset(struct net_device *dev); /* Enable Interrupts, Receive, and Transmit */ -static void smc_enable( int ioaddr ); +static void smc_enable(struct net_device *dev); /* this puts the device in an inactive state */ -static void smc_shutdown( int ioaddr ); +static void smc_shutdown(struct net_device *dev); /* This routine will find the IRQ of the driver if one is not . specified in the input to the device. */ -static int smc_findirq( int ioaddr ); +static int smc_findirq(struct net_device *dev); +#ifndef CONFIG_ASSABET_NEPONSET /* - . Function: smc_reset( int ioaddr ) + * These functions allow us to handle IO addressing as we wish - this + * ethernet controller can be connected to a variety of busses. Some + * busses do not support 16 bit or 32 bit transfers. --rmk + */ +static inline u8 smc_inb(u_int base, u_int reg) +{ + return inb(base + reg); +} + +static inline u16 smc_inw(u_int base, u_int reg) +{ + return inw(base + reg); +} + +static inline void smc_ins(u_int base, u_int reg, u8 *data, u_int len) +{ + u_int port = base + reg; +#ifdef USE_32_BIT + /* QUESTION: Like in the TX routine, do I want + to send the DWORDs or the bytes first, or some + mixture. A mixture might improve already slow PIO + performance */ + PRINTK3((" Reading %d dwords (and %d bytes) \n", + len >> 2, len & 3)); + insl(port, data, len >> 2); + /* read the left over bytes */ + insb(port, data + (len & ~3), len & 3); +#else + PRINTK3((" Reading %d words and %d byte(s) \n", + len >> 1, len & 1)); + insw(port, data, len >> 1); + if (len & 1) { + data += len & ~1; + *data = inb(port); + } +#endif +} + +static inline void smc_outb(u8 val, u_int base, u_int reg) +{ + outb(val, base + reg); +} + +static inline void smc_outw(u16 val, u_int base, u_int reg) +{ + outw(val, base + reg); +} + +static inline void smc_outl(u32 val, u_int base, u_int reg) +{ + u_int port = base + reg; +#ifdef USE_32_BIT + outl(val, port); +#else + outw(val, port); + outw(val >> 16, port); +#endif +} + +static inline void smc_outs(u_int base, u_int reg, u8 *data, u_int len) +{ + u_int port = base + reg; +#ifdef USE_32_BIT + if (len & 2) { + outsl(port, data, len >> 2); + outw(*((word *)(data + (len & ~3))), port); + } + else + outsl(port, data, len >> 2); +#else + outsw(port, data, len >> 1); +#endif +} + + +/*------------------------------------------------------------------------- + . I define some macros to make it easier to do somewhat common + . or slightly complicated, repeated tasks. + --------------------------------------------------------------------------*/ + +/* select a register bank, 0 to 3 */ + +#define SMC_SELECT_BANK(x) \ + { \ + smc_outw(x, ioaddr, BANK_SELECT); \ + } + +/* define a small delay for the reset */ +#define SMC_DELAY() \ + { \ + smc_inw(ioaddr, RCR); \ + smc_inw(ioaddr, RCR); \ + smc_inw(ioaddr, RCR); \ + } + +/* this enables an interrupt in the interrupt mask register */ +#define SMC_ENABLE_INT(x) \ + { \ + byte mask; \ + mask = smc_inb(ioaddr, INT_MASK); \ + mask |= (x); \ + smc_outb(mask, ioaddr, INT_MASK); \ + } + +/* this sets the absolutel interrupt mask */ +#define SMC_SET_INT(x) \ + { \ + smc_outw((x), INT_MASK); \ + } + +#else + +#undef SMC_IO_EXTENT +#define SMC_IO_EXTENT (16 << 2) + +/* + * These functions allow us to handle IO addressing as we wish - this + * ethernet controller can be connected to a variety of busses. Some + * busses do not support 16 bit or 32 bit transfers. --rmk + */ +static inline u8 smc_inb(u_int base, u_int reg) +{ + u_int port = base + reg * 4; + + return readb(port); +} + +static inline u16 smc_inw(u_int base, u_int reg) +{ + u_int port = base + reg * 4; + + return readb(port) | readb(port + 4) << 8; +} + +static inline void smc_ins(u_int base, u_int reg, u8 *data, u_int len) +{ + u_int port = base + reg * 4; + + insb(port, data, len); +} + +static inline void smc_outb(u8 val, u_int base, u_int reg) +{ + u_int port = base + reg * 4; + + writeb(val, port); +} + +static inline void smc_outw(u16 val, u_int base, u_int reg) +{ + u_int port = base + reg * 4; + + writeb(val, port); + writeb(val >> 8, port + 4); +} + +static inline void smc_outl(u32 val, u_int base, u_int reg) +{ + u_int port = base + reg * 4; + + writeb(val, port); + writeb(val >> 8, port + 4); + writeb(val >> 16, port + 8); + writeb(val >> 24, port + 12); +} + +static inline void smc_outs(u_int base, u_int reg, u8 *data, u_int len) +{ + u_int port = base + reg * 4; + + outsb(port, data, len & ~1); +} + +/*------------------------------------------------------------------------- + . I define some macros to make it easier to do somewhat common + . or slightly complicated, repeated tasks. + --------------------------------------------------------------------------*/ + +/* select a register bank, 0 to 3 */ + +#define SMC_SELECT_BANK(x) \ + { \ + smc_outb(x, ioaddr, BANK_SELECT); \ + } + +/* define a small delay for the reset */ +#define SMC_DELAY() \ + { \ + smc_inb(ioaddr, RCR); \ + smc_inb(ioaddr, RCR); \ + smc_inb(ioaddr, RCR); \ + } + +/* this enables an interrupt in the interrupt mask register */ +#define SMC_ENABLE_INT(x) \ + { \ + byte mask; \ + mask = smc_inb(ioaddr, INT_MASK); \ + mask |= (x); \ + smc_outb(mask, ioaddr, INT_MASK); \ + } + +/* this sets the absolutel interrupt mask */ +#define SMC_SET_INT(x) \ + { \ + smc_outb((x), ioaddr, INT_MASK); \ + } + +#endif + +/* + . A rather simple routine to print out a packet for debugging purposes. +*/ +#if SMC_DEBUG > 2 +static void print_packet(byte * buf, int length) +{ + int i; + int remainder; + int lines; + + printk("Packet of length %d \n", length); + lines = length / 16; + remainder = length % 16; + + for (i = 0; i < lines ; i ++) { + int cur; + + for (cur = 0; cur < 8; cur ++) { + byte a, b; + + a = *(buf ++); + b = *(buf ++); + printk("%02x%02x ", a, b); + } + printk("\n"); + } + for (i = 0; i < remainder/2 ; i++) { + byte a, b; + + a = *(buf ++); + b = *(buf ++); + printk("%02x%02x ", a, b); + } + if (remainder & 1) { + byte a; + + a = *buf++; + printk("%02x", a); + } + printk("\n"); +} +#else +#define print_packet(buf,len) do { } while (0) +#endif + +/* + . Function: smc_reset(struct net_device *dev) . Purpose: . This sets the SMC91xx chip to its normal state, hopefully from whatever . mess that any other DOS driver has put it in. @@ -308,36 +580,37 @@ . 5. clear all interrupts . */ -static void smc_reset( int ioaddr ) +static void smc_reset(struct net_device *dev) { + u_int ioaddr = dev->base_addr; + /* This resets the registers mostly to defaults, but doesn't affect EEPROM. That seems unnecessary */ - SMC_SELECT_BANK( 0 ); - outw( RCR_SOFTRESET, ioaddr + RCR ); + SMC_SELECT_BANK(0); + smc_outw(RCR_SOFTRESET, ioaddr, RCR); /* this should pause enough for the chip to be happy */ - SMC_DELAY( ); + SMC_DELAY(); /* Set the transmit and receive configuration registers to default values */ - outw( RCR_CLEAR, ioaddr + RCR ); - outw( TCR_CLEAR, ioaddr + TCR ); + smc_outw(RCR_CLEAR, ioaddr, RCR); + smc_outw(TCR_CLEAR, ioaddr, TCR); /* set the control register to automatically release successfully transmitted packets, to make the best use out of our limited memory */ - SMC_SELECT_BANK( 1 ); - outw( inw( ioaddr + CONTROL ) | CTL_AUTO_RELEASE , ioaddr + CONTROL ); + SMC_SELECT_BANK(1); + smc_outw(smc_inw(ioaddr, CONTROL) | CTL_AUTO_RELEASE, ioaddr, CONTROL); /* Reset the MMU */ - SMC_SELECT_BANK( 2 ); - outw( MC_RESET, ioaddr + MMU_CMD ); + SMC_SELECT_BANK(2); + smc_outw(MC_RESET, ioaddr, MMU_CMD); /* Note: It doesn't seem that waiting for the MMU busy is needed here, but this is a place where future chipsets _COULD_ break. Be wary of issuing another MMU command right after this */ - - outb( 0, ioaddr + INT_MASK ); + SMC_SET_INT(0); } /* @@ -348,20 +621,21 @@ . 2. Enable the receiver . 3. Enable interrupts */ -static void smc_enable( int ioaddr ) +static void smc_enable(struct net_device *dev) { - SMC_SELECT_BANK( 0 ); + u_int ioaddr = dev->base_addr; + SMC_SELECT_BANK(0); /* see the header file for options in TCR/RCR NORMAL*/ - outw( TCR_NORMAL, ioaddr + TCR ); - outw( RCR_NORMAL, ioaddr + RCR ); + smc_outw(TCR_NORMAL, ioaddr, TCR); + smc_outw(RCR_NORMAL, ioaddr, RCR); /* now, enable interrupts */ - SMC_SELECT_BANK( 2 ); - outb( SMC_INTERRUPT_MASK, ioaddr + INT_MASK ); + SMC_SELECT_BANK(2); + SMC_SET_INT(SMC_INTERRUPT_MASK); } /* - . Function: smc_shutdown + . Function: smc_shutdown(struct net_device *dev) . Purpose: closes down the SMC91xxx chip. . Method: . 1. zero the interrupt mask @@ -374,26 +648,28 @@ . the manual says that it will wake up in response to any I/O requests . in the register space. Empirical results do not show this working. */ -static void smc_shutdown( int ioaddr ) +static void smc_shutdown(struct net_device *dev) { + u_int ioaddr = dev->base_addr; + /* no more interrupts for me */ - SMC_SELECT_BANK( 2 ); - outb( 0, ioaddr + INT_MASK ); + SMC_SELECT_BANK(2); + SMC_SET_INT(0); /* and tell the card to stay away from that nasty outside world */ - SMC_SELECT_BANK( 0 ); - outb( RCR_CLEAR, ioaddr + RCR ); - outb( TCR_CLEAR, ioaddr + TCR ); + SMC_SELECT_BANK(0); + smc_outb(RCR_CLEAR, ioaddr, RCR); + smc_outb(TCR_CLEAR, ioaddr, TCR); #if 0 /* finally, shut the chip down */ - SMC_SELECT_BANK( 1 ); - outw( inw( ioaddr + CONTROL ), CTL_POWERDOWN, ioaddr + CONTROL ); + SMC_SELECT_BANK(1); + smc_outw(smc_inw(ioaddr, CONTROL), CTL_POWERDOWN, ioaddr, CONTROL); #endif } /* - . Function: smc_setmulticast( int ioaddr, int count, dev_mc_list * adds ) + . Function: smc_setmulticast(int ioaddr, int count, dev_mc_list * adds) . Purpose: . This sets the internal hardware table to filter out unwanted multicast . packets before they take up memory. @@ -410,26 +686,28 @@ */ -static void smc_setmulticast( int ioaddr, int count, struct dev_mc_list * addrs ) { +static void smc_setmulticast(struct net_device *dev, int count, struct dev_mc_list * addrs) +{ + u_int ioaddr = dev->base_addr; int i; - unsigned char multicast_table[ 8 ]; + unsigned char multicast_table[8]; struct dev_mc_list * cur_addr; /* table for flipping the order of 3 bits */ unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; /* start with a table of all zeros: reject all */ - memset( multicast_table, 0, sizeof( multicast_table ) ); + memset(multicast_table, 0, sizeof(multicast_table)); cur_addr = addrs; - for ( i = 0; i < count ; i ++, cur_addr = cur_addr->next ) { + for (i = 0; i < count ; i ++, cur_addr = cur_addr->next) { int position; /* do we have a pointer here? */ - if ( !cur_addr ) + if (!cur_addr) break; /* make sure this is a multicast address - shouldn't this be a given if we have it here ? */ - if ( !( *cur_addr->dmi_addr & 1 ) ) + if (!(*cur_addr->dmi_addr & 1)) continue; /* only use the low order bits */ @@ -441,15 +719,15 @@ } /* now, the table can be loaded into the chipset */ - SMC_SELECT_BANK( 3 ); + SMC_SELECT_BANK(3); - for ( i = 0; i < 8 ; i++ ) { - outb( multicast_table[i], ioaddr + MULTICAST1 + i ); + for (i = 0; i < 8 ; i++) { + smc_outb(multicast_table[i], ioaddr, MULTICAST1 + i); } } /* - . Function: smc_wait_to_send_packet( struct sk_buff * skb, struct net_device * ) + . Function: smc_wait_to_send_packet(struct sk_buff * skb, struct net_device *) . Purpose: . Attempt to allocate memory for a packet, if chip-memory is not . available, then tell the card to generate an interrupt when it @@ -464,10 +742,10 @@ . o (NO): Enable interrupts and let the interrupt handler deal with it. . o (YES):Send it now. */ -static int smc_wait_to_send_packet( struct sk_buff * skb, struct net_device * dev ) +static int smc_wait_to_send_packet(struct sk_buff * skb, struct net_device * dev) { struct smc_local *lp = (struct smc_local *)dev->priv; - unsigned short ioaddr = dev->base_addr; + u_int ioaddr = dev->base_addr; word length; unsigned short numPages; word time_out; @@ -476,16 +754,17 @@ /* Well, I want to send the packet.. but I don't know if I can send it right now... */ - if ( lp->saved_skb) { + if (lp->saved_skb) { /* THIS SHOULD NEVER HAPPEN. */ lp->stats.tx_aborted_errors++; - printk(CARDNAME": Bad Craziness - sent packet while busy.\n" ); + printk("%s: Bad Craziness - sent packet while busy.\n", + dev->name); return 1; } lp->saved_skb = skb; length = skb->len; - + if (length < ETH_ZLEN) { skb = skb_padto(skb, ETH_ZLEN); if (skb == NULL) { @@ -494,32 +773,35 @@ } length = ETH_ZLEN; } - + /* ** The MMU wants the number of pages to be the number of 256 bytes - ** 'pages', minus 1 ( since a packet can't ever have 0 pages :) ) + ** 'pages', minus 1 (since a packet can't ever have 0 pages :)) ** ** Pkt size for allocating is data length +6 (for additional status words, ** length and ctl!) If odd size last byte is included in this header. */ - numPages = ((length & 0xfffe) + 6) / 256; + numPages = ((length & 0xfffe) + 6) / 256; - if (numPages > 7 ) { - printk(CARDNAME": Far too big packet error. \n"); + if (numPages > 7) { + printk("%s: Far too big packet error. (%d:%d)\n", dev->name, length, skb->len); /* freeing the packet is a good thing here... but should . any packets of this size get down here? */ dev_kfree_skb (skb); lp->saved_skb = NULL; + lp->stats.tx_errors++; + lp->stats.tx_dropped++; /* this IS an error, but, i don't want the skb saved */ netif_wake_queue(dev); return 0; } + /* either way, a packet is waiting now */ lp->packets_waiting++; /* now, try to allocate the memory */ - SMC_SELECT_BANK( 2 ); - outw( MC_ALLOC | numPages, ioaddr + MMU_CMD ); + SMC_SELECT_BANK(2); + smc_outw(MC_ALLOC | numPages, ioaddr, MMU_CMD); /* . Performance Hack . @@ -536,21 +818,21 @@ do { word status; - status = inb( ioaddr + INTERRUPT ); - if ( status & IM_ALLOC_INT ) { + status = smc_inb(ioaddr, INTERRUPT); + if (status & IM_ALLOC_INT) { /* acknowledge the interrupt */ - outb( IM_ALLOC_INT, ioaddr + INTERRUPT ); - break; + smc_outb(IM_ALLOC_INT, ioaddr, INTERRUPT); + break; } - } while ( -- time_out ); + } while (-- time_out); - if ( !time_out ) { + if (!time_out) { /* oh well, wait until the chip finds memory later */ - SMC_ENABLE_INT( IM_ALLOC_INT ); - PRINTK2((CARDNAME": memory allocation deferred. \n")); + SMC_ENABLE_INT(IM_ALLOC_INT); + PRINTK2(("%s: memory allocation deferred.\n", dev->name)); /* it's deferred, but I'll handle it later */ - return 0; - } + return 0; + } /* or YES! I can send the packet now.. */ smc_hardware_send_packet(dev); netif_wake_queue(dev); @@ -558,46 +840,46 @@ } /* - . Function: smc_hardware_send_packet(struct net_device * ) + . Function: smc_hardware_send_packet(struct net_device *) . Purpose: . This sends the actual packet to the SMC9xxx chip. . . Algorithm: . First, see if a saved_skb is available. - . ( this should NOT be called if there is no 'saved_skb' + . (this should NOT be called if there is no 'saved_skb' . Now, find the packet number that the chip allocated . Point the data pointers at it in memory . Set the length word in the chip's memory . Dump the packet to chip memory - . Check if a last byte is needed ( odd length packet ) + . Check if a last byte is needed (odd length packet) . if so, set the control flag right . Tell the card to send it . Enable the transmit interrupt, so I know if it failed . Free the kernel data if I actually sent it. */ -static void smc_hardware_send_packet( struct net_device * dev ) +static void smc_hardware_send_packet(struct net_device *dev) { struct smc_local *lp = (struct smc_local *)dev->priv; - byte packet_no; - struct sk_buff * skb = lp->saved_skb; - word length; - unsigned short ioaddr; - byte * buf; - - ioaddr = dev->base_addr; + struct sk_buff *skb = lp->saved_skb; + word length, lastword; + u_int ioaddr = dev->base_addr; + byte packet_no; + byte *buf; - if ( !skb ) { - PRINTK((CARDNAME": In XMIT with no packet to send \n")); + if (!skb) { + PRINTK(("%s: In XMIT with no packet to send\n", dev->name)); return; } + length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; buf = skb->data; /* If I get here, I _know_ there is a packet slot waiting for me */ - packet_no = inb( ioaddr + PNR_ARR + 1 ); - if ( packet_no & 0x80 ) { + packet_no = smc_inb(ioaddr, PNR_ARR + 1); + if (packet_no & 0x80) { /* or isn't there? BAD CHIP! */ - printk(KERN_DEBUG CARDNAME": Memory allocation failed. \n"); + printk(KERN_DEBUG "%s: Memory allocation failed.\n", + dev->name); dev_kfree_skb_any(skb); lp->saved_skb = NULL; netif_wake_queue(dev); @@ -605,26 +887,19 @@ } /* we have a packet address, so tell the card to use it */ - outb( packet_no, ioaddr + PNR_ARR ); + smc_outb(packet_no, ioaddr, PNR_ARR); /* point to the beginning of the packet */ - outw( PTR_AUTOINC , ioaddr + POINTER ); + smc_outw(PTR_AUTOINC, ioaddr, POINTER); - PRINTK3((CARDNAME": Trying to xmit packet of length %x\n", length )); -#if SMC_DEBUG > 2 - print_packet( buf, length ); -#endif + PRINTK3(("%s: Trying to xmit packet of length %x\n", + dev->name, length)); - /* send the packet length ( +6 for status, length and ctl byte ) - and the status word ( set to zeros ) */ -#ifdef USE_32_BIT - outl( (length +6 ) << 16 , ioaddr + DATA_1 ); -#else - outw( 0, ioaddr + DATA_1 ); - /* send the packet length ( +6 for status words, length, and ctl*/ - outb( (length+6) & 0xFF,ioaddr + DATA_1 ); - outb( (length+6) >> 8 , ioaddr + DATA_1 ); -#endif + print_packet(buf, length); + + /* send the packet length (+6 for status, length and ctl byte) + and the status word (set to zeros) */ + smc_outl((length + 6) << 16, ioaddr, DATA_1); /* send the actual data . I _think_ it's faster to send the longs first, and then @@ -633,32 +908,22 @@ . a good idea to check which is optimal? But that could take . almost as much time as is saved? */ -#ifdef USE_32_BIT - if ( length & 0x2 ) { - outsl(ioaddr + DATA_1, buf, length >> 2 ); - outw( *((word *)(buf + (length & 0xFFFFFFFC))),ioaddr +DATA_1); - } - else - outsl(ioaddr + DATA_1, buf, length >> 2 ); -#else - outsw(ioaddr + DATA_1 , buf, (length ) >> 1); -#endif - /* Send the last byte, if there is one. */ + smc_outs(ioaddr, DATA_1, buf, length); - if ( (length & 1) == 0 ) { - outw( 0, ioaddr + DATA_1 ); - } else { - outb( buf[length -1 ], ioaddr + DATA_1 ); - outb( 0x20, ioaddr + DATA_1); - } + /* Send the last byte, if there is one. */ + if ((length & 1) == 0) + lastword = 0; + else + lastword = 0x2000 | buf[length - 1]; + smc_outw(lastword, ioaddr, DATA_1); /* enable the interrupts */ - SMC_ENABLE_INT( (IM_TX_INT | IM_TX_EMPTY_INT) ); + SMC_ENABLE_INT(IM_TX_INT | IM_TX_EMPTY_INT); /* and let the chipset deal with it */ - outw( MC_ENQUEUE , ioaddr + MMU_CMD ); + smc_outw(MC_ENQUEUE, ioaddr, MMU_CMD); - PRINTK2((CARDNAME": Sent packet of length %d \n",length)); + PRINTK2(("%s: Sent packet of length %d\n", dev->name, length)); lp->saved_skb = NULL; dev_kfree_skb_any (skb); @@ -671,9 +936,56 @@ return; } +#ifdef CONFIG_ARCH_SA1100 +static int smc_enable_device(unsigned long attrib_phys) +{ + unsigned int *addr; + unsigned char ecor; + unsigned long flags; + + /* + * Map the attribute space. This is overkill, but clean. + */ + addr = ioremap(attrib_phys, 64 * 1024 * 4); + if (!addr) + return -ENOMEM; + + /* + * Reset the device. We must disable IRQs around this. + */ + local_irq_save(flags); + ecor = readl(addr + ECOR) & ~ECOR_RESET; + writel(ecor | ECOR_RESET, addr + ECOR); + udelay(100); + + /* + * The device will ignore all writes to the enable bit while + * reset is asserted, even if the reset bit is cleared in the + * same write. Must clear reset first, then enable the device. + */ + writel(ecor, addr + ECOR); + writel(ecor | ECOR_ENABLE, addr + ECOR); + + /* + * Force byte mode. + */ + writel(readl(addr + ECSR) | ECSR_IOIS8, addr + ECSR); + local_irq_restore(flags); + + iounmap(addr); + + /* + * Wait for the chip to wake up. + */ + mdelay(1); + + return 0; +} +#endif + /*------------------------------------------------------------------------- | - | smc_init( struct net_device * dev ) + | smc_init(struct net_device * dev) | Input parameters: | dev->base_addr == 0, try to find all possible locations | dev->base_addr == 1, return failure code @@ -688,6 +1000,53 @@ */ int __init smc_init(struct net_device *dev) { + int ret = -ENODEV; +#if defined(CONFIG_ASSABET_NEPONSET) + + SET_MODULE_OWNER(dev); + + if (machine_has_neponset()) { + const unsigned long base = SA1100_CS3_PHYS; + const unsigned long ext_base = base + (1 << 25); + unsigned int *addr; + + /* + * Map the real registers. + */ + if (!request_mem_region(base, 8 * 1024, dev->name)) + return -EBUSY; + + if (!request_mem_region(ext_base, 64 * 1024 * 4, dev->name)) { + release_mem_region(base, 8 * 1024); + return -EBUSY; + } + + NCR_0 |= NCR_ENET_OSC_EN; + dev->irq = IRQ_NEPONSET_SMC9196; + + ret = smc_enable_device(ext_base); + if (ret) { + release_mem_region(ext_base, 64 * 1024 * 4); + release_mem_region(base, 8 * 1024); + return ret; + } + + addr = ioremap(base, 8 * 1024); + if (!addr) { + release_mem_region(ext_base, 64 * 1024 * 4); + release_mem_region(base, 8 * 1024); + return -ENOMEM; + } + + ret = smc_probe(dev, (int)addr); + if (ret) { + iounmap(addr); + release_mem_region(ext_base, 64 * 1024 * 4); + release_mem_region(base, 8 * 1024); + } + } + +#elif defined(CONFIG_ISA) int i; int base_addr = dev->base_addr; @@ -705,7 +1064,8 @@ return 0; /* couldn't find anything */ - return -ENODEV; +#endif + return ret; } /*---------------------------------------------------------------------- @@ -715,10 +1075,11 @@ . interrupt, so an auto-detect routine can detect it, and find the IRQ, ------------------------------------------------------------------------ */ -int __init smc_findirq( int ioaddr ) +int __init smc_findirq(struct net_device *dev) { int timeout = 20; unsigned long cookie; + u_int ioaddr = dev->base_addr; cookie = probe_irq_on(); @@ -729,26 +1090,25 @@ * when done. */ - + /* enable ALLOCation interrupts ONLY. */ SMC_SELECT_BANK(2); - /* enable ALLOCation interrupts ONLY */ - outb( IM_ALLOC_INT, ioaddr + INT_MASK ); + SMC_SET_INT(IM_ALLOC_INT); /* . Allocate 512 bytes of memory. Note that the chip was just . reset so all the memory is available */ - outw( MC_ALLOC | 1, ioaddr + MMU_CMD ); + smc_outw(MC_ALLOC | 1, ioaddr, MMU_CMD); /* . Wait until positive that the interrupt has been generated */ - while ( timeout ) { + while (timeout) { byte int_status; - int_status = inb( ioaddr + INTERRUPT ); + int_status = smc_inb(ioaddr, INTERRUPT); - if ( int_status & IM_ALLOC_INT ) + if (int_status & IM_ALLOC_INT) break; /* got the interrupt */ timeout--; } @@ -767,14 +1127,93 @@ SMC_DELAY(); /* and disable all interrupts again */ - outb( 0, ioaddr + INT_MASK ); + SMC_SET_INT(0); /* and return what I found */ return probe_irq_off(cookie); } +static int __init smc_probe_chip(struct net_device *dev, int ioaddr) +{ + unsigned int temp; + + /* First, see if the high byte is 0x33 */ + temp = smc_inw(ioaddr, BANK_SELECT); + if ((temp & 0xFF00) != 0x3300) + return -ENODEV; + + /* The above MIGHT indicate a device, but I need to write to further + test this. */ + smc_outw(0, ioaddr, BANK_SELECT); + temp = smc_inw(ioaddr, BANK_SELECT); + if ((temp & 0xFF00) != 0x3300) + return -ENODEV; + +#ifndef CONFIG_ASSABET_NEPONSET + /* well, we've already written once, so hopefully another time won't + hurt. This time, I need to switch the bank register to bank 1, + so I can access the base address register */ + SMC_SELECT_BANK(1); + temp = smc_inw(ioaddr, BASE); + if (ioaddr != (temp >> 3 & 0x3E0)) { + printk("%s: IOADDR %x doesn't match configuration (%x)." + "Probably not a SMC chip\n", dev->name, + ioaddr, (base_address_register >> 3) & 0x3E0); + /* well, the base address register didn't match. Must not have + been a SMC chip after all. */ + return -ENODEV; + } +#endif + + return 0; +} + +/* + . If dev->irq is 0, then the device has to be banged on to see + . what the IRQ is. + . + . This banging doesn't always detect the IRQ, for unknown reasons. + . a workaround is to reset the chip and try again. + . + . Interestingly, the DOS packet driver *SETS* the IRQ on the card to + . be what is requested on the command line. I don't do that, mostly + . because the card that I have uses a non-standard method of accessing + . the IRQs, and because this _should_ work in most configurations. + . + . Specifying an IRQ is done with the assumption that the user knows + . what (s)he is doing. No checking is done!!!! + . +*/ +static int __init smc_probe_irq(struct net_device *dev) +{ + if (dev->irq < 2) { + int trials; + + trials = 3; + while (trials--) { + dev->irq = smc_findirq(dev); + if (dev->irq) + break; + /* kick the card and try again */ + smc_reset(dev); + } + } + if (dev->irq == 0) { + printk("%s: Couldn't autodetect your IRQ. Use irq=xx.\n", + dev->name); + return -ENODEV; + } + + /* + * Some machines (eg, PCs) need to cannonicalize their IRQs. + */ + dev->irq = irq_canonicalize(dev->irq); + + return 0; +} + /*---------------------------------------------------------------------- - . Function: smc_probe( int ioaddr ) + . Function: smc_probe(struct net_device *dev, int ioaddr) . . Purpose: . Tests to see if a given ioaddr points to an SMC9xxx chip. @@ -804,16 +1243,14 @@ */ static int __init smc_probe(struct net_device *dev, int ioaddr) { + struct smc_local *smc; int i, memory, retval; static unsigned version_printed; - unsigned int bank; const char *version_string; - const char *if_string; /* registers */ word revision_register; - word base_address_register; word configuration_register; word memory_info_register; word memory_cfg_register; @@ -822,44 +1259,24 @@ if (!request_region(ioaddr, SMC_IO_EXTENT, dev->name)) return -EBUSY; - /* First, see if the high byte is 0x33 */ - bank = inw( ioaddr + BANK_SELECT ); - if ( (bank & 0xFF00) != 0x3300 ) { - retval = -ENODEV; - goto err_out; - } - /* The above MIGHT indicate a device, but I need to write to further - test this. */ - outw( 0x0, ioaddr + BANK_SELECT ); - bank = inw( ioaddr + BANK_SELECT ); - if ( (bank & 0xFF00 ) != 0x3300 ) { - retval = -ENODEV; - goto err_out; - } - /* well, we've already written once, so hopefully another time won't - hurt. This time, I need to switch the bank register to bank 1, - so I can access the base address register */ - SMC_SELECT_BANK(1); - base_address_register = inw( ioaddr + BASE ); - if ( ioaddr != ( base_address_register >> 3 & 0x3E0 ) ) { - printk(CARDNAME ": IOADDR %x doesn't match configuration (%x)." - "Probably not a SMC chip\n", - ioaddr, base_address_register >> 3 & 0x3E0 ); - /* well, the base address register didn't match. Must not have - been a SMC chip after all. */ - retval = -ENODEV; + /* + * Do the basic probes. + */ + retval = smc_probe_chip(dev, ioaddr); + if (retval) goto err_out; - } /* check if the revision register is something that I recognize. These might need to be added to later, as future revisions could be added. */ SMC_SELECT_BANK(3); - revision_register = inw( ioaddr + REVISION ); - if ( !chip_ids[ ( revision_register >> 4 ) & 0xF ] ) { + revision_register = smc_inw(ioaddr, REVISION); + version_string = chip_ids[(revision_register >> 4) & 15]; + if (!version_string) { /* I don't recognize this chip, so... */ - printk(CARDNAME ": IO %x: Unrecognized revision register:" - " %x, Contact author. \n", ioaddr, revision_register ); + printk("%s: IO %x: unrecognized revision register: %x, " + "contact author.\n", dev->name, ioaddr, + revision_register); retval = -ENODEV; goto err_out; @@ -870,138 +1287,123 @@ against the hardware address, or do some other tests. */ if (version_printed++ == 0) - printk("%s", version); + printk(KERN_INFO "%s", version); /* fill in some of the fields */ dev->base_addr = ioaddr; /* - . Get the MAC address ( bank 1, regs 4 - 9 ) + . Get the MAC address (bank 1, regs 4 - 9) */ - SMC_SELECT_BANK( 1 ); - for ( i = 0; i < 6; i += 2 ) { + SMC_SELECT_BANK(1); + for (i = 0; i < 6; i += 2) { word address; - address = inw( ioaddr + ADDR0 + i ); - dev->dev_addr[ i + 1] = address >> 8; - dev->dev_addr[ i ] = address & 0xFF; + address = smc_inw(ioaddr, ADDR0 + i); + dev->dev_addr[i + 1] = address >> 8; + dev->dev_addr[i] = address & 0xFF; } + if (!is_valid_ether_addr(dev->dev_addr)) + printk("%s: Invalid ethernet MAC address. Please set using " + "ifconfig\n", dev->name); + /* get the memory information */ - SMC_SELECT_BANK( 0 ); - memory_info_register = inw( ioaddr + MIR ); - memory_cfg_register = inw( ioaddr + MCR ); - memory = ( memory_cfg_register >> 9 ) & 0x7; /* multiplier */ - memory *= 256 * ( memory_info_register & 0xFF ); + SMC_SELECT_BANK(0); + memory_info_register = smc_inw(ioaddr, MIR); + memory_cfg_register = smc_inw(ioaddr, MCR); + memory = (memory_cfg_register >> 9) & 0x7; /* multiplier */ + memory *= 256 * (memory_info_register & 0xFF); + + /* now, reset the chip, and put it into a known state */ + smc_reset(dev); /* - Now, I want to find out more about the chip. This is sort of - redundant, but it's cleaner to have it in both, rather than having - one VERY long probe procedure. - */ - SMC_SELECT_BANK(3); - revision_register = inw( ioaddr + REVISION ); - version_string = chip_ids[ ( revision_register >> 4 ) & 0xF ]; - if ( !version_string ) { - /* I shouldn't get here because this call was done before.... */ - retval = -ENODEV; + * Ok, now that we have everything in a + * sane state, probe for the interrupt. + */ + retval = smc_probe_irq(dev); + if (retval) goto err_out; - } - /* is it using AUI or 10BaseT ? */ - if ( dev->if_port == 0 ) { - SMC_SELECT_BANK(1); - configuration_register = inw( ioaddr + CONFIG ); - if ( configuration_register & CFG_AUI_SELECT ) - dev->if_port = 2; - else - dev->if_port = 1; + /* Initialize the private structure. */ + if (dev->priv == NULL) { + dev->priv = kmalloc(sizeof(struct smc_local), GFP_KERNEL); + if (dev->priv == NULL) { + retval = -ENOMEM; + goto err_out; + } } - if_string = interfaces[ dev->if_port - 1 ]; - /* now, reset the chip, and put it into a known state */ - smc_reset( ioaddr ); + smc = dev->priv; + + /* set the private data to zero by default */ + memset(smc, 0, sizeof(struct smc_local)); + spin_lock_init(&smc->lock); /* - . If dev->irq is 0, then the device has to be banged on to see - . what the IRQ is. - . - . This banging doesn't always detect the IRQ, for unknown reasons. - . a workaround is to reset the chip and try again. - . - . Interestingly, the DOS packet driver *SETS* the IRQ on the card to - . be what is requested on the command line. I don't do that, mostly - . because the card that I have uses a non-standard method of accessing - . the IRQs, and because this _should_ work in most configurations. - . - . Specifying an IRQ is done with the assumption that the user knows - . what (s)he is doing. No checking is done!!!! - . - */ - if ( dev->irq < 2 ) { - int trials; + * Get the interface characteristics. + * is it using AUI or 10BaseT ? + */ + switch (dev->if_port) { + case IF_PORT_10BASET: + smc->port = PORT_TP; + break; + + case IF_PORT_AUI: + smc->port = PORT_AUI; + break; - trials = 3; - while ( trials-- ) { - dev->irq = smc_findirq( ioaddr ); - if ( dev->irq ) - break; - /* kick the card and try again */ - smc_reset( ioaddr ); + default: + SMC_SELECT_BANK(1); + configuration_register = smc_inw(ioaddr, CONFIG); + if (configuration_register & CFG_AUI_SELECT) { + dev->if_port = IF_PORT_AUI; + smc->port = PORT_AUI; + } else { + dev->if_port = IF_PORT_10BASET; + smc->port = PORT_TP; } - } - if (dev->irq == 0 ) { - printk(CARDNAME": Couldn't autodetect your IRQ. Use irq=xx.\n"); - retval = -ENODEV; - goto err_out; + break; } - /* now, print out the card info, in a short format.. */ + /* all interfaces are half-duplex by default */ + smc->duplex = DUPLEX_HALF; - printk("%s: %s(r:%d) at %#3x IRQ:%d INTF:%s MEM:%db ", dev->name, - version_string, revision_register & 0xF, ioaddr, dev->irq, - if_string, memory ); + /* now, print out the card info, in a short format.. */ + printk("%s: %s (rev %d) at %#3x IRQ:%d INTF:%s MEM:%db ", dev->name, + version_string, revision_register & 15, ioaddr, dev->irq, + interfaces[smc->port], memory); /* . Print the Ethernet address */ printk("ADDR: "); for (i = 0; i < 5; i++) - printk("%2.2x:", dev->dev_addr[i] ); - printk("%2.2x \n", dev->dev_addr[5] ); - - - /* Initialize the private structure. */ - if (dev->priv == NULL) { - dev->priv = kmalloc(sizeof(struct smc_local), GFP_KERNEL); - if (dev->priv == NULL) { - retval = -ENOMEM; - goto err_out; - } - } - /* set the private data to zero by default */ - memset(dev->priv, 0, sizeof(struct smc_local)); + printk("%2.2x:", dev->dev_addr[i]); + printk("%2.2x\n", dev->dev_addr[5]); /* Fill in the fields of the device structure with ethernet values. */ ether_setup(dev); /* Grab the IRQ */ - retval = request_irq(dev->irq, &smc_interrupt, 0, dev->name, dev); - if (retval) { + retval = request_irq(dev->irq, &smc_interrupt, 0, dev->name, dev); + if (retval) { printk("%s: unable to get IRQ %d (irqval=%d).\n", dev->name, dev->irq, retval); kfree(dev->priv); dev->priv = NULL; - goto err_out; - } + goto err_out; + } - dev->open = smc_open; - dev->stop = smc_close; - dev->hard_start_xmit = smc_wait_to_send_packet; - dev->tx_timeout = smc_timeout; - dev->watchdog_timeo = HZ/20; - dev->get_stats = smc_query_statistics; - dev->set_multicast_list = smc_set_multicast_list; + dev->open = smc_open; + dev->stop = smc_close; + dev->hard_start_xmit = smc_wait_to_send_packet; + dev->tx_timeout = smc_timeout; + dev->watchdog_timeo = HZ/20; + dev->get_stats = smc_query_statistics; + dev->set_multicast_list = smc_set_multicast_list; + dev->do_ioctl = smc_ioctl; return 0; @@ -1010,42 +1412,45 @@ return retval; } -#if SMC_DEBUG > 2 -static void print_packet( byte * buf, int length ) +/* + * This is responsible for setting the chip appropriately + * for the interface type. This should only be called while + * the interface is up and running. + */ +static void smc_set_port(struct net_device *dev) { -#if 0 - int i; - int remainder; - int lines; + struct smc_local *smc = dev->priv; + u_int ioaddr = dev->base_addr; + u_int val; - printk("Packet of length %d \n", length ); - lines = length / 16; - remainder = length % 16; - - for ( i = 0; i < lines ; i ++ ) { - int cur; - - for ( cur = 0; cur < 8; cur ++ ) { - byte a, b; - - a = *(buf ++ ); - b = *(buf ++ ); - printk("%02x%02x ", a, b ); - } - printk("\n"); + spin_lock_irq(&smc->lock); + SMC_SELECT_BANK(1); + val = smc_inw(ioaddr, CONFIG); + switch (smc->port) { + case PORT_TP: + val &= ~CFG_AUI_SELECT; + break; + + case PORT_AUI: + val |= CFG_AUI_SELECT; + break; } - for ( i = 0; i < remainder/2 ; i++ ) { - byte a, b; + smc_outw(val, ioaddr, CONFIG); - a = *(buf ++ ); - b = *(buf ++ ); - printk("%02x%02x ", a, b ); + SMC_SELECT_BANK(0); + val = smc_inw(ioaddr, TCR); + switch (smc->duplex) { + case DUPLEX_HALF: + val &= ~TCR_FDSE; + break; + + case DUPLEX_FULL: + val |= TCR_FDSE; + break; } - printk("\n"); -#endif + smc_outw(val, ioaddr, TCR); + spin_unlock_irq(&smc->lock); } -#endif - /* * Open and Initialize the board @@ -1055,48 +1460,222 @@ */ static int smc_open(struct net_device *dev) { - int ioaddr = dev->base_addr; + struct smc_local *smc = dev->priv; + u_int ioaddr = dev->base_addr; + int i; - int i; /* used to set hw ethernet address */ + /* + * Check that the address is valid. If its not, refuse + * to bring the device up. The user must specify an + * address using ifconfig eth0 hw ether xx:xx:xx:xx:xx:xx + */ + if (!is_valid_ether_addr(dev->dev_addr)) { + PRINTK2((KERN_DEBUG "smc_open: no valid ethernet hw addr\n")); + return -EINVAL; + } /* clear out all the junk that was put here before... */ - memset(dev->priv, 0, sizeof(struct smc_local)); + smc->saved_skb = NULL; + smc->packets_waiting = 0; /* reset the hardware */ - - smc_reset( ioaddr ); - smc_enable( ioaddr ); + smc_reset(dev); + smc_enable(dev); /* Select which interface to use */ - - SMC_SELECT_BANK( 1 ); - if ( dev->if_port == 1 ) { - outw( inw( ioaddr + CONFIG ) & ~CFG_AUI_SELECT, - ioaddr + CONFIG ); - } - else if ( dev->if_port == 2 ) { - outw( inw( ioaddr + CONFIG ) | CFG_AUI_SELECT, - ioaddr + CONFIG ); - } + smc_set_port(dev); /* - According to Becker, I have to set the hardware address + According to Becker, I have to set the hardware address at this point, because the (l)user can set it with an ioctl. Easily done... */ - SMC_SELECT_BANK( 1 ); - for ( i = 0; i < 6; i += 2 ) { + + spin_lock_irq(&smc->lock); + SMC_SELECT_BANK(1); + for (i = 0; i < 6; i += 2) { word address; - address = dev->dev_addr[ i + 1 ] << 8 ; - address |= dev->dev_addr[ i ]; - outw( address, ioaddr + ADDR0 + i ); + address = dev->dev_addr[i + 1] << 8 ; + address |= dev->dev_addr[i]; + smc_outw(address, ioaddr, ADDR0 + i); } + spin_unlock_irq(&smc->lock); netif_start_queue(dev); return 0; } +#define EEPROM_SIZE 128 + +static void +smc_read_eeprom(struct net_device *dev, unsigned char *ptr, int words, int offset) +{ + struct smc_local *smc = dev->priv; + u_int ioaddr = dev->base_addr; + u_int ctl; + + spin_lock_irq(&smc->lock); + SMC_SELECT_BANK(1); + + ctl = smc_inw(ioaddr, CONTROL) & ~CTL_EPROM_ACCESS; + ctl |= CTL_EPROM_SELECT; + smc_outw(ctl, ioaddr, CONTROL); + + while (words > 0) { + u_int word; + + SMC_SELECT_BANK(2); + smc_outw(offset, ioaddr, POINTER); + SMC_SELECT_BANK(1); + smc_outw(ctl|CTL_EPROM_RELOAD, ioaddr, CONTROL); + + /* + * Wait a short while for the store to take effect. + */ + do { + spin_unlock_irq(&smc->lock); + + udelay(50); + + spin_lock_irq(&smc->lock); + SMC_SELECT_BANK(1); + } while ((smc_inw(ioaddr, CONTROL) & CTL_EPROM_ACCESS) != 0); + word = smc_inw(ioaddr, GENERAL); + + *ptr++ = word; + if (words > 1) + *ptr++ = word >> 8; + words -= 2; + offset ++; + } + + smc_outw(ctl & ~CTL_EPROM_SELECT, ioaddr, CONTROL); + spin_unlock_irq(&smc->lock); +} + +/* + * This is our template. Fill the rest in at run-time + */ +static const struct ethtool_cmd ecmd_template = { + .supported = SUPPORTED_10baseT_Half | + SUPPORTED_10baseT_Full | + SUPPORTED_TP | + SUPPORTED_AUI, + .speed = SPEED_10, + .autoneg = AUTONEG_DISABLE, + .maxtxpkt = 1, + .maxrxpkt = 1, + .transceiver = XCVR_INTERNAL, +}; + +static int smc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct smc_local *smc = dev->priv; + u32 etcmd; + int ret = -EINVAL; + + if (cmd != SIOCETHTOOL) + return -EOPNOTSUPP; + + if (get_user(etcmd, (u32 *)rq->ifr_data)) + return -EFAULT; + + switch (etcmd) { + case ETHTOOL_GSET: { + struct ethtool_cmd ecmd = ecmd_template; + + ecmd.cmd = etcmd; + ecmd.port = smc->port; + ecmd.duplex = smc->duplex; + + ret = copy_to_user(rq->ifr_data, &ecmd, sizeof(ecmd)) + ? -EFAULT : 0; + break; + } + + case ETHTOOL_SSET: { + struct ethtool_cmd ecmd; + + ret = -EPERM; + if (!capable(CAP_NET_ADMIN)) + break; + + ret = -EFAULT; + if (copy_from_user(&ecmd, rq->ifr_data, sizeof(ecmd))) + break; + + /* + * Sanity-check the arguments. + */ + ret = -EINVAL; + if (ecmd.autoneg != AUTONEG_DISABLE) + break; + if (ecmd.speed != SPEED_10) + break; + if (ecmd.duplex != DUPLEX_HALF && ecmd.duplex != DUPLEX_FULL) + break; + if (ecmd.port != PORT_TP && ecmd.port != PORT_AUI) + break; + + smc->port = ecmd.port; + smc->duplex = ecmd.duplex; + + if (netif_running(dev)) + smc_set_port(dev); + + ret = 0; + break; + } + + case ETHTOOL_GDRVINFO: { + struct ethtool_drvinfo edrv; + + memset(&edrv, 0, sizeof(edrv)); + + edrv.cmd = etcmd; + strcpy(edrv.driver, DRV_NAME); + strcpy(edrv.version, DRV_VERSION); + sprintf(edrv.bus_info, "ISA:%8.8lx:%d", + dev->base_addr, dev->irq); + edrv.eedump_len = EEPROM_SIZE; + + ret = copy_to_user(rq->ifr_data, &edrv, sizeof(edrv)) + ? -EFAULT : 0; + break; + } + + case ETHTOOL_GEEPROM: { + struct ethtool_eeprom eeprom; + void *eedata; + + if (copy_from_user(&eeprom, rq->ifr_data, sizeof(eeprom))) { + ret = -EFAULT; + break; + } + if (eeprom.len > EEPROM_SIZE || eeprom.magic != 0 || + (eeprom.offset + eeprom.len) > EEPROM_SIZE) { + ret = -EINVAL; + break; + } + eedata = kmalloc(eeprom.len, GFP_KERNEL); + if (!eedata) { + ret = -ENOMEM; + break; + } + smc_read_eeprom(dev, eedata, eeprom.len, eeprom.offset); + ret = 0; + if (copy_to_user(rq->ifr_data + offsetof(struct ethtool_eeprom, data), + eedata, eeprom.len)) + ret = -EFAULT; + kfree(eedata); + break; + } + } + + return ret; +} + /*-------------------------------------------------------- . Called by the kernel to send a packet out into the void . of the net. This routine is largely based on @@ -1108,12 +1687,10 @@ { /* If we get here, some higher level has decided we are broken. There should really be a "kick me" function call instead. */ - printk(KERN_WARNING CARDNAME": transmit timed out, %s?\n", - tx_done(dev) ? "IRQ conflict" : - "network cable problem"); + printk(KERN_WARNING "%s: transmit timed out\n", dev->name); /* "kick" the adaptor */ - smc_reset( dev->base_addr ); - smc_enable( dev->base_addr ); + smc_reset(dev); + smc_enable(dev); dev->trans_start = jiffies; /* clear anything saved */ ((struct smc_local *)dev->priv)->saved_skb = NULL; @@ -1133,10 +1710,10 @@ . ---------------------------------------------------------------------*/ -static irqreturn_t smc_interrupt(int irq, void * dev_id, struct pt_regs * regs) +static irqreturn_t smc_interrupt(int irq, void * dev_id, struct pt_regs * regs) { struct net_device *dev = dev_id; - int ioaddr = dev->base_addr; + u_int ioaddr = dev->base_addr; struct smc_local *lp = (struct smc_local *)dev->priv; byte status; @@ -1149,47 +1726,47 @@ int handled = 0; - PRINTK3((CARDNAME": SMC interrupt started \n")); + PRINTK3(("%s: SMC interrupt started\n", dev->name)); - saved_bank = inw( ioaddr + BANK_SELECT ); + saved_bank = smc_inw(ioaddr, BANK_SELECT); SMC_SELECT_BANK(2); - saved_pointer = inw( ioaddr + POINTER ); + saved_pointer = smc_inw(ioaddr, POINTER); - mask = inb( ioaddr + INT_MASK ); + mask = smc_inb(ioaddr, INT_MASK); /* clear all interrupts */ - outb( 0, ioaddr + INT_MASK ); + SMC_SET_INT(0); /* set a timeout value, so I don't stay here forever */ timeout = 4; - PRINTK2((KERN_WARNING CARDNAME ": MASK IS %x \n", mask )); + PRINTK2((KERN_WARNING "%s: MASK IS %x\n", dev->name, mask)); do { /* read the status flag, and mask it */ - status = inb( ioaddr + INTERRUPT ) & mask; - if (!status ) + status = smc_inb(ioaddr, INTERRUPT) & mask; + if (!status) break; handled = 1; - PRINTK3((KERN_WARNING CARDNAME - ": Handling interrupt status %x \n", status )); + PRINTK3((KERN_WARNING "%s: handling interrupt status %x\n", + dev->name, status)); if (status & IM_RCV_INT) { /* Got a packet(s). */ - PRINTK2((KERN_WARNING CARDNAME - ": Receive Interrupt\n")); + PRINTK2((KERN_WARNING "%s: receive interrupt\n", + dev->name)); smc_rcv(dev); - } else if (status & IM_TX_INT ) { - PRINTK2((KERN_WARNING CARDNAME - ": TX ERROR handled\n")); + } else if (status & IM_TX_INT) { + PRINTK2((KERN_WARNING "%s: TX ERROR handled\n", + dev->name)); smc_tx(dev); - outb(IM_TX_INT, ioaddr + INTERRUPT ); - } else if (status & IM_TX_EMPTY_INT ) { + smc_outb(IM_TX_INT, ioaddr, INTERRUPT); + } else if (status & IM_TX_EMPTY_INT) { /* update stats */ - SMC_SELECT_BANK( 0 ); - card_stats = inw( ioaddr + COUNTER ); + SMC_SELECT_BANK(0); + card_stats = smc_inw(ioaddr, COUNTER); /* single collisions */ lp->stats.collisions += card_stats & 0xF; card_stats >>= 4; @@ -1198,60 +1775,63 @@ /* these are for when linux supports these statistics */ - SMC_SELECT_BANK( 2 ); - PRINTK2((KERN_WARNING CARDNAME - ": TX_BUFFER_EMPTY handled\n")); - outb( IM_TX_EMPTY_INT, ioaddr + INTERRUPT ); + SMC_SELECT_BANK(2); + PRINTK2((KERN_WARNING "%s: TX_BUFFER_EMPTY handled\n", + dev->name)); + smc_outb(IM_TX_EMPTY_INT, ioaddr, INTERRUPT); mask &= ~IM_TX_EMPTY_INT; lp->stats.tx_packets += lp->packets_waiting; lp->packets_waiting = 0; - } else if (status & IM_ALLOC_INT ) { - PRINTK2((KERN_DEBUG CARDNAME - ": Allocation interrupt \n")); + } else if (status & IM_ALLOC_INT) { + PRINTK2((KERN_DEBUG "%s: Allocation interrupt\n", + dev->name)); /* clear this interrupt so it doesn't happen again */ mask &= ~IM_ALLOC_INT; - smc_hardware_send_packet( dev ); + smc_hardware_send_packet(dev); /* enable xmit interrupts based on this */ - mask |= ( IM_TX_EMPTY_INT | IM_TX_INT ); + mask |= (IM_TX_EMPTY_INT | IM_TX_INT); /* and let the card send more packets to me */ netif_wake_queue(dev); - PRINTK2((CARDNAME": Handoff done successfully.\n")); - } else if (status & IM_RX_OVRN_INT ) { + PRINTK2(("%s: Handoff done successfully.\n", + dev->name)); + } else if (status & IM_RX_OVRN_INT) { lp->stats.rx_errors++; lp->stats.rx_fifo_errors++; - outb( IM_RX_OVRN_INT, ioaddr + INTERRUPT ); - } else if (status & IM_EPH_INT ) { - PRINTK((CARDNAME ": UNSUPPORTED: EPH INTERRUPT \n")); - } else if (status & IM_ERCV_INT ) { - PRINTK((CARDNAME ": UNSUPPORTED: ERCV INTERRUPT \n")); - outb( IM_ERCV_INT, ioaddr + INTERRUPT ); + smc_outb(IM_RX_OVRN_INT, ioaddr, INTERRUPT); + } else if (status & IM_EPH_INT) { + PRINTK(("%s: UNSUPPORTED: EPH INTERRUPT\n", + dev->name)); + } else if (status & IM_ERCV_INT) { + PRINTK(("%s: UNSUPPORTED: ERCV INTERRUPT\n", + dev->name)); + smc_outb(IM_ERCV_INT, ioaddr, INTERRUPT); } - } while ( timeout -- ); + } while (timeout --); /* restore state register */ - SMC_SELECT_BANK( 2 ); - outb( mask, ioaddr + INT_MASK ); + SMC_SELECT_BANK(2); + SMC_SET_INT(mask); - PRINTK3(( KERN_WARNING CARDNAME ": MASK is now %x \n", mask )); - outw( saved_pointer, ioaddr + POINTER ); + PRINTK3((KERN_WARNING "%s: MASK is now %x\n", dev->name, mask)); + smc_outw(saved_pointer, ioaddr, POINTER); - SMC_SELECT_BANK( saved_bank ); + SMC_SELECT_BANK(saved_bank); - PRINTK3((CARDNAME ": Interrupt done\n")); + PRINTK3(("%s: Interrupt done\n", dev->name)); return IRQ_RETVAL(handled); } /*------------------------------------------------------------- . - . smc_rcv - receive a packet from the card + . smc_rcv - receive a packet from the card . - . There is ( at least ) a packet waiting to be read from + . There is (at least) a packet waiting to be read from . chip-memory. . . o Read the status @@ -1262,55 +1842,57 @@ static void smc_rcv(struct net_device *dev) { struct smc_local *lp = (struct smc_local *)dev->priv; - int ioaddr = dev->base_addr; + u_int ioaddr = dev->base_addr; int packet_number; word status; word packet_length; /* assume bank 2 */ - packet_number = inw( ioaddr + FIFO_PORTS ); + packet_number = smc_inw(ioaddr, FIFO_PORTS); - if ( packet_number & FP_RXEMPTY ) { + if (packet_number & FP_RXEMPTY) { /* we got called , but nothing was on the FIFO */ - PRINTK((CARDNAME ": WARNING: smc_rcv with nothing on FIFO. \n")); + PRINTK(("%s: WARNING: smc_rcv with nothing on FIFO.\n", + dev->name)); /* don't need to restore anything */ return; } /* start reading from the start of the packet */ - outw( PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER ); + smc_outw(PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr, POINTER); /* First two words are status and packet_length */ - status = inw( ioaddr + DATA_1 ); - packet_length = inw( ioaddr + DATA_1 ); + status = smc_inw(ioaddr, DATA_1); + packet_length = smc_inw(ioaddr, DATA_1); packet_length &= 0x07ff; /* mask off top bits */ - PRINTK2(("RCV: STATUS %4x LENGTH %4x\n", status, packet_length )); + PRINTK2(("RCV: STATUS %4x LENGTH %4x\n", status, packet_length)); /* . the packet length contains 3 extra words : . status, length, and an extra word with an odd byte . */ packet_length -= 6; - if ( !(status & RS_ERRORS ) ){ + if (!(status & RS_ERRORS)){ /* do stuff to make a new packet */ struct sk_buff * skb; byte * data; /* read one extra byte */ - if ( status & RS_ODDFRAME ) + if (status & RS_ODDFRAME) packet_length++; /* set multicast stats */ - if ( status & RS_MULTICAST ) + if (status & RS_MULTICAST) lp->stats.multicast++; - skb = dev_alloc_skb( packet_length + 5); + skb = dev_alloc_skb(packet_length + 5); - if ( skb == NULL ) { - printk(KERN_NOTICE CARDNAME ": Low memory, packet dropped.\n"); + if (skb == NULL) { + printk(KERN_NOTICE "%s: Low memory, packet dropped.\n", + dev->name); lp->stats.rx_dropped++; goto done; } @@ -1320,36 +1902,15 @@ ! in the worse case */ - skb_reserve( skb, 2 ); /* 16 bit alignment */ + skb_reserve(skb, 2); /* 16 bit alignment */ skb->dev = dev; - data = skb_put( skb, packet_length); + data = skb_put(skb, packet_length); -#ifdef USE_32_BIT - /* QUESTION: Like in the TX routine, do I want - to send the DWORDs or the bytes first, or some - mixture. A mixture might improve already slow PIO - performance */ - PRINTK3((" Reading %d dwords (and %d bytes) \n", - packet_length >> 2, packet_length & 3 )); - insl(ioaddr + DATA_1 , data, packet_length >> 2 ); - /* read the left over bytes */ - insb( ioaddr + DATA_1, data + (packet_length & 0xFFFFFC), - packet_length & 0x3 ); -#else - PRINTK3((" Reading %d words and %d byte(s) \n", - (packet_length >> 1 ), packet_length & 1 )); - insw(ioaddr + DATA_1 , data, packet_length >> 1); - if ( packet_length & 1 ) { - data += packet_length & ~1; - *(data++) = inb( ioaddr + DATA_1 ); - } -#endif -#if SMC_DEBUG > 2 - print_packet( data, packet_length ); -#endif + smc_ins(ioaddr, DATA_1, data, packet_length); + print_packet(data, packet_length); - skb->protocol = eth_type_trans(skb, dev ); + skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); dev->last_rx = jiffies; lp->stats.rx_packets++; @@ -1358,15 +1919,17 @@ /* error ... */ lp->stats.rx_errors++; - if ( status & RS_ALGNERR ) lp->stats.rx_frame_errors++; - if ( status & (RS_TOOSHORT | RS_TOOLONG ) ) + if (status & RS_ALGNERR) + lp->stats.rx_frame_errors++; + if (status & (RS_TOOSHORT | RS_TOOLONG)) lp->stats.rx_length_errors++; - if ( status & RS_BADCRC) lp->stats.rx_crc_errors++; + if (status & RS_BADCRC) + lp->stats.rx_crc_errors++; } done: /* error or good, tell the card to get rid of this packet */ - outw( MC_RELEASE, ioaddr + MMU_CMD ); + smc_outw(MC_RELEASE, ioaddr, MMU_CMD); } @@ -1379,62 +1942,64 @@ . Algorithm: . Save pointer and packet no . Get the packet no from the top of the queue - . check if it's valid ( if not, is this an error??? ) + . check if it's valid (if not, is this an error???) . read the status word . record the error - . ( resend? Not really, since we don't want old packets around ) + . (resend? Not really, since we don't want old packets around) . Restore saved values ************************************************************************/ -static void smc_tx( struct net_device * dev ) +static void smc_tx(struct net_device * dev) { - int ioaddr = dev->base_addr; + u_int ioaddr = dev->base_addr; struct smc_local *lp = (struct smc_local *)dev->priv; byte saved_packet; byte packet_no; word tx_status; - /* assume bank 2 */ + /* assume bank 2 */ - saved_packet = inb( ioaddr + PNR_ARR ); - packet_no = inw( ioaddr + FIFO_PORTS ); + saved_packet = smc_inb(ioaddr, PNR_ARR); + packet_no = smc_inw(ioaddr, FIFO_PORTS); packet_no &= 0x7F; /* select this as the packet to read from */ - outb( packet_no, ioaddr + PNR_ARR ); + smc_outb(packet_no, ioaddr, PNR_ARR); /* read the first word from this packet */ - outw( PTR_AUTOINC | PTR_READ, ioaddr + POINTER ); + smc_outw(PTR_AUTOINC | PTR_READ, ioaddr, POINTER); - tx_status = inw( ioaddr + DATA_1 ); - PRINTK3((CARDNAME": TX DONE STATUS: %4x \n", tx_status )); + tx_status = smc_inw(ioaddr, DATA_1); + PRINTK3(("%s: TX DONE STATUS: %4x\n", dev->name, tx_status)); lp->stats.tx_errors++; - if ( tx_status & TS_LOSTCAR ) lp->stats.tx_carrier_errors++; - if ( tx_status & TS_LATCOL ) { - printk(KERN_DEBUG CARDNAME - ": Late collision occurred on last xmit.\n"); + if (tx_status & TS_LOSTCAR) + lp->stats.tx_carrier_errors++; + if (tx_status & TS_LATCOL) { + printk(KERN_DEBUG "%s: Late collision occurred on " + "last xmit.\n", dev->name); lp->stats.tx_window_errors++; } #if 0 - if ( tx_status & TS_16COL ) { ... } + if (tx_status & TS_16COL) { ... } #endif - if ( tx_status & TS_SUCCESS ) { - printk(CARDNAME": Successful packet caused interrupt \n"); + if (tx_status & TS_SUCCESS) { + printk("%s: Successful packet caused interrupt\n", + dev->name); } /* re-enable transmit */ - SMC_SELECT_BANK( 0 ); - outw( inw( ioaddr + TCR ) | TCR_ENABLE, ioaddr + TCR ); + SMC_SELECT_BANK(0); + smc_outw(smc_inw(ioaddr, TCR) | TCR_ENABLE, ioaddr, TCR); /* kill the packet */ - SMC_SELECT_BANK( 2 ); - outw( MC_FREEPKT, ioaddr + MMU_CMD ); + SMC_SELECT_BANK(2); + smc_outw(MC_FREEPKT, ioaddr, MMU_CMD); /* one less packet waiting for me */ lp->packets_waiting--; - outb( saved_packet, ioaddr + PNR_ARR ); + smc_outb(saved_packet, ioaddr, PNR_ARR); return; } @@ -1450,7 +2015,7 @@ { netif_stop_queue(dev); /* clear everything */ - smc_shutdown( dev->base_addr ); + smc_shutdown(dev); /* Update the statistics here. */ return 0; @@ -1471,16 +2036,16 @@ . . This routine will, depending on the values passed to it, . either make it accept multicast packets, go into - . promiscuous mode ( for TCPDUMP and cousins ) or accept + . promiscuous mode (for TCPDUMP and cousins) or accept . a select set of multicast packets */ static void smc_set_multicast_list(struct net_device *dev) { - short ioaddr = dev->base_addr; + u_int ioaddr = dev->base_addr; SMC_SELECT_BANK(0); - if ( dev->flags & IFF_PROMISC ) - outw( inw(ioaddr + RCR ) | RCR_PROMISC, ioaddr + RCR ); + if (dev->flags & IFF_PROMISC) + smc_outw(smc_inw(ioaddr, RCR) | RCR_PROMISC, ioaddr, RCR); /* BUG? I never disable promiscuous mode if multicasting was turned on. Now, I turn off promiscuous mode, but I don't do anything to multicasting @@ -1492,34 +2057,34 @@ checked before the table is */ else if (dev->flags & IFF_ALLMULTI) - outw( inw(ioaddr + RCR ) | RCR_ALMUL, ioaddr + RCR ); + smc_outw(smc_inw(ioaddr, RCR) | RCR_ALMUL, ioaddr, RCR); /* We just get all multicast packets even if we only want them . from one source. This will be changed at some future . point. */ - else if (dev->mc_count ) { + else if (dev->mc_count) { /* support hardware multicasting */ /* be sure I get rid of flags I might have set */ - outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL), - ioaddr + RCR ); + smc_outw(smc_inw(ioaddr, RCR) & ~(RCR_PROMISC | RCR_ALMUL), + ioaddr, RCR); /* NOTE: this has to set the bank, so make sure it is the last thing called. The bank is set to zero at the top */ - smc_setmulticast( ioaddr, dev->mc_count, dev->mc_list ); + smc_setmulticast(dev, dev->mc_count, dev->mc_list); } - else { - outw( inw( ioaddr + RCR ) & ~(RCR_PROMISC | RCR_ALMUL), - ioaddr + RCR ); + else { + smc_outw(smc_inw(ioaddr, RCR) & ~(RCR_PROMISC | RCR_ALMUL), + ioaddr, RCR); /* since I'm disabling all multicast entirely, I need to clear the multicast list */ - SMC_SELECT_BANK( 3 ); - outw( 0, ioaddr + MULTICAST1 ); - outw( 0, ioaddr + MULTICAST2 ); - outw( 0, ioaddr + MULTICAST3 ); - outw( 0, ioaddr + MULTICAST4 ); + SMC_SELECT_BANK(3); + smc_outw(0, ioaddr, MULTICAST1); + smc_outw(0, ioaddr, MULTICAST2); + smc_outw(0, ioaddr, MULTICAST3); + smc_outw(0, ioaddr, MULTICAST4); } } @@ -1540,21 +2105,26 @@ int init_module(void) { - int result; - if (io == 0) - printk(KERN_WARNING - CARDNAME": You shouldn't use auto-probing with insmod!\n" ); + printk(KERN_WARNING CARDNAME + ": You shouldn't use auto-probing with insmod!\n"); + + /* + * Note: dev->if_port has changed to be 2.4 compliant. + * We keep the ifport insmod parameter the same though. + */ + switch (ifport) { + case 1: devSMC9194.if_port = IF_PORT_10BASET; break; + case 2: devSMC9194.if_port = IF_PORT_AUI; break; + default: devSMC9194.if_port = 0; break; + } /* copy the parameters from insmod into the device structure */ devSMC9194.base_addr = io; devSMC9194.irq = irq; - devSMC9194.if_port = ifport; - devSMC9194.init = smc_init; - if ((result = register_netdev(&devSMC9194)) != 0) - return result; + devSMC9194.init = smc_init; - return 0; + return register_netdev(&devSMC9194); } void cleanup_module(void) diff -urN orig/drivers/net/smc9194.h linux/drivers/net/smc9194.h --- orig/drivers/net/smc9194.h Mon Oct 1 23:10:57 2001 +++ linux/drivers/net/smc9194.h Fri Feb 21 18:09:46 2003 @@ -63,10 +63,11 @@ #define TCR 0 /* transmit control register */ #define TCR_ENABLE 0x0001 /* if this is 1, we can transmit */ +#define TCR_PAD_ENABLE 0x0080 /* pads short packets to 64 bytes */ +#define TCR_MON_CNS 0x0400 /* monitors the carrier status */ #define TCR_FDUPLX 0x0800 /* receive packets sent out */ #define TCR_STP_SQET 0x1000 /* stop transmitting if Signal quality error */ -#define TCR_MON_CNS 0x0400 /* monitors the carrier status */ -#define TCR_PAD_ENABLE 0x0080 /* pads short packets to 64 bytes */ +#define TCR_FDSE 0x8000 /* full duplex, switched ethernet */ #define TCR_CLEAR 0 /* do NOTHING */ /* the normal settings for the TCR register : */ @@ -107,7 +108,10 @@ #define CTL_CR_ENABLE 0x40 #define CTL_TE_ENABLE 0x0020 #define CTL_AUTO_RELEASE 0x0800 -#define CTL_EPROM_ACCESS 0x0003 /* high if Eprom is being read */ +#define CTL_EPROM_SELECT 0x0004 +#define CTL_EPROM_RELOAD 0x0002 +#define CTL_EPROM_STORE 0x0001 +#define CTL_EPROM_ACCESS (CTL_EPROM_RELOAD | CTL_EPROM_STORE) /* high if Eprom is being read */ /* BANK 2 */ #define MMU_CMD 0 @@ -130,7 +134,6 @@ #define PTR_READ 0x2000 #define PTR_RCV 0x8000 #define PTR_AUTOINC 0x4000 -#define PTR_AUTO_INC 0x0040 #define DATA_1 8 #define DATA_2 10 @@ -162,17 +165,6 @@ #define CHIP_9195 5 #define CHIP_91100 7 -static const char * chip_ids[ 15 ] = { - NULL, NULL, NULL, - /* 3 */ "SMC91C90/91C92", - /* 4 */ "SMC91C94", - /* 5 */ "SMC91C95", - NULL, - /* 7 */ "SMC91C100", - /* 8 */ "SMC91C100FD", - NULL, NULL, NULL, - NULL, NULL, NULL}; - /* . Transmit status bits */ @@ -192,40 +184,20 @@ #define RS_MULTICAST 0x0001 #define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT) -static const char * interfaces[ 2 ] = { "TP", "AUI" }; - -/*------------------------------------------------------------------------- - . I define some macros to make it easier to do somewhat common - . or slightly complicated, repeated tasks. - --------------------------------------------------------------------------*/ - -/* select a register bank, 0 to 3 */ - -#define SMC_SELECT_BANK(x) { outw( x, ioaddr + BANK_SELECT ); } - -/* define a small delay for the reset */ -#define SMC_DELAY() { inw( ioaddr + RCR );\ - inw( ioaddr + RCR );\ - inw( ioaddr + RCR ); } - -/* this enables an interrupt in the interrupt mask register */ -#define SMC_ENABLE_INT(x) {\ - unsigned char mask;\ - SMC_SELECT_BANK(2);\ - mask = inb( ioaddr + INT_MASK );\ - mask |= (x);\ - outb( mask, ioaddr + INT_MASK ); \ -} - -/* this disables an interrupt from the interrupt mask register */ - -#define SMC_DISABLE_INT(x) {\ - unsigned char mask;\ - SMC_SELECT_BANK(2);\ - mask = inb( ioaddr + INT_MASK );\ - mask &= ~(x);\ - outb( mask, ioaddr + INT_MASK ); \ -} +/* + * SMC91C96 ethernet config and status registers. + * These are in the "attribute" space. + */ +#define ECOR 0x8000 +#define ECOR_RESET 0x80 +#define ECOR_LEVEL_IRQ 0x40 +#define ECOR_WR_ATTRIB 0x04 +#define ECOR_ENABLE 0x01 + +#define ECSR 0x8002 +#define ECSR_IOIS8 0x20 +#define ECSR_PWRDWN 0x04 +#define ECSR_INT 0x02 /*---------------------------------------------------------------------- . Define the interrupts that I want to receive from the card @@ -237,5 +209,38 @@ --------------------------------------------------------------------------*/ #define SMC_INTERRUPT_MASK (IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT) +/* store this information for the driver.. */ +struct smc_local { + spinlock_t lock; + + /* + these are things that the kernel wants me to keep, so users + can find out semi-useless statistics of how well the card is + performing + */ + struct net_device_stats stats; + + /* + If I have to wait until memory is available to send + a packet, I will store the skbuff here, until I get the + desired memory. Then, I'll send it out and free it. + */ + struct sk_buff * saved_skb; + + /* + . This keeps track of how many packets that I have + . sent out. When an TX_EMPTY interrupt comes, I know + . that all of these have been sent. + */ + int packets_waiting; + + /* + . Interface status. These correspond to the parameters + . in the ethtool_cmd structure. + */ + u8 duplex; + u8 port; +}; + #endif /* _SMC_9194_H_ */ diff -urN orig/drivers/parport/parport_cs.c linux/drivers/parport/parport_cs.c --- orig/drivers/parport/parport_cs.c Mon May 5 17:39:26 2003 +++ linux/drivers/parport/parport_cs.c Sat May 17 22:53:23 2003 @@ -390,28 +390,27 @@ return 0; } /* parport_event */ -/*====================================================================*/ +static struct pcmcia_driver parport_cs_driver = { + .owner = THIS_MODULE, + .drv = { + .name = "parport_cs", + }, + .attach = parport_attach, + .detach = parport_detach, +}; static int __init init_parport_cs(void) { - servinfo_t serv; - DEBUG(0, "%s\n", version); - CardServices(GetCardServicesInfo, &serv); - if (serv.Revision != CS_RELEASE_CODE) { - printk(KERN_NOTICE "parport_cs: Card Services release " - "does not match!\n"); - return -EINVAL; - } - register_pccard_driver(&dev_info, &parport_attach, &parport_detach); - return 0; + return pcmcia_register_driver(&parport_cs_driver); } static void __exit exit_parport_cs(void) { - DEBUG(0, "parport_cs: unloading\n"); - unregister_pccard_driver(&dev_info); - while (dev_list != NULL) - parport_detach(dev_list); + pcmcia_unregister_driver(&parport_cs_driver); + + /* XXX: this really needs to move into generic code.. */ + while (dev_list != NULL) + parport_detach(dev_list); } module_init(init_parport_cs); diff -urN orig/drivers/pci/probe.c linux/drivers/pci/probe.c --- orig/drivers/pci/probe.c Tue May 27 10:04:54 2003 +++ linux/drivers/pci/probe.c Tue May 27 10:12:17 2003 @@ -316,6 +316,10 @@ child->secondary = (buses >> 8) & 0xFF; child->subordinate = (buses >> 16) & 0xFF; child->number = child->secondary; + + pci_read_config_word(dev, PCI_BRIDGE_CONTROL, + &child->bridge_ctl); + cmax = pci_scan_child_bus(child); if (cmax > max) max = cmax; } else { @@ -350,6 +354,8 @@ pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses); if (!is_cardbus) { + child->bridge_ctl = PCI_BRIDGE_CTL_NO_ISA; + /* Now we can scan all subordinate buses... */ max = pci_scan_child_bus(child); } else { @@ -396,7 +402,7 @@ * Returns 0 on success and -1 if unknown type of device (not normal, bridge * or CardBus). */ -int pci_setup_device(struct pci_dev * dev) +static int pci_setup_device(struct pci_dev * dev) { u32 class; @@ -692,7 +698,6 @@ EXPORT_SYMBOL(pci_root_buses); #ifdef CONFIG_HOTPLUG -EXPORT_SYMBOL(pci_setup_device); EXPORT_SYMBOL(pci_add_new_bus); EXPORT_SYMBOL(pci_do_scan_bus); EXPORT_SYMBOL(pci_scan_slot); diff -urN orig/drivers/pci/setup-bus.c linux/drivers/pci/setup-bus.c --- orig/drivers/pci/setup-bus.c Mon Mar 24 23:47:20 2003 +++ linux/drivers/pci/setup-bus.c Tue Mar 25 00:14:56 2003 @@ -43,13 +43,15 @@ #define CARDBUS_IO_SIZE (4096) #define CARDBUS_MEM_SIZE (32*1024*1024) -static int __devinit +static void __devinit pbus_assign_resources_sorted(struct pci_bus *bus) { struct pci_dev *dev; struct resource *res; struct resource_list head, *list, *tmp; - int idx, found_vga = 0; + int idx; + + bus->bridge_ctl &= ~PCI_BRIDGE_CTL_VGA; head.next = NULL; list_for_each_entry(dev, &bus->devices, bus_list) { @@ -57,7 +59,7 @@ if (class == PCI_CLASS_DISPLAY_VGA || class == PCI_CLASS_NOT_DEFINED_VGA) - found_vga = 1; + bus->bridge_ctl |= PCI_BRIDGE_CTL_VGA; pdev_sort_resources(dev, &head); } @@ -70,8 +72,6 @@ list = list->next; kfree(tmp); } - - return found_vga; } static void __devinit @@ -199,10 +199,7 @@ } pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, l); - /* Check if we have VGA behind the bridge. - Enable ISA in either case (FIXME!). */ - l = (bus->resource[0]->flags & IORESOURCE_BUS_HAS_VGA) ? 0x0c : 0x04; - pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, l); + pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl); } /* Check whether the bridge supports optional I/O and @@ -486,13 +483,14 @@ pci_bus_assign_resources(struct pci_bus *bus) { struct pci_bus *b; - int found_vga = pbus_assign_resources_sorted(bus); struct pci_dev *dev; - if (found_vga) { + pbus_assign_resources_sorted(bus); + + if (bus->bridge_ctl & PCI_BRIDGE_CTL_VGA) { /* Propagate presence of the VGA to upstream bridges */ for (b = bus; b->parent; b = b->parent) { - b->resource[0]->flags |= IORESOURCE_BUS_HAS_VGA; + b->bridge_ctl |= PCI_BRIDGE_CTL_VGA; } } list_for_each_entry(dev, &bus->devices, bus_list) { diff -urN orig/drivers/pci/setup-irq.c linux/drivers/pci/setup-irq.c --- orig/drivers/pci/setup-irq.c Mon Nov 18 09:52:12 2002 +++ linux/drivers/pci/setup-irq.c Tue Mar 25 00:15:07 2003 @@ -26,7 +26,7 @@ #endif -static void __init +static void __devinit pdev_fixup_irq(struct pci_dev *dev, u8 (*swizzle)(struct pci_dev *, u8 *), int (*map_irq)(struct pci_dev *, u8, u8)) @@ -60,6 +60,20 @@ pcibios_update_irq(dev, irq); } +void __devinit +pci_bus_fixup_irqs(struct pci_bus *bus, u8 (*swizzle)(struct pci_dev *, u8 *), + int (*map_irq)(struct pci_dev *, u8, u8)) +{ + struct pci_dev *dev; + + list_for_each_entry(dev, &bus->devices, bus_list) { + pdev_fixup_irq(dev, swizzle, map_irq); + + if (dev->subordinate) + pci_bus_fixup_irqs(dev->subordinate, swizzle, map_irq); + } +} + void __init pci_fixup_irqs(u8 (*swizzle)(struct pci_dev *, u8 *), int (*map_irq)(struct pci_dev *, u8, u8)) diff -urN orig/drivers/pcmcia/Kconfig linux/drivers/pcmcia/Kconfig --- orig/drivers/pcmcia/Kconfig Mon May 5 17:39:30 2003 +++ linux/drivers/pcmcia/Kconfig Thu Jun 12 12:44:48 2003 @@ -29,8 +29,8 @@ and ds. If you want to compile it as a module, say M here and read . -config CARDBUS - bool "CardBus support" +config YENTA + tristate "CardBus yenta-compatible bridge support" depends on PCMCIA && PCI ---help--- CardBus is a bus mastering architecture for PC-cards, which allows @@ -48,6 +48,11 @@ If unsure, say Y. +config CARDBUS + bool + depends on YENTA + default y if YENTA + config I82092 tristate "i82092 compatible bridge support" depends on PCMCIA && PCI @@ -58,7 +63,7 @@ config I82365 tristate "i82365 compatible bridge support" - depends on PCMCIA + depends on PCMCIA && ISA help Say Y here to include support for ISA-bus PCMCIA host bridges that are register compatible with the Intel i82365. These are found on @@ -79,6 +84,10 @@ tristate "HD64465 host bridge support" depends on HD64465 && PCMCIA +config PCMCIA_CLPS6700 + tristate "CLPS6700 support" + depends on ARM && ARCH_CLPS711X && PCMCIA + config PCMCIA_SA1100 tristate "SA1100 support" depends on ARM && ARCH_SA1100 && PCMCIA diff -urN orig/drivers/pcmcia/Makefile linux/drivers/pcmcia/Makefile --- orig/drivers/pcmcia/Makefile Sat Apr 12 14:46:55 2003 +++ linux/drivers/pcmcia/Makefile Thu Jun 12 12:44:48 2003 @@ -3,9 +3,7 @@ # obj-$(CONFIG_PCMCIA) += pcmcia_core.o ds.o -ifeq ($(CONFIG_CARDBUS),y) - obj-$(CONFIG_PCMCIA) += yenta_socket.o -endif +obj-$(CONFIG_YENTA) += yenta.o obj-$(CONFIG_I82365) += i82365.o obj-$(CONFIG_I82092) += i82092.o @@ -13,10 +11,11 @@ obj-$(CONFIG_HD64465_PCMCIA) += hd64465_ss.o obj-$(CONFIG_PCMCIA_SA1100) += sa11xx_core.o sa1100_cs.o obj-$(CONFIG_PCMCIA_SA1111) += sa11xx_core.o sa1111_cs.o +obj-$(CONFIG_PCMCIA_CLPS6700) += clps6700.o -yenta_socket-y += pci_socket.o yenta.o - -pcmcia_core-y += cistpl.o rsrc_mgr.o bulkmem.o cs.o +pcmcia_core-y += cistpl.o bulkmem.o cs.o +pcmcia_core-y += mapwindow.o rsrc_mgr.o +pcmcia_core-y += mapstatic.o pcmcia_core-$(CONFIG_CARDBUS) += cardbus.o sa1111_cs-y += sa1111_generic.o diff -urN orig/drivers/pcmcia/bulkmem.c linux/drivers/pcmcia/bulkmem.c --- orig/drivers/pcmcia/bulkmem.c Mon Mar 24 23:47:20 2003 +++ linux/drivers/pcmcia/bulkmem.c Wed Jun 11 22:54:01 2003 @@ -63,7 +63,7 @@ { int ret, tries; client_t *mtd; - socket_info_t *s; + struct pcmcia_socket *s; mtd = handle->mtd; if (mtd == NULL) @@ -130,7 +130,7 @@ eraseq_entry_t *erase = busy->erase; mtd_request_t req; client_t *mtd; - socket_info_t *s; + struct pcmcia_socket *s; int ret; DEBUG(2, "cs: trying erase request 0x%p...\n", busy); @@ -259,27 +259,27 @@ win->ctl.flags |= MAP_ATTRIB; win->ctl.speed = req->AccessSpeed; win->ctl.card_start = req->CardOffset; - win->sock->ss_entry->set_mem_map(win->sock->sock, &win->ctl); + win->sock->ss_entry->set_mem_map(win->sock, &win->ctl); return CS_SUCCESS; } static int mtd_set_vpp(client_handle_t handle, mtd_vpp_req_t *req) { - socket_info_t *s; + struct pcmcia_socket *s; if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; if (req->Vpp1 != req->Vpp2) return CS_BAD_VPP; s = SOCKET(handle); s->socket.Vpp = req->Vpp1; - if (s->ss_entry->set_socket(s->sock, &s->socket)) + if (s->ss_entry->set_socket(s, &s->socket)) return CS_BAD_VPP; return CS_SUCCESS; } static int mtd_rdy_mask(client_handle_t handle, mtd_rdy_req_t *req) { - socket_info_t *s; + struct pcmcia_socket *s; if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; s = SOCKET(handle); @@ -287,7 +287,7 @@ s->socket.csc_mask |= SS_READY; else s->socket.csc_mask &= ~SS_READY; - if (s->ss_entry->set_socket(s->sock, &s->socket)) + if (s->ss_entry->set_socket(s, &s->socket)) return CS_GENERAL_FAILURE; return CS_SUCCESS; } @@ -417,7 +417,7 @@ int pcmcia_get_first_region(client_handle_t handle, region_info_t *rgn) { - socket_info_t *s = SOCKET(handle); + struct pcmcia_socket *s = SOCKET(handle); if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; @@ -450,7 +450,7 @@ int pcmcia_register_mtd(client_handle_t handle, mtd_reg_t *reg) { memory_handle_t list; - socket_info_t *s; + struct pcmcia_socket *s; if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; @@ -533,12 +533,12 @@ int pcmcia_open_memory(client_handle_t *handle, open_mem_t *open, memory_handle_t *mh) { - socket_info_t *s; + struct pcmcia_socket *s; memory_handle_t region; if ((handle == NULL) || CHECK_HANDLE(*handle)) return CS_BAD_HANDLE; - s = SOCKET(*handle); + s = (*handle)->Socket; if (open->Attributes & MEMORY_TYPE_AM) region = s->a_region; else diff -urN orig/drivers/pcmcia/cardbus.c linux/drivers/pcmcia/cardbus.c --- orig/drivers/pcmcia/cardbus.c Mon May 5 17:39:30 2003 +++ linux/drivers/pcmcia/cardbus.c Thu Jun 12 12:44:48 2003 @@ -119,11 +119,11 @@ These are similar to setup_cis_mem and release_cis_mem for 16-bit cards. The "result" that is used externally is the cb_cis_virt - pointer in the socket_info_t structure. + pointer in the struct pcmcia_socket structure. =====================================================================*/ -static void cb_release_cis_mem(socket_info_t * s) +static void cb_release_cis_mem(struct pcmcia_socket * s) { if (s->cb_cis_virt) { DEBUG(1, "cs: cb_release_cis_mem()\n"); @@ -133,16 +133,13 @@ } } -static int cb_setup_cis_mem(socket_info_t * s, struct resource *res) +static int cb_setup_cis_mem(struct pcmcia_socket * s, struct resource *res) { unsigned int start, size; if (res == s->cb_cis_res) return 0; - if (s->cb_cis_res) - cb_release_cis_mem(s); - start = res->start; size = res->end - start + 1; s->cb_cis_virt = ioremap(start, size); @@ -162,7 +159,7 @@ =====================================================================*/ -int read_cb_mem(socket_info_t * s, int space, u_int addr, u_int len, void *ptr) +int read_cb_mem(struct pcmcia_socket * s, int space, u_int addr, u_int len, void *ptr) { struct pci_dev *dev; struct resource *res; @@ -192,15 +189,17 @@ if (space == 7) { addr = xlate_rom_addr(s->cb_cis_virt, addr); if (addr == 0) - goto fail; + goto unmap; } if (addr + len > res->end - res->start) - goto fail; + goto unmap; memcpy_fromio(ptr, s->cb_cis_virt + addr, len); return 0; +unmap: + cb_release_cis_mem(s); fail: memset(ptr, 0xff, len); return -1; @@ -237,14 +236,14 @@ } } -int cb_alloc(socket_info_t * s) +int cb_alloc(struct pcmcia_socket * s) { struct pci_bus *bus = s->cap.cb_dev->subordinate; struct pci_dev *dev; unsigned int max, pass; s->functions = pci_scan_slot(bus, PCI_DEVFN(0, 0)); -// pcibios_fixup_bus(bus); + pcibios_fixup_bus(bus); max = bus->secondary; for (pass = 0; pass < 2; pass++) @@ -266,7 +265,7 @@ return CS_SUCCESS; } -void cb_free(socket_info_t * s) +void cb_free(struct pcmcia_socket * s) { struct pci_dev *bridge = s->cap.cb_dev; @@ -275,3 +274,118 @@ if (bridge) pci_remove_behind_bridge(bridge); } + + + + +static struct resource * +make_resource(unsigned long b, unsigned long n, int flags, char *name) +{ + struct resource *res = kmalloc(sizeof(struct resource), GFP_KERNEL); + + if (res) { + memset(res, 0, sizeof(*res)); + res->name = name; + res->start = b; + res->end = b + n - 1; + res->flags = flags | IORESOURCE_BUSY; + } + return res; +} + +struct cardbus_alignment { + unsigned long mask; + unsigned long offset; +}; + +/* + * Some PCMCIA cards have only a limited number of address lines, and + * require a certain bit pattern on the high lines. This gives us the + * following requirement for an address: + * + * (base & ~((1 << bits) - 1)) + offset + * + * The initial alignment is taken care of in the generic resource + * allocation code, so we just need to ensure the correct offset. + * + * Also, if the PCMCIA card requests a resource with a specific offset + * less than our normal minimum, we assume that this is because it knows + * it must have that address. + */ +static void +cardbus_adjust(void *data, struct resource *res, unsigned long size, + unsigned long align) +{ + struct cardbus_alignment *ca = data; + unsigned long start; + + start = (res->start & ~ca->mask) + ca->offset; + if (start < res->start) + start = ((res->start + ca->mask) & ~ca->mask) + ca->offset; + + res->start = start; +} + +struct resource * +cb_alloc_io(unsigned long base, unsigned long num, + unsigned long align, char *name, struct pcmcia_socket *s) +{ + struct pci_dev *bridge = s->cap.cb_dev; + struct resource *res = make_resource(0, num, IORESOURCE_IO, name); + struct cardbus_alignment ca; + unsigned long min; + int ret; + + /* align = 0 == must have exact match */ + if (align == 0) { + /*FIXME*/ + ret = -EBUSY; + } else { + ca.mask = align - 1; + ca.offset = base & ca.mask; + + if (base && base < PCIBIOS_MIN_IO) { + min = base; + } else { + min = PCIBIOS_MIN_IO; + } + + ret = pci_bus_alloc_resource(bridge->bus, res, num, 1, + min, 0, cardbus_adjust, &ca); + } + if (ret != 0) { + kfree(res); + res = NULL; + } + return res; +} + +struct resource * +cb_alloc_mem(unsigned long base, unsigned long num, + unsigned long align, char *name, struct pcmcia_socket *s) +{ + struct pci_dev *bridge = s->cap.cb_dev; + struct resource *res = make_resource(0, num, IORESOURCE_MEM, name); + struct cardbus_alignment ca; + unsigned long min; + int ret; + + if (align == 0) + align = 1; + + ca.mask = align - 1; + ca.offset = base & ca.mask; + + if (base && base < PCIBIOS_MIN_MEM) + min = base; + else + min = PCIBIOS_MIN_MEM; + + ret = pci_bus_alloc_resource(bridge->bus, res, num, 1, + min, 0, cardbus_adjust, &ca); + if (ret != 0) { + kfree(res); + res = NULL; + } + return res; +} diff -urN orig/drivers/pcmcia/cistpl.c linux/drivers/pcmcia/cistpl.c --- orig/drivers/pcmcia/cistpl.c Mon May 5 17:39:31 2003 +++ linux/drivers/pcmcia/cistpl.c Thu Jun 12 12:44:44 2003 @@ -53,6 +53,7 @@ #include #include #include "cs_internal.h" +#include "map.h" static const u_char mantissa[] = { 10, 12, 13, 15, 20, 25, 30, 35, @@ -82,17 +83,9 @@ INT_MODULE_PARM(cis_width, 0); /* 16-bit CIS? */ -void release_cis_mem(socket_info_t *s) +static void release_cis_mem(struct pcmcia_socket *s) { - if (s->cis_mem.sys_start != 0) { - s->cis_mem.flags &= ~MAP_ACTIVE; - s->ss_entry->set_mem_map(s->sock, &s->cis_mem); - if (!(s->cap.features & SS_CAP_STATIC_MAP)) - release_mem_region(s->cis_mem.sys_start, s->cap.map_size); - iounmap(s->cis_virt); - s->cis_mem.sys_start = 0; - s->cis_virt = NULL; - } + s->map_ops->cis_unmap(s); } /* @@ -101,31 +94,15 @@ * map the memory space. */ static unsigned char * -set_cis_map(socket_info_t *s, unsigned int card_offset, unsigned int flags) +set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flags) { - pccard_mem_map *mem = &s->cis_mem; - if (!(s->cap.features & SS_CAP_STATIC_MAP) && - mem->sys_start == 0) { - int low = !(s->cap.features & SS_CAP_PAGE_REGS); - validate_mem(s); - mem->sys_start = 0; - if (find_mem_region(&mem->sys_start, s->cap.map_size, - s->cap.map_size, low, "card services", s)) { - printk(KERN_NOTICE "cs: unable to map card memory!\n"); - return NULL; - } - mem->sys_stop = mem->sys_start+s->cap.map_size-1; - s->cis_virt = ioremap(mem->sys_start, s->cap.map_size); - } - mem->card_start = card_offset; - mem->flags = flags; - s->ss_entry->set_mem_map(s->sock, mem); - if (s->cap.features & SS_CAP_STATIC_MAP) { - if (s->cis_virt) - iounmap(s->cis_virt); - s->cis_virt = ioremap(mem->sys_start, s->cap.map_size); - } - return s->cis_virt; + void *ret; + + ret = s->map_ops->cis_map(s, card_offset, flags); + if (!ret) + printk(KERN_ERR "cs: unable to map card memory!\n"); + + return ret; } /*====================================================================== @@ -139,7 +116,7 @@ #define IS_ATTR 1 #define IS_INDIRECT 8 -int read_cis_mem(socket_info_t *s, int attr, u_int addr, +int read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr) { u_char *sys, *end, *buf = ptr; @@ -196,13 +173,14 @@ addr = 0; } } + release_cis_mem(s); DEBUG(3, "cs: %#2.2x %#2.2x %#2.2x %#2.2x ...\n", *(u_char *)(ptr+0), *(u_char *)(ptr+1), *(u_char *)(ptr+2), *(u_char *)(ptr+3)); return 0; } -void write_cis_mem(socket_info_t *s, int attr, u_int addr, +void write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr) { u_char *sys, *end, *buf = ptr; @@ -256,6 +234,7 @@ addr = 0; } } + release_cis_mem(s); } /*====================================================================== @@ -266,7 +245,7 @@ ======================================================================*/ -static void read_cis_cache(socket_info_t *s, int attr, u_int addr, +static void read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr) { struct cis_cache_entry *cis; @@ -306,7 +285,7 @@ } static void -remove_cis_cache(socket_info_t *s, int attr, u_int addr, u_int len) +remove_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, u_int len) { struct cis_cache_entry *cis; @@ -318,7 +297,7 @@ } } -void destroy_cis_cache(socket_info_t *s) +void destroy_cis_cache(struct pcmcia_socket *s) { struct list_head *l, *n; @@ -337,7 +316,7 @@ ======================================================================*/ -int verify_cis_cache(socket_info_t *s) +int verify_cis_cache(struct pcmcia_socket *s) { struct cis_cache_entry *cis; char buf[256]; @@ -369,7 +348,7 @@ int pcmcia_replace_cis(client_handle_t handle, cisdump_t *cis) { - socket_info_t *s; + struct pcmcia_socket *s; if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; s = SOCKET(handle); @@ -409,7 +388,7 @@ int pcmcia_get_first_tuple(client_handle_t handle, tuple_t *tuple) { - socket_info_t *s; + struct pcmcia_socket *s; if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; s = SOCKET(handle); @@ -445,7 +424,7 @@ return pcmcia_get_next_tuple(handle, tuple); } -static int follow_link(socket_info_t *s, tuple_t *tuple) +static int follow_link(struct pcmcia_socket *s, tuple_t *tuple) { u_char link[5]; u_int ofs; @@ -487,7 +466,7 @@ int pcmcia_get_next_tuple(client_handle_t handle, tuple_t *tuple) { - socket_info_t *s; + struct pcmcia_socket *s; u_char link[2], tmp; int ofs, i, attr; @@ -588,7 +567,7 @@ int pcmcia_get_tuple_data(client_handle_t handle, tuple_t *tuple) { - socket_info_t *s; + struct pcmcia_socket *s; u_int len; if (CHECK_HANDLE(handle)) diff -urN orig/drivers/pcmcia/clps6700.c linux/drivers/pcmcia/clps6700.c --- orig/drivers/pcmcia/clps6700.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/pcmcia/clps6700.c Fri Mar 28 23:13:40 2003 @@ -0,0 +1,492 @@ +/* + * linux/drivers/pcmcia/clps6700.c + * + * Copyright (C) 2000 Deep Blue Solutions 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "clps6700.h" + +#define DEBUG + +MODULE_AUTHOR("Russell King"); +MODULE_DESCRIPTION("CL-PS6700 PCMCIA socket driver"); + +#define NR_CLPS6700 2 + +struct clps6700_skt { + u_int nr; + u_int physbase; + unsigned char *regbase; + u_int pmr; + u_int cpcr; + u_int cpcr_3v3; + u_int cpcr_5v0; + u_int cur_pmr; + u_int cur_pcimr; + u_int cur_cpcr; + void (*handler)(void *, u_int); + void *handler_info; + void *io; + + struct work_struct work; + + u_int ev_pending; + spinlock_t ev_lock; +}; + +static struct clps6700_skt *skts[NR_CLPS6700]; + +static int clps6700_sock_init(u_int sock) +{ + struct clps6700_skt *skt = skts[sock]; + + skt->cur_pmr = skt->pmr; + skt->cur_pcimr = 0; + skt->cur_cpcr = skt->cpcr; + +#ifdef DEBUG + printk("skt%d: sock_init()\n", sock); +#endif + + writel(skt->cur_pmr, skt->regbase + PMR); + writel(skt->cur_cpcr, skt->regbase + CPCR); + writel(0x01f8, skt->regbase + SICR); + writel(0x0000, skt->regbase + DMACR); + writel(0, skt->regbase + CICR); + writel(0x1f00, skt->regbase + CITR0A); + writel(0x0000, skt->regbase + CITR0B); + writel(0x1f00, skt->regbase + CITR1A); + writel(0x0000, skt->regbase + CITR1B); + writel(skt->cur_pcimr, skt->regbase + PCIMR); + + /* + * Enable Auto Idle Mode in PM register + */ + writel(-1, skt->regbase + PCIRR1); + writel(-1, skt->regbase + PCIRR2); + writel(-1, skt->regbase + PCIRR3); + + return 0; +} + +static int clps6700_suspend(u_int sock) +{ + return 0; +} + +static int clps6700_register_callback(u_int sock, void (*handler)(void *, u_int), void *info) +{ + struct clps6700_skt *skt = skts[sock]; + +#ifdef DEBUG + printk("skt%d: register_callback: %p (%p)\n", sock, handler, info); +#endif + + skt->handler_info = info; + skt->handler = handler; + + return 0; +} + +static int clps6700_inquire_socket(u_int sock, socket_cap_t *cap) +{ + struct clps6700_skt *skt = skts[sock]; + + cap->features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP; + cap->irq_mask = 0; /* available IRQs for this socket */ + cap->map_size = PAGE_SIZE; /* minimum mapping size */ + cap->pci_irq = 0; /* PCI interrupt number */ + cap->cb_dev = NULL; + cap->io_offset = (unsigned long)skt->io; + + return 0; +} + +static int __clps6700_get_status(struct clps6700_skt *skt) +{ + unsigned int v, val; + + v = readl(skt->regbase + PCIILR); + val = 0; + if ((v & (PCM_CD1 | PCM_CD2)) == 0) + val |= SS_DETECT; + if ((v & (PCM_BVD2 | PCM_BVD1)) == PCM_BVD1) + val |= SS_BATWARN; + if ((v & PCM_BVD2) == 0) + val |= SS_BATDEAD; + + if (v & PCM_RDYL) + val |= SS_READY; + if (v & PCM_VS1) + val |= SS_3VCARD; + if (v & PCM_VS2) + val |= SS_XVCARD; + +#ifdef DEBUG + printk("skt%d: PCIILR: %08x -> (%s %s %s %s %s %s)\n", + skt->nr, v, + val & SS_READY ? "rdy" : "---", + val & SS_DETECT ? "det" : "---", + val & SS_BATWARN ? "bw" : "--", + val & SS_BATDEAD ? "bd" : "--", + val & SS_3VCARD ? "3v" : "--", + val & SS_XVCARD ? "xv" : "--"); +#endif + return val; +} + +static int clps6700_get_status(u_int sock, u_int *valp) +{ + struct clps6700_skt *skt = skts[sock]; + + *valp = __clps6700_get_status(skt); + + return 0; /* not used! */ +} + +static int clps6700_get_socket(u_int sock, socket_state_t *state) +{ + return -EINVAL; +} + +static int clps6700_set_socket(u_int sock, socket_state_t *state) +{ + struct clps6700_skt *skt = skts[sock]; + unsigned long flags; + u_int cpcr = skt->cur_cpcr, pmr = skt->cur_pmr, cicr = 0; + u_int pcimr = 0; + + if (state->flags & SS_PWR_AUTO) + pmr |= PMR_DCAR | PMR_PDCR; + + if (state->flags & SS_OUTPUT_ENA) + cicr |= CICR_RESETOE; + + /* + * Note! We must NOT assert the Card Enable bit until reset has + * been de-asserted. Some cards indicate not ready, which then + * hangs our next access. (Bug in CLPS6700?) + */ + if (state->flags & SS_RESET) + cicr |= CICR_RESET; + else if (state->flags & SS_OUTPUT_ENA) + cicr |= CICR_ENABLE; + + if (state->flags & SS_IOCARD) { + cicr |= CICR_IOMODE | CICR_AUTOIOSZ; + + if (state->csc_mask & SS_STSCHG) + pcimr |= PCM_BVD1; + } else { + if (state->csc_mask & SS_BATDEAD) + pcimr |= PCM_BVD2; + if (state->csc_mask & SS_BATWARN) + pcimr |= PCM_BVD1; + if (state->csc_mask & SS_READY) + pcimr |= PCM_RDYL; + } + + if (state->csc_mask & SS_DETECT) + pcimr |= PCM_CD1 | PCM_CD2; + + switch (state->Vcc) { + case 0: break; + case 33: cpcr |= skt->cpcr_3v3; pmr |= PMR_CPE; break; + case 50: cpcr |= skt->cpcr_5v0; pmr |= PMR_CPE; break; + default: return -EINVAL; + } + +#ifdef DEBUG + printk("skt%d: PMR: %04x, CPCR: %04x, CICR: %04x PCIMR: %04x " + "(Vcc = %d, flags = %c%c%c%c, csc = %c%c%c%c%c)\n", + sock, pmr, cpcr, cicr, pcimr, state->Vcc, + state->flags & SS_RESET ? 'r' : '-', + state->flags & SS_PWR_AUTO ? 'p' : '-', + state->flags & SS_IOCARD ? 'i' : '-', + state->flags & SS_OUTPUT_ENA ? 'o' : '-', + state->csc_mask & SS_STSCHG ? 's' : '-', + state->csc_mask & SS_BATDEAD ? 'd' : '-', + state->csc_mask & SS_BATWARN ? 'w' : '-', + state->csc_mask & SS_READY ? 'r' : '-', + state->csc_mask & SS_DETECT ? 'c' : '-'); +#endif + + local_irq_save(flags); + + if (skt->cur_cpcr != cpcr) { + skt->cur_cpcr = cpcr; + writel(skt->cur_cpcr, skt->regbase + CPCR); + } + + if (skt->cur_pmr != pmr) { + skt->cur_pmr = pmr; + writel(skt->cur_pmr, skt->regbase + PMR); + } + if (skt->cur_pcimr != pcimr) { + skt->cur_pcimr = pcimr; + writel(skt->cur_pcimr, skt->regbase + PCIMR); + } + + writel(cur_cicr, skt->regbase + CICR); + + local_irq_restore(flags); + + return 0; +} + +static int clps6700_set_io_map(u_int sock, struct pccard_io_map *io) +{ + printk("skt%d: iomap: %d: speed %d, flags %X start %X stop %X\n", + sock, io->map, io->speed, io->flags, io->start, io->stop); + return 0; +} + +/* + * Set the memory map attributes for this socket. (ie, mem->speed) + * Note that since we have SS_CAP_STATIC_MAP set, we don't need to do + * any mapping here at all; we just need to return the address (suitable + * for ioremap) to map the requested space in mem->sys_start. + * + * flags & MAP_ATTRIB indicates whether we want attribute space. + */ +static int clps6700_set_mem_map(u_int sock, struct pccard_mem_map *mem) +{ + struct clps6700_skt *skt = skts[sock]; + u_int off; + + printk("skt%d: memmap: %d: speed %d, flags %X card %X\n", + sock, mem->map, mem->speed, mem->flags, mem->card_start); + + if (mem->flags & MAP_ATTRIB) + off = CLPS6700_ATTRIB_BASE; + else + off = CLPS6700_MEM_BASE; + + mem->sys_stop -= mem->sys_start; + mem->sys_start = skt->physbase + off + mem->card_start; + mem->sys_stop += mem->sys_start; + + return 0; +} + +static void clps6700_proc_setup(u_int sock, struct proc_dir_entry *base) +{ +} + +static struct pccard_operations clps6700_operations = { + .owner = THIS_MODULE, + .init = clps6700_sock_init, + .suspend = clps6700_suspend, + .register_callback = clps6700_register_callback, + .inquire_socket = clps6700_inquire_socket, + .get_status = clps6700_get_status, + .get_socket = clps6700_get_socket, + .set_socket = clps6700_set_socket, + .set_io_map = clps6700_set_io_map, + .set_mem_map = clps6700_set_mem_map, + .proc_setup = clps6700_proc_setup +}; + +static void clps6700_work_handler(void *data) +{ + struct clps6700_skt *skt = data; + unsigned long flags; + u_int events; + + /* + * Note! We must read the pending event state + * with interrupts disabled, otherwise we race + * with our own interrupt routine! + */ + spin_lock_irqsave(&skt->ev_lock, flags); + events = skt->ev_pending; + skt->ev_pending = 0; + spin_unlock_irqrestore(&skt->ev_lock, flags); + + if (skt->handler && events) + skt->handler(skt->handler_info, events); +} + +static void clps6700_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct clps6700_skt *skt = dev_id; + u_int val, events; + + val = readl(skt->regbase + PCISR); + if (!val) + return; + + writel(val, skt->regbase + PCICR); + + events = 0; + if (val & (PCM_CD1 | PCM_CD2)) + events |= SS_DETECT; + if (val & PCM_BVD1) + events |= SS_BATWARN; + if (val & PCM_BVD2) + events |= SS_BATDEAD; + if (val & PCM_RDYL) + events |= SS_READY; + + spin_lock(&skt->ev_lock); + skt->ev_pending |= events; + spin_unlock(&skt->ev_lock); + schedule_work(&skt->work); +} + +static int __init clps6700_init_skt(int nr) +{ + struct clps6700_skt *skt; + int ret; + + skt = kmalloc(sizeof(struct clps6700_skt), GFP_KERNEL); + if (!skt) + return -ENOMEM; + + memset(skt, 0, sizeof(struct clps6700_skt)); + + spin_lock_init(&skt->ev_lock); + + skt->nr = nr; + skt->physbase = nr ? CS5_PHYS_BASE : CS4_PHYS_BASE; + skt->pmr = PMR_AUTOIDLE | PMR_MCPE | PMR_CDWEAK; + skt->cpcr = CPCR_PDIR(PCTL1|PCTL0); + skt->cpcr_3v3 = CPCR_PON(PCTL0); + skt->cpcr_5v0 = CPCR_PON(PCTL0); /* we only do 3v3 */ + + skt->cur_pmr = skt->pmr; + + skt->regbase = ioremap(skt->physbase + CLPS6700_REG_BASE, + CLPS6700_REG_SIZE); + ret = -ENOMEM; + if (!skt->regbase) + goto err_free; + + skt->io = ioremap(skt->physbase + CLPS6700_IO_BASE, CLPS6700_IO_SIZE); + if (!skt->io) + goto err_unmap; + + INIT_WORK(&skt->work, clps6700_work_handler, skt); + + skts[nr] = skt; + + ret = request_irq(IRQ_EINT3, clps6700_interrupt, + SA_SHIRQ, "pcmcia", skt); + + if (ret) { + printk(KERN_ERR "clps6700: unable to grab irq%d (%d)\n", + IRQ_EINT3, ret); + goto err_unmap2; + } + return 0; + + err_unmap2: + iounmap(skt->io); + err_unmap: + iounmap(skt->regbase); + err_free: + kfree(skt); + skts[nr] = NULL; + return ret; +} + +static void clps6700_free_resources(void) +{ + int i; + + for (i = NR_CLPS6700; i >= 0; i--) { + struct clps6700_skt *skt = skts[i]; + + skts[i] = NULL; + if (skt == NULL) + continue; + + free_irq(IRQ_EINT3, skt); + if (skt->regbase) { + writel(skt->pmr, skt->regbase + PMR); + writel(skt->cpcr, skt->regbase + CPCR); + writel(0, skt->regbase + CICR); + writel(0, skt->regbase + PCIMR); + } + iounmap(skt->io); + iounmap(skt->regbase); + kfree(skt); + } + + flush_scheduled_work(); +} + +static int __init clps6700_init(void) +{ + unsigned int v; + int err, nr; + + PLD_CF = 0; + v = clps_readl(SYSCON2) | SYSCON2_PCCARD1 | SYSCON2_PCCARD2; + clps_writel(v, SYSCON2); + v = clps_readl(SYSCON1) | SYSCON1_EXCKEN; + clps_writel(v, SYSCON1); + + for (nr = 0; nr < NR_CLPS6700; nr++) { + err = clps6700_init_skt(nr); + if (err) + goto free; + } + + err = register_ss_entry(nr, &clps6700_operations); + if (err) + goto free; + + return 0; + +free: + clps6700_free_resources(); + /* + * An error occurred. Unmap and free all CLPS6700 + */ + return err; +} + +static void __exit clps6700_exit(void) +{ + unregister_ss_entry(&clps6700_operations); + clps6700_free_resources(); +} + +module_init(clps6700_init); +module_exit(clps6700_exit); diff -urN orig/drivers/pcmcia/clps6700.h linux/drivers/pcmcia/clps6700.h --- orig/drivers/pcmcia/clps6700.h Thu Jan 1 01:00:00 1970 +++ linux/drivers/pcmcia/clps6700.h Fri Mar 28 23:13:40 2003 @@ -0,0 +1,86 @@ +#define PCISR 0x0000 /* PC Card Interrupt Status Register */ +#define PCIMR 0x0400 /* PC Card Interrupt Mask Register */ +#define PCICR 0x0800 /* PC Card Interrupt Clear Register */ +#define PCIOSR 0x0c00 /* PC Card Interrupt Output Select Regsiter */ +#define PCIRR1 0x1000 /* PC Card Interrupt Reserved Register 1 */ +#define PCIRR2 0x1400 /* PC Card Interrupt Reserved Register 2 */ +#define PCIRR3 0x1800 /* PC Card Interrupt Reserved Register 3 */ +#define PCIILR 0x1c00 /* PC Card Interrupt Input Level Register */ +#define SICR 0x2000 /* System Interface Configuration Register */ +#define CICR 0x2400 /* Card Interface Configuration Register */ +#define PMR 0x2800 /* Power Management Register */ +#define CPCR 0x2c00 /* Card Power Control Register */ +#define CITR0A 0x3000 /* Card Interface Timing Register 0A */ +#define CITR0B 0x3400 /* Card Interface Timing Register 0B */ +#define CITR1A 0x3800 /* Card Interface Timing Register 1A */ +#define CITR1B 0x3c00 /* Card Interface Timing Register 1B */ +#define DMACR 0x4000 /* DMA Control Register */ +#define DIR 0x4400 /* Device Information Register */ + +#define CLPS6700_ATTRIB_BASE 0x00000000 +#define CLPS6700_IO_BASE 0x04000000 +#define CLPS6700_IO_SIZE 0x00010000 +#define CLPS6700_MEM_BASE 0x08000000 +#define CLPS6700_REG_BASE 0x0c000000 +#define CLPS6700_REG_SIZE 0x00005000 + + +#define PMR_AUTOIDLE (1 << 0) /* auto idle mode */ +#define PMR_FORCEIDLE (1 << 1) /* force idle mode */ +#define PMR_PDCS (1 << 2) /* Power down card on standby */ +#define PMR_PDCR (1 << 3) /* Power down card on removal */ +#define PMR_DCAR (1 << 4) /* Disable card access on removal */ +#define PMR_CPE (1 << 5) /* Card power enable */ +#define PMR_MCPE (1 << 6) /* Monitor card power enable */ +#define PMR_PDREQLSEL (1 << 7) /* If set, PDREQL is a GPIO pin */ +#define PMR_DISSTBY (1 << 8) /* Disable standby */ +#define PMR_ACCSTBY (1 << 9) /* Complete card accesses before standby*/ +#define PMR_CDUNPROT (0 << 10) /* Card detect inputs unprotected */ +#define PMR_CDPROT (1 << 10) /* Card detect inputs protected */ +#define PMR_CDWEAK (2 << 10) /* Weak pullup except in standby */ +#define PMR_CDWEAKAL (3 << 10) /* Weak pullup */ + +#define CPCR_PON(x) ((x)&7) /* PCTL[2:0] value when PMR_CPE = 1 */ +#define CPCR_POFF(x) (((x)&7)<<3) /* PCTL[2:0] value when PMR_CPE = 0 */ +#define CPCR_PDIR(x) (((x)&7)<<6) /* PCTL[2:0] direction */ +#define CPCR_CON(x) (((x)&1)<<9) /* GPIO value when PMR_CPE = 1 */ +#define CPCR_COFF(x) (((x)&1)<<10) /* GPIO value when PMR_CPE = 0 */ +#define CPCR_CDIR(x) (((x)&1)<<11) /* GPIO direction (PMR_PDREQLSEL = 1) */ +#define CPCR_VS(x) (((x)&3)<<12) /* VS[2:1] output value */ +#define CPCR_VSDIR(x) (((x)&3)<<14) /* VS[2:1] direction */ + +#define PCTL0 (1 << 0) +#define PCTL1 (1 << 1) +#define PCTL2 (1 << 2) + +#define CICR_ASRTMR1 (1 << 0) /* Timer 1 select for attribute read */ +#define CICR_ASWTMR1 (1 << 1) /* Timer 1 select for attribute write */ +#define CICR_IOSRTMR1 (1 << 2) /* Timer 1 select for IO read */ +#define CICR_IOSWTMR1 (1 << 3) /* Timer 1 select for IO write */ +#define CICR_MEMSRTMR1 (1 << 4) /* Timer 1 select for memory read */ +#define CICR_MEMSWTMR1 (1 << 5) /* Timer 1 select for memory write */ +#define CICR_AUTOIOSZ (1 << 6) /* Auto size I/O accesses */ +#define CICR_CAW (1 << 7) /* Card access width */ +#define CICR_IOMODE (1 << 8) /* IO mode select */ +#define CICR_ENABLE (1 << 10) /* Card enable */ +#define CICR_RESETOE (1 << 11) /* Card reset output enable */ +#define CICR_RESET (1 << 12) /* Card reset */ + + +#define RD_FAIL (1 << 14) +#define WR_FAIL (1 << 13) +#define IDLE (1 << 12) + +#define FFOTHLD (1 << 11) +#define PCM_RDYL (1 << 10) +#define PCM_WP (1 << 9) +#define PCTL (1 << 8) + +#define PDREQ_L (1 << 6) +#define PCM_VS2 (1 << 5) +#define PCM_VS1 (1 << 4) + +#define PCM_CD2 (1 << 3) +#define PCM_CD1 (1 << 2) +#define PCM_BVD2 (1 << 1) +#define PCM_BVD1 (1 << 0) diff -urN orig/drivers/pcmcia/cs.c linux/drivers/pcmcia/cs.c --- orig/drivers/pcmcia/cs.c Tue May 27 10:04:54 2003 +++ linux/drivers/pcmcia/cs.c Thu Jun 12 12:44:48 2003 @@ -48,6 +48,7 @@ #include #include #include +#include #include #include @@ -60,6 +61,7 @@ #include #include #include "cs_internal.h" +#include "map.h" #ifdef CONFIG_PCI #define PCI_OPT " [pci]" @@ -94,7 +96,7 @@ "\n options:" OPTIONS); MODULE_LICENSE("Dual MPL/GPL"); -#define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i") +#define INT_MODULE_PARM(n, v) int n = v; MODULE_PARM(n, "i") INT_MODULE_PARM(setup_delay, 10); /* centiseconds */ INT_MODULE_PARM(resume_delay, 20); /* centiseconds */ @@ -123,9 +125,11 @@ 0, SS_DETECT, 0, 0, 0 }; -/* Table of sockets */ -socket_t sockets = 0; -socket_info_t *socket_table[MAX_SOCK]; + +/* List of all sockets, protected by a rwsem */ +LIST_HEAD(pcmcia_socket_list); +DECLARE_RWSEM(pcmcia_socket_list_rwsem); + #ifdef CONFIG_PROC_FS struct proc_dir_entry *proc_pccard = NULL; @@ -234,48 +238,43 @@ ======================================================================*/ -static int register_callback(socket_info_t *s, void (*handler)(void *, unsigned int), void * info) +static int register_callback(struct pcmcia_socket *s, void (*handler)(void *, unsigned int), void * info) { int error; if (handler && !try_module_get(s->ss_entry->owner)) return -ENODEV; - error = s->ss_entry->register_callback(s->sock, handler, info); + error = s->ss_entry->register_callback(s, handler, info); if (!handler) module_put(s->ss_entry->owner); return error; } -static int get_socket_status(socket_info_t *s, int *val) +static int get_socket_status(struct pcmcia_socket *s, int *val) { - return s->ss_entry->get_status(s->sock, val); + return s->ss_entry->get_status(s, val); } -static int set_socket(socket_info_t *s, socket_state_t *state) +static int set_socket(struct pcmcia_socket *s, socket_state_t *state) { - return s->ss_entry->set_socket(s->sock, state); + return s->ss_entry->set_socket(s, state); } -static int set_io_map(socket_info_t *s, struct pccard_io_map *io) +static int set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io) { - return s->ss_entry->set_io_map(s->sock, io); + return s->ss_entry->set_io_map(s, io); } -static int set_mem_map(socket_info_t *s, struct pccard_mem_map *mem) -{ - return s->ss_entry->set_mem_map(s->sock, mem); -} - -static int suspend_socket(socket_info_t *s) +static int suspend_socket(struct pcmcia_socket *s) { s->socket = dead_socket; - return s->ss_entry->suspend(s->sock); + return s->ss_entry->suspend(s); } -static int init_socket(socket_info_t *s) +static int init_socket(struct pcmcia_socket *s) { s->socket = dead_socket; - return s->ss_entry->init(s->sock); + return s->ss_entry->init(s); } /*====================================================================*/ @@ -284,7 +283,7 @@ static int proc_read_clients(char *buf, char **start, off_t pos, int count, int *eof, void *data) { - socket_info_t *s = data; + struct pcmcia_socket *s = data; client_handle_t c; char *p = buf; @@ -302,145 +301,232 @@ ======================================================================*/ -static int pccardd(void *__skt); -void pcmcia_unregister_socket(struct class_device *dev); +/** + * socket drivers are expected to use the following callbacks in their + * .drv struct: + * - pcmcia_socket_dev_suspend + * - pcmcia_socket_dev_resume + * These functions check for the appropriate struct pcmcia_soket arrays, + * and pass them to the low-level functions pcmcia_{suspend,resume}_socket + */ +static int socket_resume(struct pcmcia_socket *skt); +static int socket_suspend(struct pcmcia_socket *skt); + +int pcmcia_socket_dev_suspend(struct device *dev, u32 state, u32 level) +{ + struct pcmcia_socket *socket; + + if (level != SUSPEND_SAVE_STATE) + return 0; + + down_read(&pcmcia_socket_list_rwsem); + list_for_each_entry(socket, &pcmcia_socket_list, socket_list) { + if (socket->dev.dev != dev) + continue; + down(&socket->skt_sem); + socket_suspend(socket); + up(&socket->skt_sem); + } + up_read(&pcmcia_socket_list_rwsem); + + return 0; +} +EXPORT_SYMBOL(pcmcia_socket_dev_suspend); + +int pcmcia_socket_dev_resume(struct device *dev, u32 level) +{ + struct pcmcia_socket *socket; + + if (level != RESUME_RESTORE_STATE) + return 0; + + down_read(&pcmcia_socket_list_rwsem); + list_for_each_entry(socket, &pcmcia_socket_list, socket_list) { + if (socket->dev.dev != dev) + continue; + down(&socket->skt_sem); + socket_resume(socket); + up(&socket->skt_sem); + } + up_read(&pcmcia_socket_list_rwsem); + return 0; +} +EXPORT_SYMBOL(pcmcia_socket_dev_resume); + + +static int pccardd(void *__skt); #define to_class_data(dev) dev->class_data -/** - * pcmcia_register_socket - add a new pcmcia socket device - */ -int pcmcia_register_socket(struct class_device *class_dev) +static int pcmcia_add_socket(struct class_device *class_dev) { - struct pcmcia_socket_class_data *cls_d = class_get_devdata(class_dev); - socket_info_t *s_info; - unsigned int i, j, ret; + struct pcmcia_socket *socket = class_get_devdata(class_dev); + int ret = 0; - if (!cls_d) - return -EINVAL; + /* base address = 0, map = 0 */ + socket->cis_mem.flags = 0; + socket->cis_mem.speed = cis_speed; + socket->erase_busy.next = socket->erase_busy.prev = &socket->erase_busy; + for (ret = 0; ret < MAX_IO_WIN; ret++) + INIT_LIST_HEAD(&socket->io[ret].reslist); + INIT_LIST_HEAD(&socket->cis_cache); + spin_lock_init(&socket->lock); - DEBUG(0, "cs: pcmcia_register_socket(0x%p)\n", cls_d->ops); + init_socket(socket); + socket->ss_entry->inquire_socket(socket, &socket->cap); - s_info = kmalloc(cls_d->nsock * sizeof(struct socket_info_t), GFP_KERNEL); - if (!s_info) - return -ENOMEM; - memset(s_info, 0, cls_d->nsock * sizeof(socket_info_t)); - - cls_d->s_info = s_info; - ret = 0; - - /* socket initialization */ - for (i = 0; i < cls_d->nsock; i++) { - socket_info_t *s = &s_info[i]; - - s->ss_entry = cls_d->ops; - s->sock = i + cls_d->sock_offset; - - /* base address = 0, map = 0 */ - s->cis_mem.flags = 0; - s->cis_mem.speed = cis_speed; - s->erase_busy.next = s->erase_busy.prev = &s->erase_busy; - INIT_LIST_HEAD(&s->cis_cache); - spin_lock_init(&s->lock); - - /* TBD: remove usage of socket_table, use class_for_each_dev instead */ - for (j = 0; j < sockets; j++) - if (socket_table[j] == NULL) break; - socket_table[j] = s; - if (j == sockets) sockets++; - - init_socket(s); - s->ss_entry->inquire_socket(s->sock, &s->cap); - - init_completion(&s->thread_done); - init_waitqueue_head(&s->thread_wait); - init_MUTEX(&s->skt_sem); - spin_lock_init(&s->thread_lock); - ret = kernel_thread(pccardd, s, CLONE_KERNEL); - if (ret < 0) { - pcmcia_unregister_socket(class_dev); - break; - } + if (socket->cap.features & SS_CAP_STATIC_MAP) + socket->map_ops = &smap_ops; + else + socket->map_ops = &winmap_ops; + + init_completion(&socket->thread_done); + init_waitqueue_head(&socket->thread_wait); + init_MUTEX(&socket->skt_sem); + spin_lock_init(&socket->thread_lock); + ret = kernel_thread(pccardd, socket, CLONE_KERNEL); + if (ret < 0) + return ret; - wait_for_completion(&s->thread_done); - BUG_ON(!s->thread); + wait_for_completion(&socket->thread_done); + BUG_ON(!socket->thread); #ifdef CONFIG_PROC_FS - if (proc_pccard) { - char name[3]; - sprintf(name, "%02d", j); - s->proc = proc_mkdir(name, proc_pccard); - if (s->proc) - s->ss_entry->proc_setup(i, s->proc); + if (proc_pccard) { + char name[3]; + sprintf(name, "%02d", socket->sock); + socket->proc = proc_mkdir(name, proc_pccard); + if (socket->proc) + socket->ss_entry->proc_setup(socket, socket->proc); #ifdef PCMCIA_DEBUG - if (s->proc) - create_proc_read_entry("clients", 0, s->proc, - proc_read_clients, s); + if (socket->proc) + create_proc_read_entry("clients", 0, socket->proc, + proc_read_clients, socket); #endif - } + } +#endif + return 0; +} + +static void pcmcia_remove_socket(struct class_device *class_dev) +{ + struct pcmcia_socket *socket = class_get_devdata(class_dev); + client_t *client; + +#ifdef CONFIG_PROC_FS + if (proc_pccard) { + char name[3]; + sprintf(name, "%02d", socket->sock); +#ifdef PCMCIA_DEBUG + remove_proc_entry("clients", socket->proc); #endif + remove_proc_entry(name, proc_pccard); } - return ret; +#endif + if (socket->thread) { + init_completion(&socket->thread_done); + socket->thread = NULL; + wake_up(&socket->thread_wait); + wait_for_completion(&socket->thread_done); + } + while (socket->clients) { + client = socket->clients; + socket->clients = socket->clients->next; + kfree(client); + } + socket->ss_entry = NULL; +} + + +/** + * pcmcia_register_socket - add a new pcmcia socket device + */ +int pcmcia_register_socket(struct pcmcia_socket *socket) +{ + if (!socket || !socket->ss_entry || !socket->dev.dev) + return -EINVAL; + + DEBUG(0, "cs: pcmcia_register_socket(0x%p)\n", socket->ss_entry); + + /* try to obtain a socket number [yes, it gets ugly if we + * register more than 2^sizeof(unsigned int) pcmcia + * sockets... but the socket number is deprecated + * anyways, so I don't care] */ + down_write(&pcmcia_socket_list_rwsem); + if (list_empty(&pcmcia_socket_list)) + socket->sock = 0; + else { + unsigned int found, i = 1; + struct pcmcia_socket *tmp; + do { + found = 1; + list_for_each_entry(tmp, &pcmcia_socket_list, socket_list) { + if (tmp->sock == i) + found = 0; + } + i++; + } while (!found); + socket->sock = i - 1; + } + list_add_tail(&socket->socket_list, &pcmcia_socket_list); + up_write(&pcmcia_socket_list_rwsem); + + + /* set proper values in socket->dev */ + socket->dev.class_data = socket; + socket->dev.class = &pcmcia_socket_class; + snprintf(socket->dev.class_id, BUS_ID_SIZE, "pcmcia_socket%u\n", socket->sock); + + /* register with the device core */ + if (class_device_register(&socket->dev)) { + down_write(&pcmcia_socket_list_rwsem); + list_del(&socket->socket_list); + up_write(&pcmcia_socket_list_rwsem); + return -EINVAL; + } + + return 0; } /* pcmcia_register_socket */ +EXPORT_SYMBOL(pcmcia_register_socket); /** * pcmcia_unregister_socket - remove a pcmcia socket device */ -void pcmcia_unregister_socket(struct class_device *class_dev) +void pcmcia_unregister_socket(struct pcmcia_socket *socket) { - struct pcmcia_socket_class_data *cls_d = class_get_devdata(class_dev); - unsigned int i; - int j, socket = -1; - client_t *client; - socket_info_t *s; - - if (!cls_d) + if (!socket) return; - s = (socket_info_t *) cls_d->s_info; + DEBUG(0, "cs: pcmcia_unregister_socket(0x%p)\n", socket->ss_entry); - for (i = 0; i < cls_d->nsock; i++) { - for (j = 0; j < MAX_SOCK; j++) - if (socket_table [j] == s) { - socket = j; - break; - } - if (socket < 0) - continue; - -#ifdef CONFIG_PROC_FS - if (proc_pccard) { - char name[3]; - sprintf(name, "%02d", socket); -#ifdef PCMCIA_DEBUG - remove_proc_entry("clients", s->proc); -#endif - remove_proc_entry(name, proc_pccard); - } -#endif - if (s->thread) { - init_completion(&s->thread_done); - s->thread = NULL; - wake_up(&s->thread_wait); - wait_for_completion(&s->thread_done); - } - release_cis_mem(s); - while (s->clients) { - client = s->clients; - s->clients = s->clients->next; - kfree(client); - } - s->ss_entry = NULL; - socket_table[socket] = NULL; - for (j = socket; j < sockets-1; j++) - socket_table[j] = socket_table[j+1]; - sockets--; + /* remove from the device core */ + class_device_unregister(&socket->dev); - s++; - } - kfree(cls_d->s_info); + /* remove from our own list */ + down_write(&pcmcia_socket_list_rwsem); + list_del(&socket->socket_list); + up_write(&pcmcia_socket_list_rwsem); } /* pcmcia_unregister_socket */ +EXPORT_SYMBOL(pcmcia_unregister_socket); + + +struct pcmcia_socket * pcmcia_get_socket_by_nr(unsigned int nr) +{ + struct pcmcia_socket *s; + + down_read(&pcmcia_socket_list_rwsem); + list_for_each_entry(s, &pcmcia_socket_list, socket_list) + if (s->sock == nr) { + up_read(&pcmcia_socket_list_rwsem); + return s; + } + up_read(&pcmcia_socket_list_rwsem); + + return NULL; + +} +EXPORT_SYMBOL(pcmcia_get_socket_by_nr); /*====================================================================== @@ -464,9 +550,9 @@ } } -static int send_event(socket_info_t *s, event_t event, int priority); +static int send_event(struct pcmcia_socket *s, event_t event, int priority); -static void shutdown_socket(socket_info_t *s) +static void shutdown_socket(struct pcmcia_socket *s) { client_t **c; @@ -521,7 +607,7 @@ ======================================================================*/ -static int send_event(socket_info_t *s, event_t event, int priority) +static int send_event(struct pcmcia_socket *s, event_t event, int priority) { client_t *client = s->clients; int ret; @@ -542,7 +628,7 @@ return ret; } /* send_event */ -static void pcmcia_error(socket_info_t *skt, const char *fmt, ...) +static void pcmcia_error(struct pcmcia_socket *skt, const char *fmt, ...) { static char buf[128]; va_list ap; @@ -558,7 +644,7 @@ #define cs_to_timeout(cs) (((cs) * HZ + 99) / 100) -static void socket_remove_drivers(socket_info_t *skt) +static void socket_remove_drivers(struct pcmcia_socket *skt) { client_t *client; @@ -569,16 +655,17 @@ client->state |= CLIENT_STALE; } -static void socket_shutdown(socket_info_t *skt) +static void socket_shutdown(struct pcmcia_socket *skt) { socket_remove_drivers(skt); set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(cs_to_timeout(shutdown_delay)); skt->state &= ~SOCKET_PRESENT; shutdown_socket(skt); + skt->map_ops->remove(skt); } -static int socket_reset(socket_info_t *skt) +static int socket_reset(struct pcmcia_socket *skt) { int status, i; @@ -608,7 +695,7 @@ return CS_GENERAL_FAILURE; } -static int socket_setup(socket_info_t *skt, int initial_delay) +static int socket_setup(struct pcmcia_socket *skt, int initial_delay) { int status, i; @@ -645,6 +732,26 @@ } /* + * Try to map CIS. If this fails, we don't have any resources, + * and we can forget doing anything further. We will receive + * an EV_RESOURCES event when the resource database changes. + * + * Allow eject, reset and suspend events - we don't want to block + * these indefinitely. + */ + do { + int ret = skt->map_ops->insert(skt); + if (ret == 0) + break; + if (ret != -EBUSY) { + pcmcia_error(skt, "map insert failed: %d\n", ret); + return CS_GENERAL_FAILURE; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ); + } while (1); + + /* * Decode the card voltage requirements, and apply power to the card. */ if (status & SS_3VCARD) @@ -672,7 +779,7 @@ * Handle card insertion. Setup the socket, reset the card, * and then tell the rest of PCMCIA that a card is present. */ -static int socket_insert(socket_info_t *skt) +static int socket_insert(struct pcmcia_socket *skt) { int ret; @@ -692,13 +799,14 @@ return ret; } -static int socket_suspend(socket_info_t *skt) +static int socket_suspend(struct pcmcia_socket *skt) { if (skt->state & SOCKET_SUSPEND) return CS_IN_USE; send_event(skt, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW); suspend_socket(skt); + skt->map_ops->remove(skt); skt->state |= SOCKET_SUSPEND; return CS_SUCCESS; @@ -709,7 +817,7 @@ * our cached copy. If they are different, the card has been * replaced, and we need to tell the drivers. */ -static int socket_resume(socket_info_t *skt) +static int socket_resume(struct pcmcia_socket *skt) { int ret; @@ -741,7 +849,7 @@ static int pccardd(void *__skt) { - socket_info_t *skt = __skt; + struct pcmcia_socket *skt = __skt; DECLARE_WAITQUEUE(wait, current); daemonize("pccardd"); @@ -783,6 +891,9 @@ } schedule(); + if (current->flags & PF_FREEZE) + refrigerator(PF_IOTHREAD); + if (!skt->thread) break; } @@ -795,7 +906,7 @@ static void parse_events(void *info, u_int events) { - socket_info_t *s = info; + struct pcmcia_socket *s = info; spin_lock(&s->thread_lock); s->thread_events |= events; @@ -804,154 +915,89 @@ wake_up(&s->thread_wait); } /* parse_events */ -/*====================================================================== - Another event handler, for power management events. +/*====================================================================== - This does not comply with the latest PC Card spec for handling - power management events. + Special stuff for managing IO windows, because they are scarce. ======================================================================*/ -void pcmcia_suspend_socket (socket_info_t *skt) -{ - down(&skt->skt_sem); - socket_suspend(skt); - up(&skt->skt_sem); -} - -void pcmcia_resume_socket (socket_info_t *skt) -{ - down(&skt->skt_sem); - socket_resume(skt); - up(&skt->skt_sem); -} - - -int pcmcia_socket_dev_suspend(struct pcmcia_socket_class_data *cls_d, u32 state, u32 level) +static int alloc_io_space(struct pcmcia_socket *s, u_int attr, ioaddr_t *base, + ioaddr_t num, u_int lines, char *name) { - socket_info_t *s; + struct io_resource *r, *n; + unsigned long align; + struct resource *res; int i; - if ((!cls_d) || (level != SUSPEND_SAVE_STATE)) - return 0; - - s = (socket_info_t *) cls_d->s_info; - - for (i = 0; i < cls_d->nsock; i++) { - pcmcia_suspend_socket(s); - s++; + align = (*base) ? (lines ? 1<io[i].reslist, node) { + if ((r->res->start & r->mask) == (*base & (align - 1))) + return 1; + } } - return 0; -} -EXPORT_SYMBOL(pcmcia_socket_dev_suspend); + res = s->map_ops->io_alloc(s, *base, num, align, attr, name); + if (res) { + n = kmalloc(sizeof(struct io_resource), GFP_KERNEL); + if (n) { + n->mask = align - 1; + n->res = res; + list_add(&n->node, &s->io[0].reslist); + *base = res->start; + return 0; + } + kfree(n); + } + return 1; +} /* alloc_io_space */ -int pcmcia_socket_dev_resume(struct pcmcia_socket_class_data *cls_d, u32 level) +static void release_io_space(struct pcmcia_socket *s, ioaddr_t base, + ioaddr_t num) { - socket_info_t *s; + unsigned long end = base + num - 1; int i; - if ((!cls_d) || (level != RESUME_RESTORE_STATE)) - return 0; - - s = (socket_info_t *) cls_d->s_info; - - for (i = 0; i < cls_d->nsock; i++) { - pcmcia_resume_socket(s); - s++; - } - - return 0; -} -EXPORT_SYMBOL(pcmcia_socket_dev_resume); - + for (i = 0; i < MAX_IO_WIN; i++) { + struct io_resource *r; -/*====================================================================== - - Special stuff for managing IO windows, because they are scarce. - -======================================================================*/ - -static int alloc_io_space(socket_info_t *s, u_int attr, ioaddr_t *base, - ioaddr_t num, u_int lines, char *name) -{ - int i; - ioaddr_t try, align; + list_for_each_entry(r, &s->io[i].reslist, node) { + if (r->res->start == base && r->res->end == end) { + list_del(&r->node); + release_resource(r->res); + kfree(r); + break; + } + } - align = (*base) ? (lines ? 1<cap.features & SS_CAP_STATIC_MAP) && s->cap.io_offset) { - *base = s->cap.io_offset | (*base & 0x0fff); - return 0; - } - /* Check for an already-allocated window that must conflict with - what was asked for. It is a hack because it does not catch all - 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)) - return 1; - for (i = 0; i < MAX_IO_WIN; i++) { - if (s->io[i].NumPorts == 0) { - if (find_io_region(base, num, align, name, s) == 0) { - s->io[i].Attributes = attr; - s->io[i].BasePort = *base; - s->io[i].NumPorts = s->io[i].InUse = num; - break; - } else - return 1; - } else if (s->io[i].Attributes != attr) - continue; - /* Try to extend top of window */ - try = s->io[i].BasePort + s->io[i].NumPorts; - if ((*base == 0) || (*base == try)) - if (find_io_region(&try, num, 0, name, 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; - if ((*base == 0) || (*base == try)) - if (find_io_region(&try, num, 0, name, s) == 0) { - s->io[i].BasePort = *base = try; - s->io[i].NumPorts += num; - s->io[i].InUse += num; - break; - } - } - return (i == MAX_IO_WIN); -} /* alloc_io_space */ + if (s->io[i].BasePort > base && + s->io[i].BasePort + s->io[i].NumPorts <= end) + continue; -static void release_io_space(socket_info_t *s, ioaddr_t base, - ioaddr_t num) -{ - int i; - if(!(s->cap.features & SS_CAP_STATIC_MAP)) - release_region(base, num); - for (i = 0; i < MAX_IO_WIN; i++) { - if ((s->io[i].BasePort <= base) && - (s->io[i].BasePort+s->io[i].NumPorts >= base+num)) { - 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; + 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; } - } } /*====================================================================== @@ -965,7 +1011,7 @@ int pcmcia_access_configuration_register(client_handle_t handle, conf_reg_t *reg) { - socket_info_t *s; + struct pcmcia_socket *s; config_t *c; int addr; u_char val; @@ -1016,18 +1062,18 @@ int pcmcia_bind_device(bind_req_t *req) { client_t *client; - socket_info_t *s; + struct pcmcia_socket *s; - if (CHECK_SOCKET(req->Socket)) - return CS_BAD_SOCKET; - s = SOCKET(req); + s = req->Socket; + if (!s) + return CS_BAD_SOCKET; client = (client_t *)kmalloc(sizeof(client_t), GFP_KERNEL); if (!client) return CS_OUT_OF_RESOURCE; memset(client, '\0', sizeof(client_t)); client->client_magic = CLIENT_MAGIC; strlcpy(client->dev_info, (char *)req->dev_info, DEV_NAME_LEN); - client->Socket = req->Socket; + client->Socket = s; client->Function = req->Function; client->state = CLIENT_UNBOUND; client->erase_busy.next = &client->erase_busy; @@ -1051,12 +1097,12 @@ int pcmcia_bind_mtd(mtd_bind_t *req) { - socket_info_t *s; + struct pcmcia_socket *s; memory_handle_t region; - if (CHECK_SOCKET(req->Socket)) - return CS_BAD_SOCKET; - s = SOCKET(req); + s = req->Socket; + if (!s) + return CS_BAD_SOCKET; if (req->Attributes & REGION_TYPE_AM) region = s->a_region; @@ -1081,10 +1127,10 @@ int pcmcia_deregister_client(client_handle_t handle) { client_t **client; - socket_info_t *s; + struct pcmcia_socket *s; memory_handle_t region; u_long flags; - int i, sn; + int i; DEBUG(1, "cs: deregister_client(%p)\n", handle); if (CHECK_HANDLE(handle)) @@ -1105,8 +1151,6 @@ if (region->mtd == handle) region->mtd = NULL; } - sn = handle->Socket; s = socket_table[sn]; - if ((handle->state & CLIENT_STALE) || (handle->Attributes & INFO_MASTER_CLIENT)) { spin_lock_irqsave(&s->lock, flags); @@ -1138,7 +1182,7 @@ int pcmcia_get_configuration_info(client_handle_t handle, config_info_t *config) { - socket_info_t *s; + struct pcmcia_socket *s; config_t *c; if (CHECK_HANDLE(handle)) @@ -1208,9 +1252,15 @@ int pcmcia_get_card_services_info(servinfo_t *info) { + unsigned int socket_count = 0; + struct list_head *tmp; info->Signature[0] = 'C'; info->Signature[1] = 'S'; - info->Count = sockets; + down_read(&pcmcia_socket_list_rwsem); + list_for_each(tmp, &pcmcia_socket_list) + socket_count++; + up_read(&pcmcia_socket_list_rwsem); + info->Count = socket_count; info->Revision = CS_RELEASE_CODE; info->CSLevel = 0x0210; info->VendorString = (char *)release; @@ -1227,15 +1277,17 @@ int pcmcia_get_first_client(client_handle_t *handle, client_req_t *req) { socket_t s; + struct pcmcia_socket *socket; if (req->Attributes & CLIENT_THIS_SOCKET) s = req->Socket; else s = 0; - if (CHECK_SOCKET(req->Socket)) + socket = pcmcia_get_socket_by_nr(s); + if (!socket) return CS_BAD_SOCKET; - if (socket_table[s]->clients == NULL) + if (socket->clients == NULL) return CS_NO_MORE_ITEMS; - *handle = socket_table[s]->clients; + *handle = socket->clients; return CS_SUCCESS; } /* get_first_client */ @@ -1243,13 +1295,13 @@ int pcmcia_get_next_client(client_handle_t *handle, client_req_t *req) { - socket_info_t *s; + struct pcmcia_socket *s; if ((handle == NULL) || CHECK_HANDLE(*handle)) return CS_BAD_HANDLE; if ((*handle)->next == NULL) { if (req->Attributes & CLIENT_THIS_SOCKET) return CS_NO_MORE_ITEMS; - s = SOCKET(*handle); + s = (*handle)->Socket; if (s->clients == NULL) return CS_NO_MORE_ITEMS; *handle = s->clients; @@ -1262,12 +1314,12 @@ int pcmcia_get_window(window_handle_t *handle, int idx, win_req_t *req) { - socket_info_t *s; + struct pcmcia_socket *s; window_t *win; int w; if (idx == 0) - s = SOCKET((client_handle_t)*handle); + s = ((client_handle_t)*handle)->Socket; else s = (*handle)->sock; if (!(s->state & SOCKET_PRESENT)) @@ -1277,8 +1329,8 @@ if (w == MAX_WIN) return CS_NO_MORE_ITEMS; win = &s->win[w]; - req->Base = win->ctl.sys_start; - req->Size = win->ctl.sys_stop - win->ctl.sys_start + 1; + req->Base = win->ctl.res->start; + req->Size = win->ctl.res->end - win->ctl.res->start + 1; req->AccessSpeed = win->ctl.speed; req->Attributes = 0; if (win->ctl.flags & MAP_ATTRIB) @@ -1317,7 +1369,7 @@ struct pci_bus *pcmcia_lookup_bus(client_handle_t handle) { - socket_info_t *s; + struct pcmcia_socket *s; if (CHECK_HANDLE(handle)) return NULL; @@ -1341,7 +1393,7 @@ int pcmcia_get_status(client_handle_t handle, cs_status_t *status) { - socket_info_t *s; + struct pcmcia_socket *s; config_t *c; int val; @@ -1420,15 +1472,18 @@ int pcmcia_map_mem_page(window_handle_t win, memreq_t *req) { - socket_info_t *s; + struct pcmcia_socket *s; if ((win == NULL) || (win->magic != WINDOW_MAGIC)) return CS_BAD_HANDLE; if (req->Page != 0) return CS_BAD_PAGE; + s = win->sock; - win->ctl.card_start = req->CardOffset; - if (set_mem_map(s, &win->ctl) != 0) + + if (s->map_ops->mem_modify(s, &win->ctl, req->CardOffset, + win->ctl.speed, win->ctl.flags)) return CS_BAD_OFFSET; + return CS_SUCCESS; } /* map_mem_page */ @@ -1441,7 +1496,7 @@ int pcmcia_modify_configuration(client_handle_t handle, modconf_t *mod) { - socket_info_t *s; + struct pcmcia_socket *s; config_t *c; if (CHECK_HANDLE(handle)) @@ -1489,22 +1544,26 @@ int pcmcia_modify_window(window_handle_t win, modwin_t *req) { + struct pcmcia_socket *s; + unsigned int flags; + if ((win == NULL) || (win->magic != WINDOW_MAGIC)) return CS_BAD_HANDLE; - win->ctl.flags &= ~(MAP_ATTRIB|MAP_ACTIVE); + flags = win->ctl.flags & ~(MAP_ATTRIB|MAP_ACTIVE); if (req->Attributes & WIN_MEMORY_TYPE) - win->ctl.flags |= MAP_ATTRIB; + flags |= MAP_ATTRIB; if (req->Attributes & WIN_ENABLE) - win->ctl.flags |= MAP_ACTIVE; + flags |= MAP_ACTIVE; if (req->Attributes & WIN_DATA_WIDTH_16) - win->ctl.flags |= MAP_16BIT; + flags |= MAP_16BIT; if (req->Attributes & WIN_USE_WAIT) - win->ctl.flags |= MAP_USE_WAIT; - win->ctl.speed = req->AccessSpeed; - set_mem_map(win->sock, &win->ctl); - - return CS_SUCCESS; + flags |= MAP_USE_WAIT; + + s = win->sock; + + return s->map_ops->mem_modify(s, &win->ctl, win->ctl.card_start, + req->AccessSpeed, flags); } /* modify_window */ /*====================================================================== @@ -1518,14 +1577,13 @@ int pcmcia_register_client(client_handle_t *handle, client_reg_t *req) { - client_t *client; - socket_info_t *s; - socket_t ns; + client_t *client = NULL; + struct pcmcia_socket *s; /* Look for unbound client with matching dev_info */ - client = NULL; - for (ns = 0; ns < sockets; ns++) { - client = socket_table[ns]->clients; + down_read(&pcmcia_socket_list_rwsem); + list_for_each_entry(s, &pcmcia_socket_list, socket_list) { + client = s->clients; while (client != NULL) { if ((strcmp(client->dev_info, (char *)req->dev_info) == 0) && (client->state & CLIENT_UNBOUND)) break; @@ -1533,10 +1591,10 @@ } if (client != NULL) break; } + up_read(&pcmcia_socket_list_rwsem); if (client == NULL) return CS_OUT_OF_RESOURCE; - s = socket_table[ns]; if (++s->real_clients == 1) { register_callback(s, &parse_events, s); parse_events(s, SS_DETECT); @@ -1544,7 +1602,7 @@ *handle = client; client->state &= ~CLIENT_UNBOUND; - client->Socket = ns; + client->Socket = s; client->Attributes = req->Attributes; client->EventMask = req->EventMask; client->event_handler = req->event_handler; @@ -1573,8 +1631,8 @@ client, client->Socket, client->dev_info); if (client->EventMask & CS_EVENT_REGISTRATION_COMPLETE) EVENT(client, CS_EVENT_REGISTRATION_COMPLETE, CS_EVENT_PRI_LOW); - if ((socket_table[ns]->state & SOCKET_PRESENT) && - !(socket_table[ns]->state & SOCKET_SETUP_PENDING)) { + if ((s->state & SOCKET_PRESENT) && + !(s->state & SOCKET_SETUP_PENDING)) { if (client->EventMask & CS_EVENT_CARD_INSERTION) EVENT(client, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW); else @@ -1588,7 +1646,7 @@ int pcmcia_release_configuration(client_handle_t handle) { pccard_io_map io = { 0, 0, 0, 0, 1 }; - socket_info_t *s; + struct pcmcia_socket *s; int i; if (CHECK_HANDLE(handle) || @@ -1638,7 +1696,7 @@ int pcmcia_release_io(client_handle_t handle, io_req_t *req) { - socket_info_t *s; + struct pcmcia_socket *s; if (CHECK_HANDLE(handle) || !(handle->state & CLIENT_IO_REQ)) return CS_BAD_HANDLE; @@ -1673,7 +1731,7 @@ int pcmcia_release_irq(client_handle_t handle, irq_req_t *req) { - socket_info_t *s; + struct pcmcia_socket *s; if (CHECK_HANDLE(handle) || !(handle->state & CLIENT_IRQ_REQ)) return CS_BAD_HANDLE; handle->state &= ~CLIENT_IRQ_REQ; @@ -1709,7 +1767,7 @@ int pcmcia_release_window(window_handle_t win) { - socket_info_t *s; + struct pcmcia_socket *s; if ((win == NULL) || (win->magic != WINDOW_MAGIC)) return CS_BAD_HANDLE; @@ -1719,12 +1777,10 @@ /* Shut down memory window */ win->ctl.flags &= ~MAP_ACTIVE; - set_mem_map(s, &win->ctl); - s->state &= ~SOCKET_WIN_REQ(win->index); - /* Release system memory */ - if(!(s->cap.features & SS_CAP_STATIC_MAP)) - release_mem_region(win->base, win->size); + s->map_ops->mem_unmap(s, &win->ctl); + + s->state &= ~SOCKET_WIN_REQ(win->index); win->handle->state &= ~CLIENT_WIN_REQ(win->index); win->magic = 0; @@ -1739,13 +1795,13 @@ { int i; u_int base; - socket_info_t *s; + struct pcmcia_socket *s; config_t *c; pccard_io_map iomap; if (CHECK_HANDLE(handle)) return CS_BAD_HANDLE; - i = handle->Socket; s = socket_table[i]; + s = SOCKET(handle); if (!(s->state & SOCKET_PRESENT)) return CS_NO_CARD; @@ -1868,7 +1924,7 @@ int pcmcia_request_io(client_handle_t handle, io_req_t *req) { - socket_info_t *s; + struct pcmcia_socket *s; config_t *c; if (CHECK_HANDLE(handle)) @@ -1932,7 +1988,7 @@ int pcmcia_request_irq(client_handle_t handle, irq_req_t *req) { - socket_info_t *s; + struct pcmcia_socket *s; config_t *c; int ret = 0, irq = 0; @@ -2007,14 +2063,13 @@ int pcmcia_request_window(client_handle_t *handle, win_req_t *req, window_handle_t *wh) { - socket_info_t *s; + struct pcmcia_socket *s; window_t *win; - u_long align; - int w; + int w, ret; if (CHECK_HANDLE(*handle)) return CS_BAD_HANDLE; - s = SOCKET(*handle); + s = (*handle)->Socket; if (!(s->state & SOCKET_PRESENT)) return CS_NO_CARD; if (req->Attributes & (WIN_PAGED | WIN_SHARED)) @@ -2023,16 +2078,8 @@ /* Window size defaults to smallest available */ if (req->Size == 0) req->Size = s->cap.map_size; - align = (((s->cap.features & SS_CAP_MEM_ALIGN) || - (req->Attributes & WIN_STRICT_ALIGN)) ? - req->Size : s->cap.map_size); if (req->Size & (s->cap.map_size-1)) return CS_BAD_SIZE; - if ((req->Base && (s->cap.features & SS_CAP_STATIC_MAP)) || - (req->Base & (align-1))) - return CS_BAD_BASE; - if (req->Base) - align = 0; /* Allocate system memory window */ for (w = 0; w < MAX_WIN; w++) @@ -2045,18 +2092,6 @@ win->index = w; win->handle = *handle; win->sock = s; - win->base = req->Base; - win->size = req->Size; - - if (!(s->cap.features & SS_CAP_STATIC_MAP) && - find_mem_region(&win->base, win->size, align, - (req->Attributes & WIN_MAP_BELOW_1MB) || - !(s->cap.features & SS_CAP_PAGE_REGS), - (*handle)->dev_info, s)) - return CS_IN_USE; - (*handle)->state |= CLIENT_WIN_REQ(w); - - /* Configure the socket controller */ win->ctl.map = w+1; win->ctl.flags = 0; win->ctl.speed = req->AccessSpeed; @@ -2068,18 +2103,19 @@ win->ctl.flags |= MAP_16BIT; if (req->Attributes & WIN_USE_WAIT) win->ctl.flags |= MAP_USE_WAIT; - win->ctl.sys_start = win->base; - win->ctl.sys_stop = win->base + win->size-1; win->ctl.card_start = 0; - if (set_mem_map(s, &win->ctl) != 0) - return CS_BAD_ARGS; - s->state |= SOCKET_WIN_REQ(w); - /* Return window handle */ - req->Base = win->ctl.sys_start; - *wh = win; + ret = s->map_ops->mem_map(s, &win->ctl, req, (*handle)->dev_info); + if (ret == CS_SUCCESS) { + (*handle)->state |= CLIENT_WIN_REQ(w); + s->state |= SOCKET_WIN_REQ(w); + + /* Return window handle */ + req->Base = win->ctl.res->start; + *wh = win; + } - return CS_SUCCESS; + return ret; } /* request_window */ /*====================================================================== @@ -2092,7 +2128,7 @@ int pcmcia_reset_card(client_handle_t handle, client_req_t *req) { - socket_info_t *skt; + struct pcmcia_socket *skt; int ret; if (CHECK_HANDLE(handle)) @@ -2141,7 +2177,7 @@ int pcmcia_suspend_card(client_handle_t handle, client_req_t *req) { - socket_info_t *skt; + struct pcmcia_socket *skt; int ret; if (CHECK_HANDLE(handle)) @@ -2168,7 +2204,7 @@ int pcmcia_resume_card(client_handle_t handle, client_req_t *req) { - socket_info_t *skt; + struct pcmcia_socket *skt; int ret; if (CHECK_HANDLE(handle)) @@ -2201,7 +2237,7 @@ int pcmcia_eject_card(client_handle_t handle, client_req_t *req) { - socket_info_t *skt; + struct pcmcia_socket *skt; int ret; if (CHECK_HANDLE(handle)) @@ -2230,7 +2266,7 @@ int pcmcia_insert_card(client_handle_t handle, client_req_t *req) { - socket_info_t *skt; + struct pcmcia_socket *skt; int ret; if (CHECK_HANDLE(handle)) @@ -2515,11 +2551,6 @@ EXPORT_SYMBOL(proc_pccard); #endif -EXPORT_SYMBOL(pcmcia_register_socket); -EXPORT_SYMBOL(pcmcia_unregister_socket); -EXPORT_SYMBOL(pcmcia_suspend_socket); -EXPORT_SYMBOL(pcmcia_resume_socket); - struct class pcmcia_socket_class = { .name = "pcmcia_socket", }; @@ -2527,8 +2558,8 @@ static struct class_interface pcmcia_socket = { .class = &pcmcia_socket_class, - .add = &pcmcia_register_socket, - .remove = &pcmcia_unregister_socket, + .add = &pcmcia_add_socket, + .remove = &pcmcia_remove_socket, }; diff -urN orig/drivers/pcmcia/cs_internal.h linux/drivers/pcmcia/cs_internal.h --- orig/drivers/pcmcia/cs_internal.h Tue May 27 10:04:54 2003 +++ linux/drivers/pcmcia/cs_internal.h Thu Jun 12 12:44:44 2003 @@ -21,13 +21,6 @@ #include -typedef struct erase_busy_t { - eraseq_entry_t *erase; - client_handle_t client; - struct timer_list timeout; - struct erase_busy_t *prev, *next; -} erase_busy_t; - #define ERASEQ_MAGIC 0xFA67 typedef struct eraseq_t { u_short eraseq_magic; @@ -39,7 +32,7 @@ #define CLIENT_MAGIC 0x51E6 typedef struct client_t { u_short client_magic; - socket_t Socket; + struct pcmcia_socket *Socket; u_char Function; dev_info_t dev_info; u_int Attributes; @@ -63,23 +56,6 @@ #define CLIENT_WIN_REQ(i) (0x20<<(i)) #define CLIENT_CARDBUS 0x8000 -typedef struct io_window_t { - u_int Attributes; - ioaddr_t BasePort, NumPorts; - ioaddr_t InUse, Config; -} io_window_t; - -#define WINDOW_MAGIC 0xB35C -typedef struct window_t { - u_short magic; - u_short index; - client_handle_t handle; - struct socket_info_t *sock; - u_long base; - u_long size; - pccard_mem_map ctl; -} window_t; - #define REGION_MAGIC 0xE3C9 typedef struct region_t { u_short region_magic; @@ -108,12 +84,6 @@ } irq; } config_t; -/* Maximum number of IO windows per socket */ -#define MAX_IO_WIN 2 - -/* Maximum number of memory windows per socket */ -#define MAX_WIN 4 - struct cis_cache_entry { struct list_head node; unsigned int addr; @@ -122,48 +92,6 @@ unsigned char cache[0]; }; -typedef struct socket_info_t { - spinlock_t lock; - struct pccard_operations * ss_entry; - u_int sock; - socket_state_t socket; - socket_cap_t cap; - u_int state; - u_short functions; - u_short lock_count; - client_handle_t clients; - u_int real_clients; - pccard_mem_map cis_mem; - u_char *cis_virt; - config_t *config; -#ifdef CONFIG_CARDBUS - struct resource * cb_cis_res; - u_char *cb_cis_virt; -#endif - struct { - u_int AssignedIRQ; - u_int Config; - } irq; - io_window_t io[MAX_IO_WIN]; - window_t win[MAX_WIN]; - region_t *c_region, *a_region; - erase_busy_t erase_busy; - struct list_head cis_cache; - u_int fake_cis_len; - char *fake_cis; -#ifdef CONFIG_PROC_FS - struct proc_dir_entry *proc; -#endif - - struct semaphore skt_sem; /* protects socket h/w state */ - - struct task_struct *thread; - struct completion thread_done; - wait_queue_head_t thread_wait; - spinlock_t thread_lock; /* protects thread_events */ - unsigned int thread_events; -} socket_info_t; - /* Flags in config state */ #define CONFIG_LOCKED 0x01 #define CONFIG_IRQ_REQ 0x02 @@ -187,7 +115,7 @@ #define CHECK_SOCKET(s) \ (((s) >= sockets) || (socket_table[s]->ss_entry == NULL)) -#define SOCKET(h) (socket_table[(h)->Socket]) +#define SOCKET(h) (h->Socket) #define CONFIG(h) (&SOCKET(h)->config[(h)->Function]) #define CHECK_REGION(r) \ @@ -200,19 +128,18 @@ ((h)->event_handler((e), (p), &(h)->event_callback_args)) /* In cardbus.c */ -int cb_alloc(socket_info_t *s); -void cb_free(socket_info_t *s); -int read_cb_mem(socket_info_t *s, int space, u_int addr, u_int len, void *ptr); +int cb_alloc(struct pcmcia_socket *s); +void cb_free(struct pcmcia_socket *s); +int read_cb_mem(struct pcmcia_socket *s, int space, u_int addr, u_int len, void *ptr); /* In cistpl.c */ -int read_cis_mem(socket_info_t *s, int attr, +int read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr); -void write_cis_mem(socket_info_t *s, int attr, +void write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr, u_int len, void *ptr); -void release_cis_mem(socket_info_t *s); -void destroy_cis_cache(socket_info_t *s); -int verify_cis_cache(socket_info_t *s); -void preload_cis_cache(socket_info_t *s); +void destroy_cis_cache(struct pcmcia_socket *s); +int verify_cis_cache(struct pcmcia_socket *s); +void preload_cis_cache(struct pcmcia_socket *s); int get_first_tuple(client_handle_t handle, tuple_t *tuple); int get_next_tuple(client_handle_t handle, tuple_t *tuple); int get_tuple_data(client_handle_t handle, tuple_t *tuple); @@ -236,11 +163,11 @@ int copy_memory(memory_handle_t handle, copy_op_t *req); /* In rsrc_mgr */ -void validate_mem(socket_info_t *s); -int find_io_region(ioaddr_t *base, ioaddr_t num, ioaddr_t align, - char *name, socket_info_t *s); -int find_mem_region(u_long *base, u_long num, u_long align, - int force_low, char *name, socket_info_t *s); +void validate_mem(struct pcmcia_socket *s); +struct resource *find_io_region(u_long base, u_long num, u_long align, + char *name, struct pcmcia_socket *s); +struct resource *find_mem_region(u_long base, u_long num, u_long align, + int low, char *name, struct pcmcia_socket *s); int try_irq(u_int Attributes, int irq, int specific); void undo_irq(u_int Attributes, int irq); int adjust_resource_info(client_handle_t handle, adjust_t *adj); @@ -250,9 +177,8 @@ int proc_read_mem(char *buf, char **start, off_t pos, int count, int *eof, void *data); -#define MAX_SOCK 8 -extern socket_t sockets; -extern socket_info_t *socket_table[MAX_SOCK]; +extern struct rw_semaphore pcmcia_socket_list_rwsem; +extern struct list_head pcmcia_socket_list; #ifdef CONFIG_PROC_FS extern struct proc_dir_entry *proc_pccard; diff -urN orig/drivers/pcmcia/ds.c linux/drivers/pcmcia/ds.c --- orig/drivers/pcmcia/ds.c Tue May 27 10:04:55 2003 +++ linux/drivers/pcmcia/ds.c Wed Jun 11 21:57:28 2003 @@ -107,8 +107,7 @@ struct work_struct removal; socket_bind_t *bind; struct device *socket_dev; - struct list_head socket_list; - unsigned int socket_no; /* deprecated */ + struct pcmcia_socket *parent; }; #define SOCKET_PRESENT 0x01 @@ -122,10 +121,6 @@ static int major_dev = -1; -/* list of all sockets registered with the pcmcia bus driver */ -static DECLARE_RWSEM(bus_socket_list_rwsem); -static LIST_HEAD(bus_socket_list); - extern struct proc_dir_entry *proc_pccard; /*====================================================================*/ @@ -164,68 +159,10 @@ */ void pcmcia_unregister_driver(struct pcmcia_driver *driver) { - socket_bind_t *b; - struct pcmcia_bus_socket *bus_sock; - - if (driver->use_count > 0) { - /* Blank out any left-over device instances */ - driver->attach = NULL; driver->detach = NULL; - down_read(&bus_socket_list_rwsem); - list_for_each_entry(bus_sock, &bus_socket_list, socket_list) { - for (b = bus_sock->bind; b; b = b->next) - if (b->driver == driver) - b->instance = NULL; - } - up_read(&bus_socket_list_rwsem); - } driver_unregister(&driver->drv); } EXPORT_SYMBOL(pcmcia_unregister_driver); - -int register_pccard_driver(dev_info_t *dev_info, - dev_link_t *(*attach)(void), - void (*detach)(dev_link_t *)) -{ - struct pcmcia_driver *driver; - - DEBUG(0, "ds: register_pccard_driver('%s')\n", (char *)dev_info); - driver = get_pcmcia_driver(dev_info); - if (driver) - return -EBUSY; - - driver = kmalloc(sizeof(struct pcmcia_driver), GFP_KERNEL); - if (!driver) return -ENOMEM; - memset(driver, 0, sizeof(struct pcmcia_driver)); - driver->drv.name = (char *)dev_info; - pcmcia_register_driver(driver); - - driver->attach = attach; - driver->detach = detach; - - return 0; -} /* register_pccard_driver */ - -/*====================================================================*/ - -int unregister_pccard_driver(dev_info_t *dev_info) -{ - struct pcmcia_driver *driver; - - DEBUG(0, "ds: unregister_pccard_driver('%s')\n", - (char *)dev_info); - - driver = get_pcmcia_driver(dev_info); - if (!driver) - return -ENODEV; - - pcmcia_unregister_driver(driver); - kfree(driver); - return 0; -} /* unregister_pccard_driver */ - -/*====================================================================*/ - #ifdef CONFIG_PROC_FS static int proc_read_drivers_callback(struct device_driver *driver, void *d) { @@ -363,14 +300,14 @@ bind_req.dev_info = &mtd_info->dev_info; bind_req.Attributes = mtd_info->Attributes; - bind_req.Socket = bus_sock->socket_no; + bind_req.Socket = bus_sock->parent; bind_req.CardOffset = mtd_info->CardOffset; ret = pcmcia_bind_mtd(&bind_req); if (ret != CS_SUCCESS) { cs_error(NULL, BindMTD, ret); printk(KERN_NOTICE "ds: unable to bind MTD '%s' to socket %d" " offset 0x%x\n", - (char *)bind_req.dev_info, bus_sock->socket_no, bind_req.CardOffset); + (char *)bind_req.dev_info, bus_sock->parent->sock, bind_req.CardOffset); return -ENODEV; } return 0; @@ -395,7 +332,7 @@ if (!s) return -EINVAL; - DEBUG(2, "bind_request(%d, '%s')\n", s->socket_no, + DEBUG(2, "bind_request(%d, '%s')\n", s->parent->sock, (char *)bind_info->dev_info); driver = get_pcmcia_driver(&bind_info->dev_info); if (!driver) @@ -410,14 +347,18 @@ return -EBUSY; } - bind_req.Socket = s->socket_no; + if (!try_module_get(driver->owner)) + return -EINVAL; + + bind_req.Socket = s->parent; bind_req.Function = bind_info->function; bind_req.dev_info = (dev_info_t *) driver->drv.name; ret = pcmcia_bind_device(&bind_req); if (ret != CS_SUCCESS) { cs_error(NULL, BindDevice, ret); printk(KERN_NOTICE "ds: unable to bind '%s' to socket %d\n", - (char *)dev_info, s->socket_no); + (char *)dev_info, s->parent->sock); + module_put(driver->owner); return -ENODEV; } @@ -427,6 +368,7 @@ if (!b) { driver->use_count--; + module_put(driver->owner); return -ENOMEM; } b->driver = driver; @@ -440,6 +382,7 @@ if (b->instance == NULL) { printk(KERN_NOTICE "ds: unable to create instance " "of '%s'!\n", (char *)bind_info->dev_info); + module_put(driver->owner); return -ENODEV; } } @@ -520,7 +463,7 @@ { socket_bind_t **b, *c; - DEBUG(2, "unbind_request(%d, '%s')\n", s->socket_no, + DEBUG(2, "unbind_request(%d, '%s')\n", s->parent->sock, (char *)bind_info->dev_info); for (b = &s->bind; *b; b = &(*b)->next) if ((strcmp((char *)(*b)->driver->drv.name, @@ -536,9 +479,9 @@ if (c->instance) c->driver->detach(c->instance); } + module_put(c->driver->owner); *b = c->next; kfree(c); - return 0; } /* unbind_request */ @@ -876,18 +819,13 @@ .poll = ds_poll, }; -EXPORT_SYMBOL(register_pccard_driver); -EXPORT_SYMBOL(unregister_pccard_driver); - -/*====================================================================*/ - -static int __devinit pcmcia_bus_add_socket(struct device *dev, unsigned int socket_nr) +static int __devinit pcmcia_bus_add_socket(struct class_device *class_dev) { + struct pcmcia_socket *socket = class_dev->class_data; client_reg_t client_reg; bind_req_t bind; - struct pcmcia_bus_socket *s, *tmp_s; + struct pcmcia_bus_socket *s; int ret; - int i; s = kmalloc(sizeof(struct pcmcia_bus_socket), GFP_KERNEL); if(!s) @@ -904,28 +842,15 @@ init_waitqueue_head(&s->queue); init_waitqueue_head(&s->request); - /* find the lowest, unused socket no. Please note that this is a - * temporary workaround until "struct pcmcia_socket" is introduced - * into cs.c which will include this number, and which will be - * accessible to ds.c directly */ - i = 0; - next_try: - list_for_each_entry(tmp_s, &bus_socket_list, socket_list) { - if (tmp_s->socket_no == i) { - i++; - goto next_try; - } - } - s->socket_no = i; - /* initialize data */ - s->socket_dev = dev; + s->socket_dev = socket->dev.dev; INIT_WORK(&s->removal, handle_removal, s); - + s->parent = socket; + /* Set up hotline to Card Services */ client_reg.dev_info = bind.dev_info = &dev_info; - bind.Socket = s->socket_no; + bind.Socket = socket; bind.Function = BIND_FN_ALL; ret = pcmcia_bind_device(&bind); if (ret != CS_SUCCESS) { @@ -950,50 +875,26 @@ return -EINVAL; } - list_add(&s->socket_list, &bus_socket_list); + socket->pcmcia = s; return 0; } -static int pcmcia_bus_add_socket_dev(struct class_device *class_dev) +static void pcmcia_bus_remove_socket(struct class_device *class_dev) { - struct pcmcia_socket_class_data *cls_d = class_get_devdata(class_dev); - unsigned int i; - unsigned int ret = 0; + struct pcmcia_socket *socket = class_dev->class_data; - if (!cls_d) - return -ENODEV; - - down_write(&bus_socket_list_rwsem); - for (i = 0; i < cls_d->nsock; i++) - ret += pcmcia_bus_add_socket(class_dev->dev, i); - up_write(&bus_socket_list_rwsem); - - return ret; -} - -static void pcmcia_bus_remove_socket_dev(struct class_device *class_dev) -{ - struct pcmcia_socket_class_data *cls_d = class_get_devdata(class_dev); - struct list_head *list_loop; - struct list_head *tmp_storage; - - if (!cls_d) + if (!socket || !socket->pcmcia) return; flush_scheduled_work(); - down_write(&bus_socket_list_rwsem); - list_for_each_safe(list_loop, tmp_storage, &bus_socket_list) { - struct pcmcia_bus_socket *bus_sock = container_of(list_loop, struct pcmcia_bus_socket, socket_list); - if (bus_sock->socket_dev == class_dev->dev) { - pcmcia_deregister_client(bus_sock->handle); - list_del(&bus_sock->socket_list); - kfree(bus_sock); - } - } - up_write(&bus_socket_list_rwsem); + pcmcia_deregister_client(socket->pcmcia->handle); + + kfree(socket->pcmcia); + socket->pcmcia = NULL; + return; } @@ -1001,8 +902,8 @@ /* the pcmcia_bus_interface is used to handle pcmcia socket devices */ static struct class_interface pcmcia_bus_interface = { .class = &pcmcia_socket_class, - .add = &pcmcia_bus_add_socket_dev, - .remove = &pcmcia_bus_remove_socket_dev, + .add = &pcmcia_bus_add_socket, + .remove = &pcmcia_bus_remove_socket, }; @@ -1057,18 +958,13 @@ /* helpers for backwards-compatible functions */ - static struct pcmcia_bus_socket * get_socket_info_by_nr(unsigned int nr) { - struct pcmcia_bus_socket * s; - down_read(&bus_socket_list_rwsem); - list_for_each_entry(s, &bus_socket_list, socket_list) - if (s->socket_no == nr) { - up_read(&bus_socket_list_rwsem); - return s; - } - up_read(&bus_socket_list_rwsem); - return NULL; + struct pcmcia_socket * s = pcmcia_get_socket_by_nr(nr); + if (s && s->pcmcia) + return s->pcmcia; + else + return NULL; } /* backwards-compatible accessing of driver --- by name! */ diff -urN orig/drivers/pcmcia/i82092.c linux/drivers/pcmcia/i82092.c --- orig/drivers/pcmcia/i82092.c Tue May 27 10:04:55 2003 +++ linux/drivers/pcmcia/i82092.c Wed Jun 11 20:55:09 2003 @@ -44,14 +44,12 @@ static int i82092aa_socket_suspend (struct pci_dev *dev, u32 state) { - struct pcmcia_socket_class_data *cls_d = pci_get_drvdata(dev); - return pcmcia_socket_dev_suspend(cls_d, state, 0); + return pcmcia_socket_dev_suspend(&dev->dev, state, 0); } static int i82092aa_socket_resume (struct pci_dev *dev) { - struct pcmcia_socket_class_data *cls_d = pci_get_drvdata(dev); - return pcmcia_socket_dev_resume(cls_d, RESUME_RESTORE_STATE); + return pcmcia_socket_dev_resume(&dev->dev, RESUME_RESTORE_STATE); } static struct pci_driver i82092aa_pci_drv = { @@ -82,6 +80,7 @@ /* The card can do upto 4 sockets, allocate a structure for each of them */ struct socket_info { + int number; int card_state; /* 0 = no socket, 1 = empty socket, 2 = card but not initialized, @@ -95,6 +94,7 @@ /* callback to the driver of the card */ void *info; /* to be passed to the handler */ + struct pcmcia_socket socket; struct pci_dev *dev; /* The PCI device for the socket */ }; @@ -107,7 +107,6 @@ { unsigned char configbyte; int i, ret; - struct pcmcia_socket_class_data *cls_d; enter("i82092aa_pci_probe"); @@ -146,6 +145,8 @@ sockets[i].cap.map_size = 0x1000; sockets[i].cap.irq_mask = 0; sockets[i].cap.pci_irq = dev->irq; + + sockets[i].number = i; if (card_present(i)) { sockets[i].card_state = 3; @@ -166,26 +167,26 @@ goto err_out_free_res; } - - cls_d = kmalloc(sizeof(*cls_d), GFP_KERNEL); - if (!cls_d) { - printk(KERN_ERR "i82092aa: kmalloc failed\n"); - goto err_out_free_irq; - } - memset(cls_d, 0, sizeof(*cls_d)); - cls_d->nsock = socket_count; - cls_d->ops = &i82092aa_operations; - pci_set_drvdata(dev, &cls_d); - cls_d->class_dev.class = &pcmcia_socket_class; - cls_d->class_dev.dev = &dev->dev; - strlcpy(cls_d->class_dev.class_id, dev->dev.name, BUS_ID_SIZE); - class_set_devdata(&cls_d->class_dev, cls_d); - class_device_register(&cls_d->class_dev); + pci_set_drvdata(dev, &sockets[i].socket); + + for (i = 0; idev; + sockets[i].socket.ss_entry = &i82092aa_operations; + ret = pcmcia_register_socket(&sockets[i].socket); + if (ret) { + goto err_out_free_sockets; + } + } leave("i82092aa_pci_probe"); return 0; -err_out_free_irq: +err_out_free_sockets: + if (i) { + for (i--;i>=0;i--) { + pcmcia_unregister_socket(&sockets[i].socket); + } + } free_irq(dev->irq, i82092aa_interrupt); err_out_free_res: release_region(pci_resource_start(dev, 0), 2); @@ -196,16 +197,14 @@ static void __devexit i82092aa_pci_remove(struct pci_dev *dev) { - struct pcmcia_socket_class_data *cls_d = pci_get_drvdata(dev); + struct pcmcia_socket *socket = pci_get_drvdata(dev); enter("i82092aa_pci_remove"); free_irq(dev->irq, i82092aa_interrupt); - if (cls_d) { - class_device_unregister(&cls_d->class_dev); - kfree(cls_d); - } + if (socket) + pcmcia_unregister_socket(socket); leave("i82092aa_pci_remove"); } @@ -447,7 +446,7 @@ -static int i82092aa_init(unsigned int s) +static int i82092aa_init(struct pcmcia_socket *sock) { int i; pccard_io_map io = { 0, 0, 0, 0, 1 }; @@ -456,21 +455,21 @@ enter("i82092aa_init"); mem.sys_stop = 0x0fff; - i82092aa_set_socket(s, &dead_socket); + i82092aa_set_socket(sock, &dead_socket); for (i = 0; i < 2; i++) { io.map = i; - i82092aa_set_io_map(s, &io); + i82092aa_set_io_map(sock, &io); } for (i = 0; i < 5; i++) { mem.map = i; - i82092aa_set_mem_map(s, &mem); + i82092aa_set_mem_map(sock, &mem); } leave("i82092aa_init"); return 0; } -static int i82092aa_suspend(unsigned int sock) +static int i82092aa_suspend(struct pcmcia_socket *sock) { int retval; enter("i82092aa_suspend"); @@ -479,8 +478,9 @@ return retval; } -static int i82092aa_register_callback(unsigned int sock, void (*handler)(void *, unsigned int), void * info) +static int i82092aa_register_callback(struct pcmcia_socket *socket, void (*handler)(void *, unsigned int), void * info) { + unsigned int sock = container_of(socket, struct socket_info, socket)->number; enter("i82092aa_register_callback"); sockets[sock].handler = handler; sockets[sock].info = info; @@ -488,8 +488,9 @@ return 0; } /* i82092aa_register_callback */ -static int i82092aa_inquire_socket(unsigned int sock, socket_cap_t *cap) +static int i82092aa_inquire_socket(struct pcmcia_socket *socket, socket_cap_t *cap) { + unsigned int sock = container_of(socket, struct socket_info, socket)->number; enter("i82092aa_inquire_socket"); *cap = sockets[sock].cap; leave("i82092aa_inquire_socket"); @@ -497,8 +498,9 @@ } /* i82092aa_inquire_socket */ -static int i82092aa_get_status(unsigned int sock, u_int *value) +static int i82092aa_get_status(struct pcmcia_socket *socket, u_int *value) { + unsigned int sock = container_of(socket, struct socket_info, socket)->number; unsigned int status; enter("i82092aa_get_status"); @@ -539,8 +541,9 @@ } -static int i82092aa_get_socket(unsigned int sock, socket_state_t *state) +static int i82092aa_get_socket(struct pcmcia_socket *socket, socket_state_t *state) { + unsigned int sock = container_of(socket, struct socket_info, socket)->number; unsigned char reg,vcc,vpp; enter("i82092aa_get_socket"); @@ -610,8 +613,9 @@ return 0; } -static int i82092aa_set_socket(unsigned int sock, socket_state_t *state) +static int i82092aa_set_socket(struct pcmcia_socket *socket, socket_state_t *state) { + unsigned int sock = container_of(socket, struct socket_info, socket)->number; unsigned char reg; enter("i82092aa_set_socket"); @@ -706,8 +710,9 @@ return 0; } -static int i82092aa_set_io_map(unsigned sock, struct pccard_io_map *io) +static int i82092aa_set_io_map(struct pcmcia_socket *socket, struct pccard_io_map *io) { + unsigned int sock = container_of(socket, struct socket_info, socket)->number; unsigned char map, ioctl; enter("i82092aa_set_io_map"); @@ -749,8 +754,9 @@ return 0; } -static int i82092aa_set_mem_map(unsigned sock, struct pccard_mem_map *mem) +static int i82092aa_set_mem_map(struct pcmcia_socket *socket, struct pccard_mem_map *mem) { + unsigned int sock = container_of(socket, struct socket_info, socket)->number; unsigned short base, i; unsigned char map; @@ -826,7 +832,7 @@ return 0; } -static void i82092aa_proc_setup(unsigned int sock, struct proc_dir_entry *base) +static void i82092aa_proc_setup(struct pcmcia_socket *socket, struct proc_dir_entry *base) { } diff -urN orig/drivers/pcmcia/i82092aa.h linux/drivers/pcmcia/i82092aa.h --- orig/drivers/pcmcia/i82092aa.h Mon May 5 17:39:31 2003 +++ linux/drivers/pcmcia/i82092aa.h Wed Jun 11 20:55:09 2003 @@ -28,16 +28,16 @@ -static int i82092aa_get_status(unsigned int sock, u_int *value); -static int i82092aa_get_socket(unsigned int sock, socket_state_t *state); -static int i82092aa_set_socket(unsigned int sock, socket_state_t *state); -static int i82092aa_set_io_map(unsigned int sock, struct pccard_io_map *io); -static int i82092aa_set_mem_map(unsigned int sock, struct pccard_mem_map *mem); -static int i82092aa_init(unsigned int s); -static int i82092aa_suspend(unsigned int sock); -static int i82092aa_register_callback(unsigned int sock, void (*handler)(void *, unsigned int), void * info); -static int i82092aa_inquire_socket(unsigned int sock, socket_cap_t *cap); -static void i82092aa_proc_setup(unsigned int sock, struct proc_dir_entry *base); +static int i82092aa_get_status(struct pcmcia_socket *socket, u_int *value); +static int i82092aa_get_socket(struct pcmcia_socket *socket, socket_state_t *state); +static int i82092aa_set_socket(struct pcmcia_socket *socket, socket_state_t *state); +static int i82092aa_set_io_map(struct pcmcia_socket *socket, struct pccard_io_map *io); +static int i82092aa_set_mem_map(struct pcmcia_socket *socket, struct pccard_mem_map *mem); +static int i82092aa_init(struct pcmcia_socket *socket); +static int i82092aa_suspend(struct pcmcia_socket *socket); +static int i82092aa_register_callback(struct pcmcia_socket *socket, void (*handler)(void *, unsigned int), void * info); +static int i82092aa_inquire_socket(struct pcmcia_socket *socket, socket_cap_t *cap); +static void i82092aa_proc_setup(struct pcmcia_socket *socket, struct proc_dir_entry *base); #endif diff -urN orig/drivers/pcmcia/i82365.c linux/drivers/pcmcia/i82365.c --- orig/drivers/pcmcia/i82365.c Tue May 27 10:04:55 2003 +++ linux/drivers/pcmcia/i82365.c Thu Jun 12 12:44:48 2003 @@ -68,6 +68,15 @@ #include "ricoh.h" #include "o2micro.h" +#ifdef CONFIG_ARCH_EBSA110 +#define I365_MASK (1 << 6) +#define SOCKIRQ2REG(sock,irq) ((sock) ? 3 : 4) +#define REG2SOCKIRQ(sock,reg) (6) +#else +#define SOCKIRQ2REG(sock,irq) (irq) +#define REG2SOCKIRQ(sock,reg) (reg) +#endif + #ifdef PCMCIA_DEBUG static int pc_debug = PCMCIA_DEBUG; MODULE_PARM(pc_debug, "i"); @@ -91,7 +100,6 @@ /* Parameters that can be set with 'insmod' */ -#ifdef CONFIG_ISA /* Default base address for i82365sl and other ISA chips */ static int i365_base = 0x3e0; /* Should we probe at 0x3e2 for an extra ISA controller? */ @@ -103,7 +111,6 @@ static int irq_list[16] = { -1 }; /* The card status change interrupt -- 0 means autoselect */ static int cs_irq = 0; -#endif /* Probe for safe interrupts? */ static int do_scan = 1; @@ -122,14 +129,11 @@ static int cmd_time = -1; static int recov_time = -1; -#ifdef CONFIG_ISA /* Vadem options */ static int async_clock = -1; static int cable_mode = -1; static int wakeup = 0; -#endif -#ifdef CONFIG_ISA MODULE_PARM(i365_base, "i"); MODULE_PARM(ignore, "i"); MODULE_PARM(extra_sockets, "i"); @@ -139,7 +143,6 @@ MODULE_PARM(async_clock, "i"); MODULE_PARM(cable_mode, "i"); MODULE_PARM(wakeup, "i"); -#endif MODULE_PARM(do_scan, "i"); MODULE_PARM(poll_interval, "i"); @@ -164,8 +167,10 @@ u_char ctl, ema; } vg46x_state_t; -typedef struct socket_info_t { +struct i82365_socket { u_short type, flags; + struct pcmcia_socket socket; + unsigned int number; socket_cap_t cap; ioaddr_t ioaddr; u_short psock; @@ -179,26 +184,23 @@ cirrus_state_t cirrus; vg46x_state_t vg46x; } state; -} socket_info_t; +}; /* Where we keep track of our sockets... */ -static int sockets = 0; -static socket_info_t socket[8] = { - { 0, }, /* ... */ -}; +static int sockets /* = 0 */; +static struct i82365_socket socket[8] /* = { + { 0, }, +} */; /* Default ISA interrupt mask */ +#ifndef I365_MASK #define I365_MASK 0xdeb8 /* irq 15,14,12,11,10,9,7,5,4,3 */ +#endif -#ifdef CONFIG_ISA static int grab_irq; static spinlock_t isa_lock = SPIN_LOCK_UNLOCKED; #define ISA_LOCK(n, f) spin_lock_irqsave(&isa_lock, f) #define ISA_UNLOCK(n, f) spin_unlock_irqrestore(&isa_lock, f) -#else -#define ISA_LOCK(n, f) do { } while (0) -#define ISA_UNLOCK(n, f) do { } while (0) -#endif static struct timer_list poll_timer; @@ -209,13 +211,11 @@ PCI_COMMAND_MASTER|PCI_COMMAND_WAIT) /* These definitions must match the pcic table! */ -#ifdef CONFIG_ISA typedef enum pcic_id { IS_I82365A, IS_I82365B, IS_I82365DF, IS_IBM, IS_RF5Cx96, IS_VLSI, IS_VG468, IS_VG469, IS_PD6710, IS_PD672X, IS_VT83C469, } pcic_id; -#endif /* Flags for classifying groups of controllers */ #define IS_VADEM 0x0001 @@ -237,7 +237,6 @@ } pcic_t; static pcic_t pcic[] = { -#ifdef CONFIG_ISA { "Intel i82365sl A step", 0 }, { "Intel i82365sl B step", 0 }, { "Intel i82365sl DF", IS_DF_PWR }, @@ -249,7 +248,6 @@ { "Cirrus PD6710", IS_CIRRUS }, { "Cirrus PD672x", IS_CIRRUS }, { "VIA VT83C469", IS_CIRRUS|IS_VIA }, -#endif }; #define PCIC_COUNT (sizeof(pcic)/sizeof(pcic_t)) @@ -364,7 +362,7 @@ static u_int __init cirrus_set_opts(u_short s, char *buf) { - socket_info_t *t = &socket[s]; + struct i82365_socket *t = &socket[s]; cirrus_state_t *p = &socket[s].state.cirrus; u_int mask = 0xffff; @@ -421,8 +419,6 @@ ======================================================================*/ -#ifdef CONFIG_ISA - static void vg46x_get_state(u_short s) { vg46x_state_t *p = &socket[s].state.vg46x; @@ -464,9 +460,6 @@ return 0xffff; } -#endif - - /*====================================================================== Generic routines to get and set controller options @@ -475,18 +468,16 @@ static void get_bridge_state(u_short s) { - socket_info_t *t = &socket[s]; + struct i82365_socket *t = &socket[s]; if (t->flags & IS_CIRRUS) cirrus_get_state(s); -#ifdef CONFIG_ISA else if (t->flags & IS_VADEM) vg46x_get_state(s); -#endif } static void set_bridge_state(u_short s) { - socket_info_t *t = &socket[s]; + struct i82365_socket *t = &socket[s]; if (t->flags & IS_CIRRUS) cirrus_set_state(s); else { @@ -494,10 +485,8 @@ i365_set(s, I365_GENCTL, 0x00); } i365_bflip(s, I365_INTCTL, I365_INTR_ENA, t->intr); -#ifdef CONFIG_ISA if (t->flags & IS_VADEM) vg46x_set_state(s); -#endif } static u_int __init set_bridge_opts(u_short s, u_short ns) @@ -515,10 +504,8 @@ get_bridge_state(i); if (socket[i].flags & IS_CIRRUS) m = cirrus_set_opts(i, buf); -#ifdef CONFIG_ISA else if (socket[i].flags & IS_VADEM) m = vg46x_set_opts(i, buf); -#endif set_bridge_state(i); printk(KERN_INFO " host opts [%d]:%s\n", i, (*buf) ? buf : " none"); @@ -558,7 +545,7 @@ } /* Generate one interrupt */ - i365_set(sock, I365_CSCINT, I365_CSC_DETECT | (irq << 4)); + i365_set(sock, I365_CSCINT, I365_CSC_DETECT | (SOCKIRQ2REG(sock, irq) << 4)); i365_bset(sock, I365_GENCTL, I365_CTL_SW_IRQ); udelay(1000); @@ -571,7 +558,6 @@ return (irq_hits != 1); } -#ifdef CONFIG_ISA static u_int __init isa_scan(u_short sock, u_int mask0) { @@ -617,7 +603,6 @@ return mask1; } -#endif /* CONFIG_ISA */ /*====================================================================*/ @@ -630,7 +615,6 @@ /*====================================================================*/ -#ifdef CONFIG_ISA static int __init identify(u_short port, u_short sock) { @@ -691,8 +675,6 @@ return type; } /* identify */ -#endif - /*====================================================================== See if a card is present, powered up, in IO mode, and already @@ -737,7 +719,7 @@ { u_int mask = 0, i, base; int use_pci = 0, isa_irq = 0; - socket_info_t *t = &socket[sockets-ns]; + struct i82365_socket *t = &socket[sockets-ns]; base = sockets-ns; if (t->ioaddr > 0) request_region(t->ioaddr, 2, "i82365"); @@ -748,23 +730,17 @@ t->ioaddr, t->psock*0x40); printk(", %d socket%s\n", ns, ((ns > 1) ? "s" : "")); -#ifdef CONFIG_ISA /* Set host options, build basic interrupt mask */ if (irq_list[0] == -1) mask = irq_mask; else for (i = mask = 0; i < 16; i++) mask |= (1<number; socket[sock].handler = handler; socket[sock].info = info; return 0; @@ -1022,8 +994,9 @@ /*====================================================================*/ -static int pcic_inquire_socket(unsigned int sock, socket_cap_t *cap) +static int pcic_inquire_socket(struct pcmcia_socket *s, socket_cap_t *cap) { + unsigned int sock = container_of(s, struct i82365_socket, socket)->number; *cap = socket[sock].cap; return 0; } /* pcic_inquire_socket */ @@ -1048,7 +1021,6 @@ *value |= (status & I365_CS_READY) ? SS_READY : 0; *value |= (status & I365_CS_POWERON) ? SS_POWERON : 0; -#ifdef CONFIG_ISA if (socket[sock].type == IS_VG469) { status = i365_get(sock, VG469_VSENSE); if (socket[sock].psock & 1) { @@ -1059,7 +1031,6 @@ *value |= (status & VG469_VSENSE_A_VS2) ? 0 : SS_XVCARD; } } -#endif DEBUG(1, "i82365: GetStatus(%d) = %#4.4x\n", sock, *value); return 0; @@ -1069,7 +1040,7 @@ static int i365_get_socket(u_short sock, socket_state_t *state) { - socket_info_t *t = &socket[sock]; + struct i82365_socket *t = &socket[sock]; u_char reg, vcc, vpp; reg = i365_get(sock, I365_POWER); @@ -1112,7 +1083,7 @@ reg = i365_get(sock, I365_INTCTL); state->flags |= (reg & I365_PC_RESET) ? 0 : SS_RESET; if (reg & I365_PC_IOCARD) state->flags |= SS_IOCARD; - state->io_irq = reg & I365_IRQ_MASK; + state->io_irq = REG2SOCKIRQ(sock, reg & I365_IRQ_MASK); /* speaker control */ if (t->flags & IS_CIRRUS) { @@ -1141,7 +1112,7 @@ static int i365_set_socket(u_short sock, socket_state_t *state) { - socket_info_t *t = &socket[sock]; + struct i82365_socket *t = &socket[sock]; u_char reg; DEBUG(1, "i82365: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, " @@ -1153,7 +1124,7 @@ /* IO card, RESET flag, IO interrupt */ reg = t->intr; - if (state->io_irq != t->cap.pci_irq) reg |= state->io_irq; + if (state->io_irq != t->cap.pci_irq) reg |= SOCKIRQ2REG(sock, state->io_irq); reg |= (state->flags & SS_RESET) ? 0 : I365_PC_RESET; reg |= (state->flags & SS_IOCARD) ? I365_PC_IOCARD : 0; i365_set(sock, I365_INTCTL, reg); @@ -1232,7 +1203,7 @@ } /* Card status change interrupt mask */ - reg = t->cs_irq << 4; + reg = SOCKIRQ2REG(sock, t->cs_irq) << 4; if (state->csc_mask & SS_DETECT) reg |= I365_CSC_DETECT; if (state->flags & SS_IOCARD) { if (state->csc_mask & SS_STSCHG) reg |= I365_CSC_STSCHG; @@ -1337,7 +1308,7 @@ static int proc_read_info(char *buf, char **start, off_t pos, int count, int *eof, void *data) { - socket_info_t *s = data; + struct i82365_socket *s = data; char *p = buf; p += sprintf(p, "type: %s\npsock: %d\n", pcic[s->type].name, s->psock); @@ -1347,13 +1318,11 @@ static int proc_read_exca(char *buf, char **start, off_t pos, int count, int *eof, void *data) { - u_short sock = (socket_info_t *)data - socket; + u_short sock = (struct i82365_socket *)data - socket; char *p = buf; int i, top; -#ifdef CONFIG_ISA u_long flags = 0; -#endif ISA_LOCK(sock, flags); top = 0x40; for (i = 0; i < top; i += 4) { @@ -1370,9 +1339,9 @@ return (p - buf); } -static void pcic_proc_setup(unsigned int sock, struct proc_dir_entry *base) +static void pcic_proc_setup(struct pcmcia_socket *sock, struct proc_dir_entry *base) { - socket_info_t *s = &socket[sock]; + struct i82365_socket *s = container_of(sock, struct i82365_socket, socket); if (s->flags & IS_ALIVE) return; @@ -1398,15 +1367,8 @@ /*====================================================================*/ -/* - * The locking is rather broken. Why do we only lock for ISA, not for - * all other cases? If there are reasons to lock, we should lock. Not - * this silly conditional. - * - * Plan: make it bug-for-bug compatible with the old stuff, and clean - * it up when the infrastructure is done. - */ -#ifdef CONFIG_ISA +/* this is horribly ugly... proper locking needs to be done here at + * some time... */ #define LOCKED(x) do { \ int retval; \ unsigned long flags; \ @@ -1415,13 +1377,12 @@ spin_unlock_irqrestore(&isa_lock, flags); \ return retval; \ } while (0) -#else -#define LOCKED(x) return x -#endif -static int pcic_get_status(unsigned int sock, u_int *value) +static int pcic_get_status(struct pcmcia_socket *s, u_int *value) { + unsigned int sock = container_of(s, struct i82365_socket, socket)->number; + if (socket[sock].flags & IS_ALIVE) { *value = 0; return -EINVAL; @@ -1430,39 +1391,45 @@ LOCKED(i365_get_status(sock, value)); } -static int pcic_get_socket(unsigned int sock, socket_state_t *state) +static int pcic_get_socket(struct pcmcia_socket *s, socket_state_t *state) { + unsigned int sock = container_of(s, struct i82365_socket, socket)->number; + if (socket[sock].flags & IS_ALIVE) return -EINVAL; LOCKED(i365_get_socket(sock, state)); } -static int pcic_set_socket(unsigned int sock, socket_state_t *state) +static int pcic_set_socket(struct pcmcia_socket *s, socket_state_t *state) { + unsigned int sock = container_of(s, struct i82365_socket, socket)->number; + if (socket[sock].flags & IS_ALIVE) return -EINVAL; LOCKED(i365_set_socket(sock, state)); } -static int pcic_set_io_map(unsigned int sock, struct pccard_io_map *io) +static int pcic_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io) { + unsigned int sock = container_of(s, struct i82365_socket, socket)->number; if (socket[sock].flags & IS_ALIVE) return -EINVAL; LOCKED(i365_set_io_map(sock, io)); } -static int pcic_set_mem_map(unsigned int sock, struct pccard_mem_map *mem) +static int pcic_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *mem) { + unsigned int sock = container_of(s, struct i82365_socket, socket)->number; if (socket[sock].flags & IS_ALIVE) return -EINVAL; LOCKED(i365_set_mem_map(sock, mem)); } -static int pcic_init(unsigned int s) +static int pcic_init(struct pcmcia_socket *s) { int i; pccard_io_map io = { 0, 0, 0, 0, 1 }; @@ -1481,7 +1448,7 @@ return 0; } -static int pcic_suspend(unsigned int sock) +static int pcic_suspend(struct pcmcia_socket *sock) { return pcic_set_socket(sock, &dead_socket); } @@ -1502,15 +1469,11 @@ /*====================================================================*/ -static struct pcmcia_socket_class_data i82365_data = { - .ops = &pcic_operations, -}; - static struct device_driver i82365_driver = { .name = "i82365", .bus = &platform_bus_type, -/* .suspend = pcmcia_socket_dev_suspend, FIXME? */ -/* .resume = pcmcia_socket_dev_resume, FIXME? */ + .suspend = pcmcia_socket_dev_suspend, + .resume = pcmcia_socket_dev_resume, }; static struct platform_device i82365_device = { @@ -1521,13 +1484,11 @@ }, }; -static struct class_device i82365_class_data = { - .class = &pcmcia_socket_class, -}; - static int __init init_i82365(void) { servinfo_t serv; + int i, ret; + pcmcia_get_card_services_info(&serv); if (serv.Revision != CS_RELEASE_CODE) { printk(KERN_NOTICE "i82365: Card Services release " @@ -1541,9 +1502,7 @@ printk(KERN_INFO "Intel PCIC probe: "); sockets = 0; -#ifdef CONFIG_ISA isa_probe(); -#endif if (sockets == 0) { printk("not found.\n"); @@ -1551,19 +1510,24 @@ return -ENODEV; } + platform_device_register(&i82365_device); + /* Set up interrupt handler(s) */ -#ifdef CONFIG_ISA if (grab_irq != 0) request_irq(cs_irq, pcic_interrupt, 0, "i82365", pcic_interrupt); -#endif - - i82365_data.nsock = sockets; - i82365_class_data.dev = &i82365_device.dev; - i82365_class_data.class_data = &i82365_data; - strlcpy(i82365_class_data.class_id, "i82365", BUS_ID_SIZE); - platform_device_register(&i82365_device); - class_device_register(&i82365_class_data); + /* register sockets with the pcmcia core */ + for (i = 0; i < sockets; i++) { + socket[i].socket.dev.dev = &i82365_device.dev; + socket[i].socket.ss_entry = &pcic_operations; + socket[i].number = i; + ret = pcmcia_register_socket(&socket[i].socket); + if (ret && i--) { + for (; i>= 0; i--) + pcmcia_unregister_socket(&socket[i].socket); + break; + } + } /* Finally, schedule a polling interrupt */ if (poll_interval != 0) { @@ -1581,23 +1545,23 @@ static void __exit exit_i82365(void) { int i; + for (i = 0; i < sockets; i++) { + pcmcia_unregister_socket(&socket[i].socket); #ifdef CONFIG_PROC_FS - for (i = 0; i < sockets; i++) pcic_proc_remove(i); + pcic_proc_remove(i); #endif - class_device_unregister(&i82365_class_data); + } platform_device_unregister(&i82365_device); if (poll_interval != 0) del_timer_sync(&poll_timer); -#ifdef CONFIG_ISA if (grab_irq != 0) free_irq(cs_irq, pcic_interrupt); -#endif for (i = 0; i < sockets; i++) { /* Turn off all interrupt sources! */ i365_set(i, I365_CSCINT, 0); release_region(socket[i].ioaddr, 2); } -#if defined(CONFIG_ISA) && defined(__ISAPNP__) +#ifdef __ISAPNP__ if (i82365_pnpdev) pnp_disable_dev(i82365_pnpdev); #endif diff -urN orig/drivers/pcmcia/map.h linux/drivers/pcmcia/map.h --- orig/drivers/pcmcia/map.h Thu Jan 1 01:00:00 1970 +++ linux/drivers/pcmcia/map.h Thu Jun 12 12:44:44 2003 @@ -0,0 +1,24 @@ + +struct pcmcia_socket; + +struct map_operations { + int (*insert)(struct pcmcia_socket *); + void (*remove)(struct pcmcia_socket *); + + void (*cis_unmap)(struct pcmcia_socket *); + void *(*cis_map)(struct pcmcia_socket *, unsigned int, unsigned int); + + void (*mem_unmap)(struct pcmcia_socket *, pccard_mem_map *); + int (*mem_map)(struct pcmcia_socket *, pccard_mem_map *, + struct win_req_t *, char *); + int (*mem_modify)(struct pcmcia_socket *, pccard_mem_map *, + unsigned int, unsigned int, unsigned int); + + struct resource *(*io_alloc)(struct pcmcia_socket *, unsigned long offset, + unsigned int sz, unsigned long align, unsigned int attr, + char *name); +}; + +extern struct map_operations smap_ops; +extern struct map_operations winmap_ops; + diff -urN orig/drivers/pcmcia/mapstatic.c linux/drivers/pcmcia/mapstatic.c --- orig/drivers/pcmcia/mapstatic.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/pcmcia/mapstatic.c Thu Jun 12 12:44:44 2003 @@ -0,0 +1,160 @@ +/* + * linux/drivers/pcmcia/mapstatic.c + * + * Handle static mapped sockets. Things to note: + * + * - the virtual address of a mapping can change when the card memory + * offset / type changes. Clients must not assume this is constant. + * (normal usage seems to indicate a call to RequestWindow, followed + * by a call to MapMemPage. This can not work for static mapped + * sockets, and requires all card drivers and cs.c to be fixed.) + * + * - the current way we obtain the base address of a mapping is bogus + * and needs to die - set_mem_map() offers a different interface + * for statically mapped sockets to that for windowed sockets. + */ +#include +#include +#include +#include +#include + +#include + +#include +struct proc_dir_entry; + +#include +#include +#include +#include +#include +#include +#include "cs_internal.h" +#include "map.h" + +static int smap_insert(struct pcmcia_socket *s) +{ + return 0; +} + +static void smap_remove(struct pcmcia_socket *s) +{ +} + +static void smap_cis_unmap(struct pcmcia_socket *s) +{ + pccard_mem_map *map = &s->cis_mem; + + if (s->cis_virt) + iounmap(s->cis_virt); + s->cis_virt = NULL; + + if (map->res) + release_resource(map->res); + map->res = NULL; + + map->flags = 0; + s->ss_entry->set_mem_map(s, map); +} + +static void * +smap_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flags) +{ + pccard_mem_map *map = &s->cis_mem; + + if (map->card_start != card_offset || map->flags != flags) { + smap_cis_unmap(s); + + map->flags = flags; + map->sys_start = 0; + map->sys_stop = s->cap.map_size - 1; + map->card_start = card_offset; + + s->ss_entry->set_mem_map(s, map); + + map->res = request_mem_region(map->sys_start, s->cap.map_size, + "card services"); + if (map->res) + s->cis_virt = ioremap(map->res->start, s->cap.map_size); + + if (!s->cis_virt) + smap_cis_unmap(s); + } + return s->cis_virt; +} + + + + +static void smap_mem_unmap(struct pcmcia_socket *s, pccard_mem_map *map) +{ + BUG_ON(!map->res); + + release_resource(map->res); + map->res = NULL; + + s->ss_entry->set_mem_map(s, map); +} + +static int +smap_mem_map(struct pcmcia_socket *s, pccard_mem_map *map, struct win_req_t *req, char *dev_info) +{ + BUG_ON(map->res); + + if (req->Base != 0) + return CS_BAD_BASE; + + map->sys_start = 0; + map->sys_stop = req->Size - 1; + + if (s->ss_entry->set_mem_map(s, map) != 0) + return CS_BAD_ARGS; + + map->res = request_mem_region(map->sys_start, req->Size, dev_info); + + return map->res ? CS_SUCCESS : CS_IN_USE; +} + +static int +smap_mem_modify(struct pcmcia_socket *s, pccard_mem_map *map, + unsigned int card_offset, unsigned int speed, + unsigned int flags) +{ + BUG_ON(!map->res); + + if (map->card_start != card_offset) + return CS_BAD_OFFSET; + + map->speed = speed; + map->flags = flags; + + return s->ss_entry->set_mem_map(s, map) ? CS_BAD_ARGS : 0; +} + + + +static struct resource * +smap_io_alloc(struct pcmcia_socket *s, unsigned long offset, unsigned int sz, + unsigned long align, unsigned int attr, char *name) +{ + if (align == 0) + align = 0x10000; + offset = s->cap.io_offset | (offset & (align - 1)); + + return request_region(offset, sz, name); +} + +struct map_operations smap_ops = { + .insert = smap_insert, + .remove = smap_remove, + + .cis_unmap = smap_cis_unmap, + .cis_map = smap_cis_map, + + .mem_unmap = smap_mem_unmap, + .mem_map = smap_mem_map, + .mem_modify = smap_mem_modify, + + .io_alloc = smap_io_alloc, +}; diff -urN orig/drivers/pcmcia/mapwindow.c linux/drivers/pcmcia/mapwindow.c --- orig/drivers/pcmcia/mapwindow.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/pcmcia/mapwindow.c Thu Jun 12 12:44:44 2003 @@ -0,0 +1,233 @@ +/* + * linux/drivers/pcmcia/mapwindow.c + * + * Handle window mapped sockets. Things to note: + * + * - validate_mem() will call winmap_cis_map and winmap_cis_unmap + * during it's probe for usable memory. + */ +#include +#include +#include +#include +#include +#include + +#include + +#include +struct proc_dir_entry; + +#include +#include +#include +#include +#include +#include +#include "cs_internal.h" +#include "map.h" + +/* + * A card was inserted. Find a memory resource for the CIS, claim it, + * and setup a mapping. + */ +static int winmap_insert(struct pcmcia_socket *s) +{ + BUG_ON(s->cis_mem.res); + BUG_ON(s->cis_virt); + + if (!(s->cap.features & SS_CAP_CARDBUS)) + validate_mem(s); + + s->cis_mem.res = find_mem_region(0, s->cap.map_size, s->cap.map_size, + 0, "card services", s); + + if (!s->cis_mem.res) + return -EBUSY; + + s->cis_virt = ioremap(s->cis_mem.res->start, s->cap.map_size); + if (!s->cis_virt) { + release_resource(s->cis_mem.res); + return -ENOMEM; + } + + return 0; +} + +/* + * The card was removed. Release resources claimed by winmap_insert(). + */ +static void winmap_remove(struct pcmcia_socket *s) +{ + if (s->cis_virt) + iounmap(s->cis_virt); + if (s->cis_mem.res) + release_resource(s->cis_mem.res); + + s->cis_virt = NULL; + s->cis_mem.res = NULL; +} + +/* + * Unmap a CIS mapping. Note that the resource and ioremap of + * the region remains in place since winmap_cis_map() did not + * setup this mapping. + */ +static void winmap_cis_unmap(struct pcmcia_socket *s) +{ + pccard_mem_map *mem = &s->cis_mem; + + if (mem->flags & MAP_ACTIVE) { + mem->flags &= ~MAP_ACTIVE; + s->ss_entry->set_mem_map(s, mem); + } +} + +/* + * Map card memory at card_offset in the memory region defined + * by "flags" into memory, returning the virtual address of this + * mapping. We may be altering a pre-existing mapping. + */ +static void * +winmap_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flags) +{ + pccard_mem_map *mem = &s->cis_mem; + + if (s->cis_virt && + (mem->card_start != card_offset || mem->flags != flags)) { + mem->flags = flags; + mem->sys_start = mem->res->start; + mem->sys_stop = mem->res->start + s->cap.map_size - 1; + mem->card_start = card_offset; + + s->ss_entry->set_mem_map(s, mem); + } + + return s->cis_virt; +} + + + + +static void winmap_mem_unmap(struct pcmcia_socket *s, pccard_mem_map *map) +{ + BUG_ON(!map->res); + + s->ss_entry->set_mem_map(s, map); + + release_resource(map->res); + map->res = NULL; +} + +static int +winmap_mem_map(struct pcmcia_socket *s, pccard_mem_map *map, struct win_req_t *req, char *dev_info) +{ + unsigned long align; + + BUG_ON(map->res); + + align = s->cap.map_size; + if ((s->cap.features & SS_CAP_MEM_ALIGN) || + (req->Attributes & WIN_STRICT_ALIGN)) + align = req->Size; + + if (req->Base & (align-1)) + return CS_BAD_BASE; + if (req->Base) + align = 0; + + map->res = find_mem_region(req->Base, req->Size, align, + req->Attributes & WIN_MAP_BELOW_1MB, + dev_info, s); + if (!map->res) + return CS_IN_USE; + + map->sys_start = map->res->start; + map->sys_stop = map->res->end; + + if (s->ss_entry->set_mem_map(s, map) != 0) { + release_resource(map->res); + map->res = NULL; + return CS_BAD_ARGS; + } + + return CS_SUCCESS; +} + +static int +winmap_mem_modify(struct pcmcia_socket *s, pccard_mem_map *map, + unsigned int card_offset, unsigned int speed, + unsigned int flags) +{ + BUG_ON(!map->res); + + map->card_start = card_offset; + map->speed = speed; + map->flags = flags; + + return s->ss_entry->set_mem_map(s, map) ? CS_BAD_ARGS : 0; +} + + + +static struct resource * +winmap_io_alloc(struct pcmcia_socket *s, unsigned long offset, unsigned int sz, + unsigned long align, unsigned int attr, char *name) +{ + struct resource *res = NULL; + unsigned long try; + int i; + + for (i = 0; i < MAX_IO_WIN; i++) { + if (s->io[i].NumPorts == 0) { + res = find_io_region(offset, sz, align, name, s); + if (res != NULL) { + s->io[i].Attributes = attr; + s->io[i].BasePort = res->start; + s->io[i].NumPorts = s->io[i].InUse = sz; + break; + } else + return NULL; + } else if (s->io[i].Attributes != attr) + continue; + + /* Try to extend top of window */ + try = s->io[i].BasePort + s->io[i].NumPorts; + if ((offset == 0) || (offset == try)) { + res = find_io_region(try, sz, 0, name, s); + if (res != NULL) { + s->io[i].NumPorts += sz; + s->io[i].InUse += sz; + break; + } + } + + /* Try to extend bottom of window */ + try = s->io[i].BasePort - sz; + if ((offset == 0) || (offset == try)) { + res = find_io_region(try, sz, 0, name, s); + if (res != NULL) { + s->io[i].BasePort = res->start; + s->io[i].NumPorts += sz; + s->io[i].InUse += sz; + break; + } + } + } + + return res; +} + +struct map_operations winmap_ops = { + .insert = winmap_insert, + .remove = winmap_remove, + + .cis_unmap = winmap_cis_unmap, + .cis_map = winmap_cis_map, + + .mem_unmap = winmap_mem_unmap, + .mem_map = winmap_mem_map, + .mem_modify = winmap_mem_modify, + + .io_alloc = winmap_io_alloc, +}; diff -urN orig/drivers/pcmcia/pci_socket.c linux/drivers/pcmcia/pci_socket.c --- orig/drivers/pcmcia/pci_socket.c Tue May 27 10:04:55 2003 +++ linux/drivers/pcmcia/pci_socket.c Thu Jan 1 01:00:00 1970 @@ -1,250 +0,0 @@ -/* - * Generic PCI pccard driver interface. - * - * (C) Copyright 1999 Linus Torvalds - * - * This implements the common parts of PCI pccard drivers, - * notably detection and infrastructure conversion (ie change - * from socket index to "struct pci_dev" etc) - * - * This does NOT implement the actual low-level driver details, - * and this has on purpose been left generic enough that it can - * be used to set up a PCI PCMCIA controller (ie non-cardbus), - * or to set up a controller. - * - * See for example the "yenta" driver for PCI cardbus controllers - * conforming to the yenta cardbus specifications. - */ -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include "pci_socket.h" - - -/* - * Arbitrary define. This is the array of active cardbus - * entries. - */ -#define MAX_SOCKETS (8) -static pci_socket_t pci_socket_array[MAX_SOCKETS]; - -static int pci_init_socket(unsigned int sock) -{ - pci_socket_t *socket = pci_socket_array + sock; - - if (socket->op && socket->op->init) - return socket->op->init(socket); - return -EINVAL; -} - -static int pci_suspend_socket(unsigned int sock) -{ - pci_socket_t *socket = pci_socket_array + sock; - - if (socket->op && socket->op->suspend) - return socket->op->suspend(socket); - return -EINVAL; -} - -static int pci_register_callback(unsigned int sock, void (*handler)(void *, unsigned int), void * info) -{ - pci_socket_t *socket = pci_socket_array + sock; - - socket->handler = handler; - socket->info = info; - return 0; -} - -static int pci_inquire_socket(unsigned int sock, socket_cap_t *cap) -{ - pci_socket_t *socket = pci_socket_array + sock; - - *cap = socket->cap; - return 0; -} - -static int pci_get_status(unsigned int sock, unsigned int *value) -{ - pci_socket_t *socket = pci_socket_array + sock; - - if (socket->op && socket->op->get_status) - return socket->op->get_status(socket, value); - *value = 0; - return -EINVAL; -} - -static int pci_get_socket(unsigned int sock, socket_state_t *state) -{ - pci_socket_t *socket = pci_socket_array + sock; - - if (socket->op && socket->op->get_socket) - return socket->op->get_socket(socket, state); - return -EINVAL; -} - -static int pci_set_socket(unsigned int sock, socket_state_t *state) -{ - pci_socket_t *socket = pci_socket_array + sock; - - if (socket->op && socket->op->set_socket) - return socket->op->set_socket(socket, state); - return -EINVAL; -} - -static int pci_set_io_map(unsigned int sock, struct pccard_io_map *io) -{ - pci_socket_t *socket = pci_socket_array + sock; - - if (socket->op && socket->op->set_io_map) - return socket->op->set_io_map(socket, io); - return -EINVAL; -} - -static int pci_set_mem_map(unsigned int sock, struct pccard_mem_map *mem) -{ - pci_socket_t *socket = pci_socket_array + sock; - - if (socket->op && socket->op->set_mem_map) - return socket->op->set_mem_map(socket, mem); - return -EINVAL; -} - -static void pci_proc_setup(unsigned int sock, struct proc_dir_entry *base) -{ - pci_socket_t *socket = pci_socket_array + sock; - - if (socket->op && socket->op->proc_setup) - socket->op->proc_setup(socket, base); -} - -static struct pccard_operations pci_socket_operations = { - .owner = THIS_MODULE, - .init = pci_init_socket, - .suspend = pci_suspend_socket, - .register_callback = pci_register_callback, - .inquire_socket = pci_inquire_socket, - .get_status = pci_get_status, - .get_socket = pci_get_socket, - .set_socket = pci_set_socket, - .set_io_map = pci_set_io_map, - .set_mem_map = pci_set_mem_map, - .proc_setup = pci_proc_setup, -}; - -static int __devinit add_pci_socket(int nr, struct pci_dev *dev, struct pci_socket_ops *ops) -{ - pci_socket_t *socket = nr + pci_socket_array; - int err; - - memset(socket, 0, sizeof(*socket)); - - /* prepare class_data */ - socket->cls_d.sock_offset = nr; - socket->cls_d.nsock = 1; /* yenta is 1, no other low-level driver uses - this yet */ - socket->cls_d.ops = &pci_socket_operations; - socket->cls_d.class_dev.class = &pcmcia_socket_class; - socket->cls_d.class_dev.dev = &dev->dev; - strlcpy(socket->cls_d.class_dev.class_id, dev->dev.bus_id, BUS_ID_SIZE); - class_set_devdata(&socket->cls_d.class_dev, &socket->cls_d); - - /* prepare pci_socket_t */ - socket->dev = dev; - socket->op = ops; - pci_set_drvdata(dev, socket); - spin_lock_init(&socket->event_lock); - err = socket->op->open(socket); - if (err) { - socket->dev = NULL; - pci_set_drvdata(dev, NULL); - } else { - class_device_register(&socket->cls_d.class_dev); - } - return err; -} - -int cardbus_register(struct pci_dev *p_dev) -{ - return 0; -} - -static int __devinit -cardbus_probe (struct pci_dev *dev, const struct pci_device_id *id) -{ - int s; - - for (s = 0; s < MAX_SOCKETS; s++) { - if (pci_socket_array [s].dev == 0) { - return add_pci_socket (s, dev, ¥ta_operations); - } - } - return -ENODEV; -} - -static void __devexit cardbus_remove (struct pci_dev *dev) -{ - pci_socket_t *socket = pci_get_drvdata(dev); - - /* note: we are already unregistered from the cs core */ - if (socket->op && socket->op->close) - socket->op->close(socket); - class_device_unregister(&socket->cls_d.class_dev); - pci_set_drvdata(dev, NULL); -} - -static int cardbus_suspend (struct pci_dev *dev, u32 state) -{ - pci_socket_t *socket = pci_get_drvdata(dev); - return pcmcia_socket_dev_suspend(&socket->cls_d, state, 0); -} - -static int cardbus_resume (struct pci_dev *dev) -{ - pci_socket_t *socket = pci_get_drvdata(dev); - return pcmcia_socket_dev_resume(&socket->cls_d, RESUME_RESTORE_STATE); -} - - -static struct pci_device_id cardbus_table [] __devinitdata = { { - .class = PCI_CLASS_BRIDGE_CARDBUS << 8, - .class_mask = ~0, - - .vendor = PCI_ANY_ID, - .device = PCI_ANY_ID, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, -}, { /* all zeroes */ } -}; -MODULE_DEVICE_TABLE(pci, cardbus_table); - -static struct pci_driver pci_cardbus_driver = { - .name = "cardbus", - .id_table = cardbus_table, - .probe = cardbus_probe, - .remove = __devexit_p(cardbus_remove), - .suspend = cardbus_suspend, - .resume = cardbus_resume, -}; - -static int __init pci_socket_init(void) -{ - return pci_register_driver (&pci_cardbus_driver); -} - -static void __exit pci_socket_exit (void) -{ - pci_unregister_driver (&pci_cardbus_driver); -} - -module_init(pci_socket_init); -module_exit(pci_socket_exit); diff -urN orig/drivers/pcmcia/pci_socket.h linux/drivers/pcmcia/pci_socket.h --- orig/drivers/pcmcia/pci_socket.h Sat Apr 12 14:46:55 2003 +++ linux/drivers/pcmcia/pci_socket.h Thu Jan 1 01:00:00 1970 @@ -1,48 +0,0 @@ -/* - * drivers/pcmcia/pci_socket.h - * - * (C) Copyright 1999 Linus Torvalds - */ - -#ifndef __PCI_SOCKET_H -#define __PCI_SOCKET_H - -struct pci_socket_ops; -struct socket_info_t; - -typedef struct pci_socket { - struct pci_dev *dev; - int cb_irq, io_irq; - void *base; - void (*handler)(void *, unsigned int); - void *info; - struct pci_socket_ops *op; - socket_cap_t cap; - spinlock_t event_lock; - unsigned int events; - struct work_struct tq_task; - struct timer_list poll_timer; - - struct pcmcia_socket_class_data cls_d; - /* A few words of private data for the low-level driver.. */ - unsigned int private[8]; -} pci_socket_t; - -struct pci_socket_ops { - int (*open)(struct pci_socket *); - void (*close)(struct pci_socket *); - - int (*init)(struct pci_socket *); - int (*suspend)(struct pci_socket *); - int (*get_status)(struct pci_socket *, unsigned int *); - int (*get_socket)(struct pci_socket *, socket_state_t *); - int (*set_socket)(struct pci_socket *, socket_state_t *); - int (*set_io_map)(struct pci_socket *, struct pccard_io_map *); - int (*set_mem_map)(struct pci_socket *, struct pccard_mem_map *); - void (*proc_setup)(struct pci_socket *, struct proc_dir_entry *base); -}; - -extern struct pci_socket_ops yenta_operations; -extern struct pci_socket_ops ricoh_operations; - -#endif diff -urN orig/drivers/pcmcia/ricoh.h linux/drivers/pcmcia/ricoh.h --- orig/drivers/pcmcia/ricoh.h Sat Apr 12 14:46:55 2003 +++ linux/drivers/pcmcia/ricoh.h Wed Jun 11 22:15:53 2003 @@ -125,11 +125,26 @@ #define rl_mem(socket) ((socket)->private[3]) #define rl_config(socket) ((socket)->private[4]) +static int ricoh_init(struct pcmcia_socket *sock) +{ + struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); + yenta_init(sock); + + config_writew(socket, RL5C4XX_MISC, rl_misc(socket)); + config_writew(socket, RL5C4XX_16BIT_CTL, rl_ctl(socket)); + config_writew(socket, RL5C4XX_16BIT_IO_0, rl_io(socket)); + config_writew(socket, RL5C4XX_16BIT_MEM_0, rl_mem(socket)); + config_writew(socket, RL5C4XX_CONFIG, rl_config(socket)); + + return 0; +} + + /* * Magic Ricoh initialization code.. Save state at * beginning, re-initialize it after suspend. */ -static int ricoh_open(pci_socket_t *socket) +static int ricoh_override(struct yenta_socket *socket) { rl_misc(socket) = config_readw(socket, RL5C4XX_MISC); rl_ctl(socket) = config_readw(socket, RL5C4XX_16BIT_CTL); @@ -146,35 +161,11 @@ rl_config(socket) |= RL5C4XX_CONFIG_PREFETCH; } - return 0; -} - -static int ricoh_init(pci_socket_t *socket) -{ - yenta_init(socket); + socket->socket.ss_entry->init = ricoh_init; - config_writew(socket, RL5C4XX_MISC, rl_misc(socket)); - config_writew(socket, RL5C4XX_16BIT_CTL, rl_ctl(socket)); - config_writew(socket, RL5C4XX_16BIT_IO_0, rl_io(socket)); - config_writew(socket, RL5C4XX_16BIT_MEM_0, rl_mem(socket)); - config_writew(socket, RL5C4XX_CONFIG, rl_config(socket)); - return 0; } -static struct pci_socket_ops ricoh_ops = { - ricoh_open, - yenta_close, - ricoh_init, - yenta_suspend, - yenta_get_status, - yenta_get_socket, - yenta_set_socket, - yenta_set_io_map, - yenta_set_mem_map, - yenta_proc_setup -}; - #endif /* CONFIG_CARDBUS */ #endif /* _LINUX_RICOH_H */ diff -urN orig/drivers/pcmcia/rsrc_mgr.c linux/drivers/pcmcia/rsrc_mgr.c --- orig/drivers/pcmcia/rsrc_mgr.c Mon May 5 17:39:31 2003 +++ linux/drivers/pcmcia/rsrc_mgr.c Thu Jun 12 12:44:48 2003 @@ -52,6 +52,18 @@ #include #include #include "cs_internal.h" +#include "map.h" + +#ifdef CONFIG_FOOTBRIDGE +#define BUS_TO_RESOURCE(x) ((x) + 0x80000000) +#else +#define BUS_TO_RESOURCE(x) (x) +#endif + +struct resource *cb_alloc_io(unsigned long base, unsigned int num, + unsigned long align, char *name, struct pcmcia_socket *s); +struct resource *cb_alloc_mem(unsigned long base, unsigned long num, + unsigned long align, char *name, struct pcmcia_socket *s); /*====================================================================*/ @@ -90,7 +102,7 @@ typedef struct irq_info_t { u_int Attributes; int time_share, dyn_share; - struct socket_info_t *Socket; + struct pcmcia_socket *Socket; } irq_info_t; /* Table of IRQ assignments */ @@ -104,50 +116,12 @@ ======================================================================*/ -static struct resource *resource_parent(unsigned long b, unsigned long n, - int flags, struct pci_dev *dev) -{ -#ifdef CONFIG_PCI - struct resource res, *pr; - - if (dev != NULL) { - res.start = b; - res.end = b + n - 1; - res.flags = flags; - pr = pci_find_parent_resource(dev, &res); - if (pr) - return pr; - } -#endif /* CONFIG_PCI */ - if (flags & IORESOURCE_MEM) - return &iomem_resource; - return &ioport_resource; -} - -/* FIXME: Fundamentally racy. */ -static inline int check_io_resource(unsigned long b, unsigned long n, - struct pci_dev *dev) -{ - struct resource *region; - - region = __request_region(resource_parent(b, n, IORESOURCE_IO, dev), - b, n, "check_io_resource"); - if (!region) - return -EBUSY; - - release_resource(region); - kfree(region); - return 0; -} - /* FIXME: Fundamentally racy. */ -static inline int check_mem_resource(unsigned long b, unsigned long n, - struct pci_dev *dev) +static inline int check_io_resource(unsigned long b, unsigned long n) { struct resource *region; - region = __request_region(resource_parent(b, n, IORESOURCE_MEM, dev), - b, n, "check_mem_resource"); + region = __request_region(&ioport_resource, b, n, "check_io_resource"); if (!region) return -EBUSY; @@ -156,51 +130,6 @@ return 0; } -static struct resource *make_resource(unsigned long b, unsigned long n, - int flags, char *name) -{ - struct resource *res = kmalloc(sizeof(*res), GFP_KERNEL); - - if (res) { - memset(res, 0, sizeof(*res)); - res->name = name; - res->start = b; - res->end = b + n - 1; - res->flags = flags | IORESOURCE_BUSY; - } - return res; -} - -static int request_io_resource(unsigned long b, unsigned long n, - char *name, struct pci_dev *dev) -{ - struct resource *res = make_resource(b, n, IORESOURCE_IO, name); - struct resource *pr = resource_parent(b, n, IORESOURCE_IO, dev); - int err = -ENOMEM; - - if (res) { - err = request_resource(pr, res); - if (err) - kfree(res); - } - return err; -} - -static int request_mem_resource(unsigned long b, unsigned long n, - char *name, struct pci_dev *dev) -{ - struct resource *res = make_resource(b, n, IORESOURCE_MEM, name); - struct resource *pr = resource_parent(b, n, IORESOURCE_MEM, dev); - int err = -ENOMEM; - - if (res) { - err = request_resource(pr, res); - if (err) - kfree(res); - } - return err; -} - /*====================================================================== These manage the internal databases of available resources. @@ -289,7 +218,7 @@ } memset(b, 0, 256); for (i = base, most = 0; i < base+num; i += 8) { - if (check_io_resource(i, 8, NULL)) + if (check_io_resource(i, 8)) continue; hole = inb(i); for (j = 1; j < 8; j++) @@ -302,7 +231,7 @@ bad = any = 0; for (i = base; i < base+num; i += 8) { - if (check_io_resource(i, 8, NULL)) + if (check_io_resource(i, 8)) continue; for (j = 0; j < 8; j++) if (inb(i+j) != most) break; @@ -341,52 +270,107 @@ ======================================================================*/ /* Validation function for cards with a valid CIS */ -static int cis_readable(socket_info_t *s, u_long base) +static int readable(struct pcmcia_socket *s, struct resource *res, cisinfo_t *info) { - cisinfo_t info1, info2; - int ret; - s->cis_mem.sys_start = base; - s->cis_mem.sys_stop = base+s->cap.map_size-1; - s->cis_virt = ioremap(base, s->cap.map_size); - ret = pcmcia_validate_cis(s->clients, &info1); - /* invalidate mapping and CIS cache */ - iounmap(s->cis_virt); - destroy_cis_cache(s); - if ((ret != 0) || (info1.Chains == 0)) - return 0; - s->cis_mem.sys_start = base+s->cap.map_size; - s->cis_mem.sys_stop = base+2*s->cap.map_size-1; - s->cis_virt = ioremap(base+s->cap.map_size, s->cap.map_size); - ret = pcmcia_validate_cis(s->clients, &info2); - iounmap(s->cis_virt); - destroy_cis_cache(s); - return ((ret == 0) && (info1.Chains == info2.Chains)); + /* this can not work for now. */ + int ret = -1; + + s->cis_mem.res = res; + s->cis_virt = ioremap(res->start, s->cap.map_size); + if (s->cis_virt) { + ret = pcmcia_validate_cis(s->clients, info); + /* invalidate mapping and CIS cache */ + iounmap(s->cis_virt); + s->cis_virt = NULL; + s->cis_mem.res = NULL; + destroy_cis_cache(s); + } + if ((ret != 0) || (info->Chains == 0)) + return 0; + return 1; } /* Validation function for simple memory cards */ -static int checksum(socket_info_t *s, u_long base) +static int checksum(struct pcmcia_socket *s, struct resource *res) { - int i, a, b, d; - s->cis_mem.sys_start = base; - s->cis_mem.sys_stop = base+s->cap.map_size-1; - s->cis_virt = ioremap(base, s->cap.map_size); - s->cis_mem.card_start = 0; - s->cis_mem.flags = MAP_ACTIVE; - s->ss_entry->set_mem_map(s->sock, &s->cis_mem); - /* Don't bother checking every word... */ - a = 0; b = -1; - for (i = 0; i < s->cap.map_size; i += 44) { - d = readl(s->cis_virt+i); - a += d; b &= d; - } - iounmap(s->cis_virt); - return (b == -1) ? -1 : (a>>1); + pccard_mem_map map; + int i, a = 0, b = -1, d; + void *virt; + + virt = ioremap(res->start, s->cap.map_size); + if (virt) { + map.map = 0; + map.flags = MAP_ACTIVE; + map.speed = 0; + map.res = res; + map.sys_start = res->start; + map.sys_stop = res->end; + map.card_start = 0; + s->ss_entry->set_mem_map(s, &map); + + /* Don't bother checking every word... */ + for (i = 0; i < s->cap.map_size; i += 44) { + d = readl(virt+i); + a += d; + b &= d; + } + + map.flags = 0; + s->ss_entry->set_mem_map(s, &map); + + iounmap(virt); + } + + return (b == -1) ? -1 : (a>>1); } -static int checksum_match(socket_info_t *s, u_long base) +static int +cis_readable(struct pcmcia_socket *s, unsigned long base, unsigned long size) { - int a = checksum(s, base), b = checksum(s, base+s->cap.map_size); - return ((a == b) && (a >= 0)); + struct resource *res1, *res2; + cisinfo_t info1, info2; + int ret = 0; + + base = BUS_TO_RESOURCE(base); + + res1 = request_mem_region(base, size/2, "cs memory probe"); + res2 = request_mem_region(base + size/2, size/2, "cs memory probe"); + + if (res1 && res2) { + ret = readable(s, res1, &info1); + ret += readable(s, res2, &info2); + } + + if (res2) + release_resource(res2); + if (res1) + release_resource(res1); + + return ((ret == 2) && (info1.Chains == info2.Chains)); +} + +static int +checksum_match(struct pcmcia_socket *s, unsigned long base, unsigned long size) +{ + struct resource *res1, *res2; + int a = -1, b = -1; + + base = BUS_TO_RESOURCE(base); + + res1 = request_mem_region(base, size/2, "cs memory probe"); + res2 = request_mem_region(base + size/2, size/2, "cs memory probe"); + + if (res1 && res2) { + a = checksum(s, res1); + b = checksum(s, res2); + } + + if (res2) + release_resource(res2); + if (res1) + release_resource(res1); + + return ((a == b) && (a >= 0)); } /*====================================================================== @@ -397,7 +381,7 @@ ======================================================================*/ -static int do_mem_probe(u_long base, u_long num, socket_info_t *s) +static int do_mem_probe(u_long base, u_long num, struct pcmcia_socket *s) { u_long i, j, bad, fail, step; @@ -410,16 +394,16 @@ step = 2 * s->cap.map_size; for (i = j = base; i < base+num; i = j + step) { if (!fail) { - for (j = i; j < base+num; j += step) - if ((check_mem_resource(j, step, s->cap.cb_dev) == 0) && - cis_readable(s, j)) + for (j = i; j < base+num; j += step) { + if (cis_readable(s, j, step)) break; + } fail = ((i == base) && (j == base+num)); } if (fail) { for (j = i; j < base+num; j += 2*step) - if ((check_mem_resource(j, 2*step, s->cap.cb_dev) == 0) && - checksum_match(s, j) && checksum_match(s, j + step)) + if (checksum_match(s, j, step) && + checksum_match(s, j + step, step)) break; } if (i != j) { @@ -435,7 +419,7 @@ #ifdef CONFIG_PCMCIA_PROBE -static u_long inv_probe(resource_map_t *m, socket_info_t *s) +static u_long inv_probe(resource_map_t *m, struct pcmcia_socket *s) { u_long ok; if (m == &mem_db) @@ -451,7 +435,7 @@ return do_mem_probe(m->base, m->num, s); } -void validate_mem(socket_info_t *s) +void validate_mem(struct pcmcia_socket *s) { resource_map_t *m, *n; static u_char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 }; @@ -497,7 +481,7 @@ #else /* CONFIG_PCMCIA_PROBE */ -void validate_mem(socket_info_t *s) +void validate_mem(struct pcmcia_socket *s) { resource_map_t *m, *n; static int done = 0; @@ -528,39 +512,51 @@ ======================================================================*/ -int find_io_region(ioaddr_t *base, ioaddr_t num, ioaddr_t align, - char *name, socket_info_t *s) +struct resource * +find_io_region(u_long base, u_long num, u_long align, char *name, struct pcmcia_socket *s) { + struct resource *res = NULL; ioaddr_t try; resource_map_t *m; - int ret = -1; + +#ifdef CONFIG_CARDBUS + if (s->cap.cb_dev) + return cb_alloc_io(base, num, align, name, s); +#endif down(&rsrc_sem); for (m = io_db.next; m != &io_db; m = m->next) { - try = (m->base & ~(align-1)) + *base; + try = (m->base & ~(align-1)) + base; for (try = (try >= m->base) ? try : try+align; (try >= m->base) && (try+num <= m->base+m->num); try += align) { - if (request_io_resource(try, num, name, s->cap.cb_dev) == 0) { - *base = try; - ret = 0; + res = request_region(try, num, name); + if (res) goto out; - } if (!align) break; } } out: up(&rsrc_sem); - return ret; + return res; } -int find_mem_region(u_long *base, u_long num, u_long align, - int force_low, char *name, socket_info_t *s) +struct resource * +find_mem_region(u_long base, u_long num, u_long align, int low, char *name, + struct pcmcia_socket *s) { + struct resource *res = NULL; u_long try; resource_map_t *m; - int ret = -1; + int force_low; + +#ifdef CONFIG_CARDBUS + if (s->cap.cb_dev) + return cb_alloc_mem(base, num, align, name, s); +#endif + + force_low = low || !(s->cap.features & SS_CAP_PAGE_REGS); down(&rsrc_sem); while (1) { @@ -569,15 +565,13 @@ if ((force_low != 0) ^ (m->base < 0x100000)) continue; - try = (m->base & ~(align-1)) + *base; + try = (m->base & ~(align-1)) + base; for (try = (try >= m->base) ? try : try+align; (try >= m->base) && (try+num <= m->base+m->num); try += align) { - if (request_mem_resource(try, num, name, s->cap.cb_dev) == 0) { - *base = try; - ret = 0; + res = request_mem_region(BUS_TO_RESOURCE(try), num, name); + if (res) goto out; - } if (!align) break; } @@ -588,7 +582,7 @@ } out: up(&rsrc_sem); - return ret; + return res; } /*====================================================================== @@ -726,7 +720,7 @@ static int adjust_memory(adjust_t *adj) { u_long base, num; - int i, ret; + int ret; base = adj->resource.memory.Base; num = adj->resource.memory.Size; @@ -742,11 +736,6 @@ break; case REMOVE_MANAGED_RESOURCE: ret = sub_interval(&mem_db, base, num); - if (ret == CS_SUCCESS) { - for (i = 0; i < sockets; i++) { - release_cis_mem(socket_table[i]); - } - } break; default: ret = CS_UNSUPPORTED_FUNCTION; diff -urN orig/drivers/pcmcia/sa1100_generic.c linux/drivers/pcmcia/sa1100_generic.c --- orig/drivers/pcmcia/sa1100_generic.c Sat Apr 12 14:46:56 2003 +++ linux/drivers/pcmcia/sa1100_generic.c Tue Jun 10 22:30:45 2003 @@ -106,19 +106,10 @@ .remove = sa11xx_drv_pcmcia_remove, .name = "sa11x0-pcmcia", .bus = &platform_bus_type, - .devclass = &pcmcia_socket_class, .suspend = pcmcia_socket_dev_suspend, .resume = pcmcia_socket_dev_resume, }; -static struct platform_device sa11x0_pcmcia_device = { - .name = "sa11x0-pcmcia", - .id = 0, - .dev = { - .name = "Intel Corporation SA11x0 [PCMCIA]", - }, -}; - /* sa11x0_pcmcia_init() * ^^^^^^^^^^^^^^^^^^^^ * @@ -129,16 +120,7 @@ */ static int __init sa11x0_pcmcia_init(void) { - int ret; - - ret = driver_register(&sa11x0_pcmcia_driver); - if (ret == 0) { - ret = platform_device_register(&sa11x0_pcmcia_device); - if (ret) - driver_unregister(&sa11x0_pcmcia_driver); - } - - return ret; + return driver_register(&sa11x0_pcmcia_driver); } /* sa11x0_pcmcia_exit() @@ -148,7 +130,6 @@ */ static void __exit sa11x0_pcmcia_exit(void) { - platform_device_unregister(&sa11x0_pcmcia_device); driver_unregister(&sa11x0_pcmcia_driver); } diff -urN orig/drivers/pcmcia/sa11xx_core.c linux/drivers/pcmcia/sa11xx_core.c --- orig/drivers/pcmcia/sa11xx_core.c Tue May 27 10:04:55 2003 +++ linux/drivers/pcmcia/sa11xx_core.c Wed Jun 11 20:59:56 2003 @@ -69,6 +69,8 @@ #define PCMCIA_SOCKET(x) (sa1100_pcmcia_socket + (x)) +#define to_sa1100_socket(x) container_of(x, struct sa1100_pcmcia_socket, socket) + /* * sa1100_pcmcia_default_mecr_timing * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -226,9 +228,9 @@ * * Returns: 0 */ -static int sa1100_pcmcia_sock_init(unsigned int sock) +static int sa1100_pcmcia_sock_init(struct pcmcia_socket *sock) { - struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock); + struct sa1100_pcmcia_socket *skt = to_sa1100_socket(sock); DEBUG(2, "%s(): initializing socket %u\n", __FUNCTION__, skt->nr); @@ -248,9 +250,9 @@ * * Returns: 0 */ -static int sa1100_pcmcia_suspend(unsigned int sock) +static int sa1100_pcmcia_suspend(struct pcmcia_socket *sock) { - struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock); + struct sa1100_pcmcia_socket *skt = to_sa1100_socket(sock); int ret; DEBUG(2, "%s(): suspending socket %u\n", __FUNCTION__, skt->nr); @@ -348,11 +350,11 @@ * Returns: 0 */ static int -sa1100_pcmcia_register_callback(unsigned int sock, +sa1100_pcmcia_register_callback(struct pcmcia_socket *sock, void (*handler)(void *, unsigned int), void *info) { - struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock); + struct sa1100_pcmcia_socket *skt = to_sa1100_socket(sock); if (handler) { if (!try_module_get(skt->ops->owner)) @@ -392,9 +394,9 @@ * Return value is irrelevant; the pcmcia subsystem ignores it. */ static int -sa1100_pcmcia_inquire_socket(unsigned int sock, socket_cap_t *cap) +sa1100_pcmcia_inquire_socket(struct pcmcia_socket *sock, socket_cap_t *cap) { - struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock); + struct sa1100_pcmcia_socket *skt = to_sa1100_socket(sock); int ret = -1; if (skt) { @@ -430,9 +432,9 @@ * Returns: 0 */ static int -sa1100_pcmcia_get_status(unsigned int sock, unsigned int *status) +sa1100_pcmcia_get_status(struct pcmcia_socket *sock, unsigned int *status) { - struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock); + struct sa1100_pcmcia_socket *skt = to_sa1100_socket(sock); skt->status = sa1100_pcmcia_skt_state(skt); *status = skt->status; @@ -450,9 +452,9 @@ * Returns: 0 */ static int -sa1100_pcmcia_get_socket(unsigned int sock, socket_state_t *state) +sa1100_pcmcia_get_socket(struct pcmcia_socket *sock, socket_state_t *state) { - struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock); + struct sa1100_pcmcia_socket *skt = to_sa1100_socket(sock); DEBUG(2, "%s() for sock %u\n", __FUNCTION__, skt->nr); @@ -472,9 +474,9 @@ * Returns: 0 */ static int -sa1100_pcmcia_set_socket(unsigned int sock, socket_state_t *state) +sa1100_pcmcia_set_socket(struct pcmcia_socket *sock, socket_state_t *state) { - struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock); + struct sa1100_pcmcia_socket *skt = to_sa1100_socket(sock); DEBUG(2, "%s() for sock %u\n", __FUNCTION__, skt->nr); @@ -508,9 +510,9 @@ * Returns: 0 on success, -1 on error */ static int -sa1100_pcmcia_set_io_map(unsigned int sock, struct pccard_io_map *map) +sa1100_pcmcia_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *map) { - struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock); + struct sa1100_pcmcia_socket *skt = to_sa1100_socket(sock); unsigned short speed = map->speed; DEBUG(2, "%s() for sock %u\n", __FUNCTION__, skt->nr); @@ -564,9 +566,9 @@ * Returns: 0 on success, -1 on error */ static int -sa1100_pcmcia_set_mem_map(unsigned int sock, struct pccard_mem_map *map) +sa1100_pcmcia_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *map) { - struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock); + struct sa1100_pcmcia_socket *skt = to_sa1100_socket(sock); struct resource *res; unsigned short speed = map->speed; @@ -708,7 +710,7 @@ * Returns: 0 on success, -1 on error */ static void -sa1100_pcmcia_proc_setup(unsigned int sock, struct proc_dir_entry *base) +sa1100_pcmcia_proc_setup(struct pcmcia_socket *sock, struct proc_dir_entry *base) { struct proc_dir_entry *entry; @@ -717,7 +719,7 @@ return; } entry->read_proc = sa1100_pcmcia_proc_status; - entry->data = PCMCIA_SOCKET(sock); + entry->data = to_sa1100_socket(sock); } #else #define sa1100_pcmcia_proc_setup NULL @@ -800,22 +802,16 @@ "PCMCIA socket 1", }; +struct skt_dev_info { + int nskt; +}; + int sa11xx_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops, int first, int nr) { - struct pcmcia_socket_class_data *cls; + struct skt_dev_info *sinfo; unsigned int cpu_clock; int ret, i; - cls = kmalloc(sizeof(struct pcmcia_socket_class_data), GFP_KERNEL); - if (!cls) { - ret = -ENOMEM; - goto out; - } - - memset(cls, 0, sizeof(struct pcmcia_socket_class_data)); - cls->ops = &sa11xx_pcmcia_operations; - cls->nsock = nr; - /* * set default MECR calculation if the board specific * code did not specify one... @@ -823,6 +819,15 @@ if (!ops->socket_get_timing) ops->socket_get_timing = sa1100_pcmcia_default_mecr_timing; + sinfo = kmalloc(sizeof(struct skt_dev_info), GFP_KERNEL); + if (!sinfo) { + ret = -ENOMEM; + goto out; + } + + memset(sinfo, 0, sizeof(struct skt_dev_info)); + sinfo->nskt = nr; + cpu_clock = cpufreq_get(0); /* @@ -832,6 +837,9 @@ struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(i); memset(skt, 0, sizeof(*skt)); + skt->socket.ss_entry = &sa11xx_pcmcia_operations; + skt->socket.dev.dev = dev; + INIT_WORK(&skt->work, sa1100_pcmcia_task_handler, skt); init_timer(&skt->poll_timer); @@ -898,16 +906,26 @@ goto out_err_6; skt->status = sa1100_pcmcia_skt_state(skt); + + ret = pcmcia_register_socket(&skt->socket); + if (ret) + goto out_err_7; + + WARN_ON(skt->socket.sock != i); + add_timer(&skt->poll_timer); } - dev->class_data = cls; + dev_set_drvdata(dev, sinfo); return 0; do { struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(i); del_timer_sync(&skt->poll_timer); + pcmcia_unregister_socket(&skt->socket); + + out_err_7: flush_scheduled_work(); ops->hw_shutdown(skt); @@ -925,7 +943,7 @@ i--; } while (i > 0); - kfree(cls); + kfree(sinfo); out: return ret; @@ -934,19 +952,22 @@ int sa11xx_drv_pcmcia_remove(struct device *dev) { - struct pcmcia_socket_class_data *cls = dev->class_data; + struct skt_dev_info *sinfo = dev_get_drvdata(dev); int i; - dev->class_data = NULL; - - for (i = 0; i < cls->nsock; i++) { - struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(cls->sock_offset + i); + dev_set_drvdata(dev, NULL); - skt->ops->hw_shutdown(skt); + for (i = 0; i < sinfo->nskt; i++) { + struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(i); del_timer_sync(&skt->poll_timer); + + pcmcia_unregister_socket(&skt->socket); + flush_scheduled_work(); + skt->ops->hw_shutdown(skt); + sa1100_pcmcia_config_skt(skt, &dead_socket); iounmap(skt->virt_io); @@ -957,7 +978,7 @@ release_resource(&skt->res_skt); } - kfree(cls); + kfree(sinfo); return 0; } @@ -977,7 +998,8 @@ for (sock = 0; sock < SA1100_PCMCIA_MAX_SOCK; ++sock) { struct sa1100_pcmcia_socket *skt = PCMCIA_SOCKET(sock); - sa1100_pcmcia_set_mecr(skt, clock); + if (skt->ops) + sa1100_pcmcia_set_mecr(skt, clock); } } diff -urN orig/drivers/pcmcia/sa11xx_core.h linux/drivers/pcmcia/sa11xx_core.h --- orig/drivers/pcmcia/sa11xx_core.h Sat Apr 12 14:46:56 2003 +++ linux/drivers/pcmcia/sa11xx_core.h Tue Jun 10 22:29:45 2003 @@ -44,6 +44,8 @@ * use when responding to a Card Services query of some kind. */ struct sa1100_pcmcia_socket { + struct pcmcia_socket socket; + /* * Info from low level handler */ diff -urN orig/drivers/pcmcia/tcic.c linux/drivers/pcmcia/tcic.c --- orig/drivers/pcmcia/tcic.c Tue May 27 10:04:55 2003 +++ linux/drivers/pcmcia/tcic.c Wed Jun 11 23:07:42 2003 @@ -115,19 +115,20 @@ static void tcic_timer(u_long data); static struct pccard_operations tcic_operations; -typedef struct socket_info_t { +struct tcic_socket { u_short psock; void (*handler)(void *info, u_int events); void *info; u_char last_sstat; u_char id; -} socket_info_t; + struct pcmcia_socket socket; +}; static struct timer_list poll_timer; static int tcic_timer_pending; static int sockets; -static socket_info_t socket_table[2]; +static struct tcic_socket socket_table[2]; static socket_cap_t tcic_cap = { /* only 16-bit cards, memory windows must be size-aligned */ @@ -372,15 +373,11 @@ /*====================================================================*/ -static struct pcmcia_socket_class_data tcic_data = { - .ops = &tcic_operations, -}; - static struct device_driver tcic_driver = { .name = "tcic-pcmcia", .bus = &platform_bus_type, -/* .suspend = pcmcia_socket_dev_suspend, FIXME? */ -/* .resume = pcmcia_socket_dev_resume, FIXME? */ + .suspend = pcmcia_socket_dev_suspend, + .resume = pcmcia_socket_dev_resume, }; static struct platform_device tcic_device = { @@ -391,13 +388,10 @@ }, }; -static struct class_device tcic_class_data = { - .class = &pcmcia_socket_class, -}; static int __init init_tcic(void) { - int i, sock; + int i, sock, ret = 0; u_int mask, scan; servinfo_t serv; @@ -524,13 +518,17 @@ /* jump start interrupt handler, if needed */ tcic_interrupt(0, NULL, NULL); - tcic_data.nsock = sockets; - tcic_class_data.dev = &tcic_device.dev; - tcic_class_data.class_data = &tcic_data; - strlcpy(tcic_class_data.class_id, "tcic-pcmcia", BUS_ID_SIZE); - platform_device_register(&tcic_device); - class_device_register(&tcic_class_data); + + for (i = 0; i < sockets; i++) { + socket_table[i].socket.ss_entry = &tcic_operations; + socket_table[i].socket.dev.dev = &tcic_device.dev; + ret = pcmcia_register_socket(&socket_table[i].socket); + if (ret && i) + pcmcia_unregister_socket(&socket_table[0].socket); + } + + return ret; return 0; @@ -540,13 +538,19 @@ static void __exit exit_tcic(void) { + int i; + del_timer_sync(&poll_timer); if (cs_irq != 0) { tcic_aux_setw(TCIC_AUX_SYSCFG, TCIC_SYSCFG_AUTOBUSY|0x0a00); free_irq(cs_irq, tcic_interrupt); } release_region(tcic_base, 16); - class_device_unregister(&tcic_class_data); + + for (i = 0; i < sockets; i++) { + pcmcia_unregister_socket(&socket_table[i].socket); + } + platform_device_unregister(&tcic_device); driver_unregister(&tcic_driver); } /* exit_tcic */ @@ -640,18 +644,19 @@ /*====================================================================*/ -static int tcic_register_callback(unsigned int lsock, void (*handler)(void *, unsigned int), void * info) +static int tcic_register_callback(struct pcmcia_socket *sock, void (*handler)(void *, unsigned int), void * info) { - socket_table[lsock].handler = handler; - socket_table[lsock].info = info; + u_short psock = container_of(sock, struct tcic_socket, socket)->psock; + socket_table[psock].handler = handler; + socket_table[psock].info = info; return 0; } /* tcic_register_callback */ /*====================================================================*/ -static int tcic_get_status(unsigned int lsock, u_int *value) +static int tcic_get_status(struct pcmcia_socket *sock, u_int *value) { - u_short psock = socket_table[lsock].psock; + u_short psock = container_of(sock, struct tcic_socket, socket)->psock; u_char reg; tcic_setl(TCIC_ADDR, (psock << TCIC_ADDR_SS_SHFT) @@ -675,7 +680,7 @@ /*====================================================================*/ -static int tcic_inquire_socket(unsigned int lsock, socket_cap_t *cap) +static int tcic_inquire_socket(struct pcmcia_socket *sock, socket_cap_t *cap) { *cap = tcic_cap; return 0; @@ -683,9 +688,9 @@ /*====================================================================*/ -static int tcic_get_socket(unsigned int lsock, socket_state_t *state) +static int tcic_get_socket(struct pcmcia_socket *sock, socket_state_t *state) { - u_short psock = socket_table[lsock].psock; + u_short psock = container_of(sock, struct tcic_socket, socket)->psock; u_char reg; u_short scf1, scf2; @@ -736,9 +741,9 @@ /*====================================================================*/ -static int tcic_set_socket(unsigned int lsock, socket_state_t *state) +static int tcic_set_socket(struct pcmcia_socket *sock, socket_state_t *state) { - u_short psock = socket_table[lsock].psock; + u_short psock = container_of(sock, struct tcic_socket, socket)->psock; u_char reg; u_short scf1, scf2; @@ -811,9 +816,9 @@ /*====================================================================*/ -static int tcic_set_io_map(unsigned int lsock, struct pccard_io_map *io) +static int tcic_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io) { - u_short psock = socket_table[lsock].psock; + u_short psock = container_of(sock, struct tcic_socket, socket)->psock; u_int addr; u_short base, len, ioctl; @@ -848,9 +853,9 @@ /*====================================================================*/ -static int tcic_set_mem_map(unsigned int lsock, struct pccard_mem_map *mem) +static int tcic_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *mem) { - u_short psock = socket_table[lsock].psock; + u_short psock = container_of(sock, struct tcic_socket, socket)->psock; u_short addr, ctl; u_long base, len, mmap; @@ -892,11 +897,11 @@ /*====================================================================*/ -static void tcic_proc_setup(unsigned int sock, struct proc_dir_entry *base) +static void tcic_proc_setup(struct pcmcia_socket *sock, struct proc_dir_entry *base) { } -static int tcic_init(unsigned int s) +static int tcic_init(struct pcmcia_socket *s) { int i; pccard_io_map io = { 0, 0, 0, 0, 1 }; @@ -915,7 +920,7 @@ return 0; } -static int tcic_suspend(unsigned int sock) +static int tcic_suspend(struct pcmcia_socket *sock) { return tcic_set_socket(sock, &dead_socket); } diff -urN orig/drivers/pcmcia/ti113x.h linux/drivers/pcmcia/ti113x.h --- orig/drivers/pcmcia/ti113x.h Sat Apr 12 14:46:56 2003 +++ linux/drivers/pcmcia/ti113x.h Wed Jun 11 22:15:53 2003 @@ -136,6 +136,26 @@ #ifdef CONFIG_CARDBUS +static int ti_intctl(struct yenta_socket *socket) +{ + u8 new, reg = exca_readb(socket, I365_INTCTL); + + new = reg & ~I365_INTR_ENA; + if (socket->cb_irq) + new |= I365_INTR_ENA; + if (new != reg) + exca_writeb(socket, I365_INTCTL, new); + return 0; +} + +static int ti_init(struct pcmcia_socket *sock) +{ + struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); + yenta_init(sock); + ti_intctl(socket); + return 0; +} + /* * Generic TI init - TI has an extension for the * INTCTL register that sets the PCI CSC interrupt. @@ -148,70 +168,28 @@ * This makes us correctly get PCI CSC interrupt * events. */ -static int ti_open(pci_socket_t *socket) +static int ti_override(struct yenta_socket *socket) { u8 new, reg = exca_readb(socket, I365_INTCTL); new = reg & ~I365_INTR_ENA; if (new != reg) exca_writeb(socket, I365_INTCTL, new); + socket->socket.ss_entry->init = ti_init; return 0; } -static int ti_intctl(pci_socket_t *socket) -{ - u8 new, reg = exca_readb(socket, I365_INTCTL); - - new = reg & ~I365_INTR_ENA; - if (socket->cb_irq) - new |= I365_INTR_ENA; - if (new != reg) - exca_writeb(socket, I365_INTCTL, new); - return 0; -} - -static int ti_init(pci_socket_t *socket) -{ - yenta_init(socket); - ti_intctl(socket); - return 0; -} - -static struct pci_socket_ops ti_ops = { - ti_open, - yenta_close, - ti_init, - yenta_suspend, - yenta_get_status, - yenta_get_socket, - yenta_set_socket, - yenta_set_io_map, - yenta_set_mem_map, - yenta_proc_setup -}; - #define ti_sysctl(socket) ((socket)->private[0]) #define ti_cardctl(socket) ((socket)->private[1]) #define ti_devctl(socket) ((socket)->private[2]) #define ti_diag(socket) ((socket)->private[3]) #define ti_irqmux(socket) ((socket)->private[4]) -static int ti113x_open(pci_socket_t *socket) -{ - ti_sysctl(socket) = config_readl(socket, TI113X_SYSTEM_CONTROL); - ti_cardctl(socket) = config_readb(socket, TI113X_CARD_CONTROL); - ti_devctl(socket) = config_readb(socket, TI113X_DEVICE_CONTROL); - - ti_cardctl(socket) &= ~(TI113X_CCR_PCI_IRQ_ENA | TI113X_CCR_PCI_IREQ | TI113X_CCR_PCI_CSC); - if (socket->cb_irq) - ti_cardctl(socket) |= TI113X_CCR_PCI_IRQ_ENA | TI113X_CCR_PCI_CSC | TI113X_CCR_PCI_IREQ; - ti_open(socket); - return 0; -} -static int ti113x_init(pci_socket_t *socket) +static int ti113x_init(struct pcmcia_socket *sock) { - yenta_init(socket); + struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); + yenta_init(sock); config_writel(socket, TI113X_SYSTEM_CONTROL, ti_sysctl(socket)); config_writeb(socket, TI113X_CARD_CONTROL, ti_cardctl(socket)); @@ -220,35 +198,26 @@ return 0; } -static struct pci_socket_ops ti113x_ops = { - ti113x_open, - yenta_close, - ti113x_init, - yenta_suspend, - yenta_get_status, - yenta_get_socket, - yenta_set_socket, - yenta_set_io_map, - yenta_set_mem_map, - yenta_proc_setup -}; - - -static int ti1250_open(pci_socket_t *socket) +static int ti113x_override(struct yenta_socket *socket) { - ti_diag(socket) = config_readb(socket, TI1250_DIAGNOSTIC); + ti_sysctl(socket) = config_readl(socket, TI113X_SYSTEM_CONTROL); + ti_cardctl(socket) = config_readb(socket, TI113X_CARD_CONTROL); + ti_devctl(socket) = config_readb(socket, TI113X_DEVICE_CONTROL); - ti_diag(socket) &= ~(TI1250_DIAG_PCI_CSC | TI1250_DIAG_PCI_IREQ); + ti_cardctl(socket) &= ~(TI113X_CCR_PCI_IRQ_ENA | TI113X_CCR_PCI_IREQ | TI113X_CCR_PCI_CSC); if (socket->cb_irq) - ti_diag(socket) |= TI1250_DIAG_PCI_CSC | TI1250_DIAG_PCI_IREQ; - ti113x_open(socket); + ti_cardctl(socket) |= TI113X_CCR_PCI_IRQ_ENA | TI113X_CCR_PCI_CSC | TI113X_CCR_PCI_IREQ; + ti_override(socket); + socket->socket.ss_entry->init = ti113x_init; return 0; } -static int ti1250_init(pci_socket_t *socket) + +static int ti1250_init(struct pcmcia_socket *sock) { - yenta_init(socket); - ti113x_init(socket); + struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); + yenta_init(sock); + ti113x_init(sock); ti_irqmux(socket) = config_readl(socket, TI122X_IRQMUX); ti_irqmux(socket) = (ti_irqmux(socket) & ~0x0f) | 0x02; /* route INTA */ if (!(ti_sysctl(socket) & TI122X_SCR_INTRTIE)) @@ -260,18 +229,17 @@ return 0; } -static struct pci_socket_ops ti1250_ops = { - ti1250_open, - yenta_close, - ti1250_init, - yenta_suspend, - yenta_get_status, - yenta_get_socket, - yenta_set_socket, - yenta_set_io_map, - yenta_set_mem_map, - yenta_proc_setup -}; +static int ti1250_override(struct yenta_socket *socket) +{ + ti_diag(socket) = config_readb(socket, TI1250_DIAGNOSTIC); + + ti_diag(socket) &= ~(TI1250_DIAG_PCI_CSC | TI1250_DIAG_PCI_IREQ); + if (socket->cb_irq) + ti_diag(socket) |= TI1250_DIAG_PCI_CSC | TI1250_DIAG_PCI_IREQ; + ti113x_override(socket); + socket->socket.ss_entry->init = ti1250_init; + return 0; +} #endif /* CONFIG_CARDBUS */ diff -urN orig/drivers/pcmcia/yenta.c linux/drivers/pcmcia/yenta.c --- orig/drivers/pcmcia/yenta.c Mon May 5 17:39:31 2003 +++ linux/drivers/pcmcia/yenta.c Thu Jun 12 12:44:48 2003 @@ -1,5 +1,5 @@ /* - * Regular lowlevel cardbus driver ("yenta") + * Regular cardbus driver ("yenta") * * (C) Copyright 1999, 2000 Linus Torvalds * @@ -7,6 +7,8 @@ * Aug 2002: Manfred Spraul * Dynamically adjust the size of the bridge resource * + * May 2003: Dominik Brodowski + * Merge pci_socket.c and yenta.c into one file */ #include #include @@ -26,6 +28,7 @@ #include "yenta.h" #include "i82365.h" + #if 0 #define DEBUG(x,args...) printk("%s: " x, __FUNCTION__, ##args) #else @@ -41,20 +44,20 @@ * regular memory space ("cb_xxx"), configuration space * ("config_xxx") and compatibility space ("exca_xxxx") */ -static inline u32 cb_readl(pci_socket_t *socket, unsigned reg) +static inline u32 cb_readl(struct yenta_socket *socket, unsigned reg) { u32 val = readl(socket->base + reg); DEBUG("%p %04x %08x\n", socket, reg, val); return val; } -static inline void cb_writel(pci_socket_t *socket, unsigned reg, u32 val) +static inline void cb_writel(struct yenta_socket *socket, unsigned reg, u32 val) { DEBUG("%p %04x %08x\n", socket, reg, val); writel(val, socket->base + reg); } -static inline u8 config_readb(pci_socket_t *socket, unsigned offset) +static inline u8 config_readb(struct yenta_socket *socket, unsigned offset) { u8 val; pci_read_config_byte(socket->dev, offset, &val); @@ -62,7 +65,7 @@ return val; } -static inline u16 config_readw(pci_socket_t *socket, unsigned offset) +static inline u16 config_readw(struct yenta_socket *socket, unsigned offset) { u16 val; pci_read_config_word(socket->dev, offset, &val); @@ -70,7 +73,7 @@ return val; } -static inline u32 config_readl(pci_socket_t *socket, unsigned offset) +static inline u32 config_readl(struct yenta_socket *socket, unsigned offset) { u32 val; pci_read_config_dword(socket->dev, offset, &val); @@ -78,32 +81,32 @@ return val; } -static inline void config_writeb(pci_socket_t *socket, unsigned offset, u8 val) +static inline void config_writeb(struct yenta_socket *socket, unsigned offset, u8 val) { DEBUG("%p %04x %02x\n", socket, offset, val); pci_write_config_byte(socket->dev, offset, val); } -static inline void config_writew(pci_socket_t *socket, unsigned offset, u16 val) +static inline void config_writew(struct yenta_socket *socket, unsigned offset, u16 val) { DEBUG("%p %04x %04x\n", socket, offset, val); pci_write_config_word(socket->dev, offset, val); } -static inline void config_writel(pci_socket_t *socket, unsigned offset, u32 val) +static inline void config_writel(struct yenta_socket *socket, unsigned offset, u32 val) { DEBUG("%p %04x %08x\n", socket, offset, val); pci_write_config_dword(socket->dev, offset, val); } -static inline u8 exca_readb(pci_socket_t *socket, unsigned reg) +static inline u8 exca_readb(struct yenta_socket *socket, unsigned reg) { u8 val = readb(socket->base + 0x800 + reg); DEBUG("%p %04x %02x\n", socket, reg, val); return val; } -static inline u8 exca_readw(pci_socket_t *socket, unsigned reg) +static inline u8 exca_readw(struct yenta_socket *socket, unsigned reg) { u16 val; val = readb(socket->base + 0x800 + reg); @@ -112,13 +115,13 @@ return val; } -static inline void exca_writeb(pci_socket_t *socket, unsigned reg, u8 val) +static inline void exca_writeb(struct yenta_socket *socket, unsigned reg, u8 val) { DEBUG("%p %04x %02x\n", socket, reg, val); writeb(val, socket->base + 0x800 + reg); } -static void exca_writew(pci_socket_t *socket, unsigned reg, u16 val) +static void exca_writew(struct yenta_socket *socket, unsigned reg, u16 val) { DEBUG("%p %04x %04x\n", socket, reg, val); writeb(val, socket->base + 0x800 + reg); @@ -129,8 +132,9 @@ * Ugh, mixed-mode cardbus and 16-bit pccard state: things depend * on what kind of card is inserted.. */ -static int yenta_get_status(pci_socket_t *socket, unsigned int *value) +static int yenta_get_status(struct pcmcia_socket *sock, unsigned int *value) { + struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); unsigned int val; u32 state = cb_readl(socket, CB_SOCKET_STATE); @@ -181,8 +185,9 @@ } } -static int yenta_get_socket(pci_socket_t *socket, socket_state_t *state) +static int yenta_get_socket(struct pcmcia_socket *sock, socket_state_t *state) { + struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); u8 reg; u32 control; @@ -221,7 +226,7 @@ return 0; } -static void yenta_set_power(pci_socket_t *socket, socket_state_t *state) +static void yenta_set_power(struct yenta_socket *socket, socket_state_t *state) { u32 reg = 0; /* CB_SC_STPCLK? */ switch (state->Vcc) { @@ -238,8 +243,9 @@ cb_writel(socket, CB_SOCKET_CONTROL, reg); } -static int yenta_set_socket(pci_socket_t *socket, socket_state_t *state) +static int yenta_set_socket(struct pcmcia_socket *sock, socket_state_t *state) { + struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); u16 bridge; if (state->flags & SS_DEBOUNCED) { @@ -300,8 +306,9 @@ return 0; } -static int yenta_set_io_map(pci_socket_t *socket, struct pccard_io_map *io) +static int yenta_set_io_map(struct pcmcia_socket *sock, struct pccard_io_map *io) { + struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); int map; unsigned char ioctl, addr, enable; @@ -333,16 +340,20 @@ return 0; } -static int yenta_set_mem_map(pci_socket_t *socket, struct pccard_mem_map *mem) +static int yenta_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *mem) { + struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); + struct pci_bus_region region; int map; unsigned char addr, enable; unsigned int start, stop, card_start; unsigned short word; + pcibios_resource_to_bus(socket->dev, ®ion, mem->res); + map = mem->map; - start = mem->sys_start; - stop = mem->sys_stop; + start = region.start; + stop = region.end; card_start = mem->card_start; if (map > 4 || start > stop || ((start ^ stop) >> 24) || @@ -386,12 +397,12 @@ return 0; } -static void yenta_proc_setup(pci_socket_t *socket, struct proc_dir_entry *base) +static void yenta_proc_setup(struct pcmcia_socket *sock, struct proc_dir_entry *base) { /* Not done yet */ } -static unsigned int yenta_events(pci_socket_t *socket) +static unsigned int yenta_events(struct yenta_socket *socket) { u8 csc; u32 cb_event; @@ -418,7 +429,7 @@ static void yenta_bh(void *data) { - pci_socket_t *socket = data; + struct yenta_socket *socket = data; unsigned int events; spin_lock_irq(&socket->event_lock); @@ -432,7 +443,7 @@ static irqreturn_t yenta_interrupt(int irq, void *dev_id, struct pt_regs *regs) { unsigned int events; - pci_socket_t *socket = (pci_socket_t *) dev_id; + struct yenta_socket *socket = (struct yenta_socket *) dev_id; events = yenta_events(socket); if (events) { @@ -447,7 +458,7 @@ static void yenta_interrupt_wrapper(unsigned long data) { - pci_socket_t *socket = (pci_socket_t *) data; + struct yenta_socket *socket = (struct yenta_socket *) data; yenta_interrupt(0, (void *)socket, NULL); socket->poll_timer.expires = jiffies + HZ; @@ -465,7 +476,7 @@ */ static u32 isa_interrupts = 0x0ef8; -static unsigned int yenta_probe_irq(pci_socket_t *socket, u32 isa_irq_mask) +static unsigned int yenta_probe_irq(struct yenta_socket *socket, u32 isa_irq_mask) { int i; unsigned long val; @@ -509,7 +520,7 @@ /* * Set static data that doesn't need re-initializing.. */ -static void yenta_get_socket_capabilities(pci_socket_t *socket, u32 isa_irq_mask) +static void yenta_get_socket_capabilities(struct yenta_socket *socket, u32 isa_irq_mask) { socket->cap.features |= SS_CAP_PAGE_REGS | SS_CAP_PCCARD | SS_CAP_CARDBUS; socket->cap.map_size = 0x1000; @@ -520,35 +531,44 @@ printk("Yenta IRQ list %04x, PCI irq%d\n", socket->cap.irq_mask, socket->cb_irq); } -static void yenta_clear_maps(pci_socket_t *socket) +static int yenta_inquire_socket(struct pcmcia_socket *sock, socket_cap_t *cap) +{ + struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); + + *cap = socket->cap; + + return 0; +} + + +static void yenta_clear_maps(struct yenta_socket *socket) { int i; pccard_io_map io = { 0, 0, 0, 0, 1 }; pccard_mem_map mem = { 0, 0, 0, 0, 0, 0 }; mem.sys_stop = 0x0fff; - yenta_set_socket(socket, &dead_socket); + yenta_set_socket(&socket->socket, &dead_socket); for (i = 0; i < 2; i++) { io.map = i; - yenta_set_io_map(socket, &io); + yenta_set_io_map(&socket->socket, &io); } for (i = 0; i < 5; i++) { mem.map = i; - yenta_set_mem_map(socket, &mem); + yenta_set_mem_map(&socket->socket, &mem); } } /* * Initialize the standard cardbus registers */ -static void yenta_config_init(pci_socket_t *socket) +static void yenta_config_init(struct yenta_socket *socket) { u16 bridge; struct pci_dev *dev = socket->dev; - pci_set_power_state(socket->dev, 0); - config_writel(socket, CB_LEGACY_MODE_BASE, 0); +#if 0 config_writel(socket, PCI_BASE_ADDRESS_0, dev->resource[0].start); config_writew(socket, PCI_COMMAND, PCI_COMMAND_IO | @@ -564,6 +584,7 @@ (dev->subordinate->subordinate << 16) | /* subordinate bus */ (dev->subordinate->secondary << 8) | /* secondary bus */ dev->subordinate->primary); /* primary bus */ +#endif /* * Set up the bridging state: @@ -586,8 +607,12 @@ } /* Called at resume and initialization events */ -static int yenta_init(pci_socket_t *socket) +static int yenta_init(struct pcmcia_socket *sock) { + struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); + pci_set_power_state(socket->dev, 0); + pci_restore_state(socket->dev, socket->saved_state); + yenta_config_init(socket); yenta_clear_maps(socket); @@ -596,13 +621,17 @@ return 0; } -static int yenta_suspend(pci_socket_t *socket) +static int yenta_suspend(struct pcmcia_socket *sock) { - yenta_set_socket(socket, &dead_socket); + struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); + + yenta_set_socket(sock, &dead_socket); /* Disable interrupts */ cb_writel(socket, CB_SOCKET_MASK, 0x0); + pci_save_state(socket->dev, socket->saved_state); + /* * This does not work currently. The controller * loses too much information during D3 to come up @@ -618,6 +647,7 @@ return 0; } +#if 0 /* * Use an adaptive allocation for the memory resource, * sometimes the memory behind pci bridges is limited: @@ -630,7 +660,7 @@ #define BRIDGE_IO_MAX 256 #define BRIDGE_IO_MIN 32 -static void yenta_allocate_res(pci_socket_t *socket, int nr, unsigned type) +static void yenta_allocate_res(struct yenta_socket *socket, int nr, unsigned type) { struct pci_bus *bus; struct resource *root, *res; @@ -644,7 +674,7 @@ if (type & IORESOURCE_IO) mask = ~3; - offset = 0x1c + 8*nr; + offset = PCI_CB_MEMORY_BASE_0 + 8*nr; bus = socket->dev->subordinate; res = socket->dev->resource + PCI_BRIDGE_RESOURCES + nr; res->name = bus->name; @@ -711,7 +741,7 @@ /* * Allocate the bridge mappings for the device.. */ -static void yenta_allocate_resources(pci_socket_t *socket) +static void yenta_allocate_resources(struct yenta_socket *socket) { yenta_allocate_res(socket, 0, IORESOURCE_MEM|IORESOURCE_PREFETCH); yenta_allocate_res(socket, 1, IORESOURCE_MEM); @@ -719,10 +749,11 @@ yenta_allocate_res(socket, 3, IORESOURCE_IO); /* PCI isn't clever enough to use this one yet */ } + /* * Free the bridge mappings for the device.. */ -static void yenta_free_resources(pci_socket_t *socket) +static void yenta_free_resources(struct yenta_socket *socket) { int i; for (i=0;i<4;i++) { @@ -733,11 +764,16 @@ res->start = res->end = 0; } } +#endif + + /* * Close it down - release our resources and go home.. */ -static void yenta_close(pci_socket_t *sock) +static void yenta_close(struct pci_dev *dev) { + struct yenta_socket *sock = pci_get_drvdata(dev); + /* Disable all events so we don't die in an IRQ storm */ cb_writel(sock, CB_SOCKET_MASK, 0x0); exca_writeb(sock, I365_CSCINT, 0); @@ -749,9 +785,40 @@ if (sock->base) iounmap(sock->base); +#if 0 yenta_free_resources(sock); +#endif + pcmcia_unregister_socket(&sock->socket); + pci_set_drvdata(dev, NULL); + pci_disable_device(sock->dev); } + +static int yenta_register_callback(struct pcmcia_socket *sock, void (*handler)(void *, unsigned int), void * info) +{ + struct yenta_socket *socket = container_of(sock, struct yenta_socket, socket); + + socket->handler = handler; + socket->info = info; + return 0; +} + + +static struct pccard_operations yenta_socket_operations = { + .owner = THIS_MODULE, + .init = yenta_init, + .suspend = yenta_suspend, + .register_callback = yenta_register_callback, + .inquire_socket = yenta_inquire_socket, + .get_status = yenta_get_status, + .get_socket = yenta_get_socket, + .set_socket = yenta_set_socket, + .set_io_map = yenta_set_io_map, + .set_mem_map = yenta_set_mem_map, + .proc_setup = yenta_proc_setup, +}; + + #include "ti113x.h" #include "ricoh.h" @@ -760,49 +827,62 @@ * initialization sequences etc details. List them here.. */ #define PD(x,y) PCI_VENDOR_ID_##x, PCI_DEVICE_ID_##x##_##y -static struct cardbus_override_struct { +struct cardbus_override_struct { unsigned short vendor; unsigned short device; - struct pci_socket_ops *op; + int (*override) (struct yenta_socket *socket); } cardbus_override[] = { - { PD(TI,1130), &ti113x_ops }, - { PD(TI,1031), &ti_ops }, - { PD(TI,1131), &ti113x_ops }, - { PD(TI,1250), &ti1250_ops }, - { PD(TI,1220), &ti_ops }, - { PD(TI,1221), &ti_ops }, - { PD(TI,1210), &ti_ops }, - { PD(TI,1450), &ti_ops }, - { PD(TI,1225), &ti_ops }, - { PD(TI,1251A), &ti_ops }, - { PD(TI,1211), &ti_ops }, - { PD(TI,1251B), &ti_ops }, - { PD(TI,1410), &ti1250_ops }, - { PD(TI,1420), &ti_ops }, - { PD(TI,4410), &ti_ops }, - { PD(TI,4451), &ti_ops }, - - { PD(RICOH,RL5C465), &ricoh_ops }, - { PD(RICOH,RL5C466), &ricoh_ops }, - { PD(RICOH,RL5C475), &ricoh_ops }, - { PD(RICOH,RL5C476), &ricoh_ops }, - { PD(RICOH,RL5C478), &ricoh_ops } -}; - -#define NR_OVERRIDES (sizeof(cardbus_override)/sizeof(struct cardbus_override_struct)) + { PD(TI,1130), &ti113x_override }, + { PD(TI,1031), &ti_override }, + { PD(TI,1131), &ti113x_override }, + { PD(TI,1250), &ti1250_override }, + { PD(TI,1220), &ti_override }, + { PD(TI,1221), &ti_override }, + { PD(TI,1210), &ti_override }, + { PD(TI,1450), &ti_override }, + { PD(TI,1225), &ti_override }, + { PD(TI,1251A), &ti_override }, + { PD(TI,1211), &ti_override }, + { PD(TI,1251B), &ti_override }, + { PD(TI,1410), ti1250_override }, + { PD(TI,1420), &ti_override }, + { PD(TI,4410), &ti_override }, + { PD(TI,4451), &ti_override }, + + { PD(RICOH,RL5C465), &ricoh_override }, + { PD(RICOH,RL5C466), &ricoh_override }, + { PD(RICOH,RL5C475), &ricoh_override }, + { PD(RICOH,RL5C476), &ricoh_override }, + { PD(RICOH,RL5C478), &ricoh_override }, + { }, /* all zeroes */ +}; -extern int cardbus_register(struct pci_dev *p_dev); /* * Initialize a cardbus controller. Make sure we have a usable * interrupt, and that we can map the cardbus area. Fill in the * socket information structure.. */ -static int yenta_open(pci_socket_t *socket) +static int __devinit yenta_probe (struct pci_dev *dev, const struct pci_device_id *id) { - int i; - struct pci_dev *dev = socket->dev; + struct yenta_socket *socket; + struct cardbus_override_struct *d; + + socket = kmalloc(sizeof(struct yenta_socket), GFP_KERNEL); + if (!socket) + return -ENOMEM; + memset(socket, 0, sizeof(*socket)); + + /* prepare pcmcia_socket */ + socket->socket.ss_entry = ¥ta_socket_operations; + socket->socket.dev.dev = &dev->dev; + socket->socket.driver_data = socket; + + /* prepare struct yenta_socket */ + socket->dev = dev; + pci_set_drvdata(dev, socket); + spin_lock_init(&socket->event_lock); /* * Do some basic sanity checking.. @@ -827,22 +907,26 @@ /* Disable all events */ cb_writel(socket, CB_SOCKET_MASK, 0x0); +#if 0 /* Set up the bridge regions.. */ yenta_allocate_resources(socket); +#endif + + pci_set_master(dev); + /* FIXME: should we also pci_set_mwi(dev); ? */ + pci_save_state(dev, socket->saved_state); socket->cb_irq = dev->irq; /* Do we have special options for the device? */ - for (i = 0; i < NR_OVERRIDES; i++) { - struct cardbus_override_struct *d = cardbus_override+i; - if (dev->vendor == d->vendor && dev->device == d->device) { - socket->op = d->op; - if (d->op->open) { - int retval = d->op->open(socket); - if (retval < 0) - return retval; - } + d = cardbus_override; + while (d->override) { + if ((dev->vendor == d->vendor) && (dev->device == d->device)) { + int retval = d->override(socket); + if (retval < 0) + return retval; } + d++; } /* We must finish initialization here */ @@ -864,23 +948,58 @@ printk("Socket status: %08x\n", cb_readl(socket, CB_SOCKET_STATE)); /* Register it with the pcmcia layer.. */ - return cardbus_register(dev); + return pcmcia_register_socket(&socket->socket); } -/* - * Standard plain cardbus - no frills, no extensions - */ -struct pci_socket_ops yenta_operations = { - yenta_open, - yenta_close, - yenta_init, - yenta_suspend, - yenta_get_status, - yenta_get_socket, - yenta_set_socket, - yenta_set_io_map, - yenta_set_mem_map, - yenta_proc_setup + +static int yenta_dev_suspend (struct pci_dev *dev, u32 state) +{ + return pcmcia_socket_dev_suspend(&dev->dev, state, 0); +} + + +static int yenta_dev_resume (struct pci_dev *dev) +{ + return pcmcia_socket_dev_resume(&dev->dev, RESUME_RESTORE_STATE); +} + + +static struct pci_device_id yenta_table [] __devinitdata = { { + .class = PCI_CLASS_BRIDGE_CARDBUS << 8, + .class_mask = ~0, + + .vendor = PCI_ANY_ID, + .device = PCI_ANY_ID, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, +}, { /* all zeroes */ } }; -EXPORT_SYMBOL(yenta_operations); +MODULE_DEVICE_TABLE(pci, yenta_table); + + +static struct pci_driver yenta_cardbus_driver = { + .name = "yenta_cardbus", + .id_table = yenta_table, + .probe = yenta_probe, + .remove = __devexit_p(yenta_close), + .suspend = yenta_dev_suspend, + .resume = yenta_dev_resume, +}; + + +static int __init yenta_socket_init(void) +{ + return pci_register_driver (¥ta_cardbus_driver); +} + + +static void __exit yenta_socket_exit (void) +{ + pci_unregister_driver (¥ta_cardbus_driver); +} + + +module_init(yenta_socket_init); +module_exit(yenta_socket_exit); + MODULE_LICENSE("GPL"); diff -urN orig/drivers/pcmcia/yenta.h linux/drivers/pcmcia/yenta.h --- orig/drivers/pcmcia/yenta.h Thu Jan 6 22:07:55 2000 +++ linux/drivers/pcmcia/yenta.h Thu Jun 12 12:44:48 2003 @@ -2,7 +2,6 @@ #define __YENTA_H #include -#include "pci_socket.h" #define CB_SOCKET_EVENT 0x00 #define CB_CSTSEVENT 0x00000001 /* Card status event */ @@ -96,4 +95,26 @@ */ #define CB_MEM_PAGE(map) (0x40 + (map)) +struct yenta_socket { + struct pci_dev *dev; + int cb_irq, io_irq; + void *base; + void (*handler)(void *, unsigned int); + void *info; + socket_cap_t cap; + spinlock_t event_lock; + unsigned int events; + struct work_struct tq_task; + struct timer_list poll_timer; + + struct pcmcia_socket socket; + + /* A few words of private data for special stuff of overrides... */ + unsigned int private[8]; + + /* PCI saved state - 64 bytes */ + u32 saved_state[16]; +}; + + #endif diff -urN orig/drivers/pld/Makefile linux/drivers/pld/Makefile --- orig/drivers/pld/Makefile Thu Jan 1 01:00:00 1970 +++ linux/drivers/pld/Makefile Sun Apr 6 23:22:59 2003 @@ -0,0 +1,11 @@ +# +# Makefile for the kernel pld device drivers. +# + +obj-y := +obj-m := +obj-n := +obj- := + +obj-$(CONFIG_PLD) += pld_epxa.o +obj-$(CONFIG_PLD_HOTSWAP) += pld_hotswap.o diff -urN orig/drivers/pld/pld_epxa.c linux/drivers/pld/pld_epxa.c --- orig/drivers/pld/pld_epxa.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/pld/pld_epxa.c Sun Feb 17 11:48:27 2002 @@ -0,0 +1,375 @@ + +/* + * drivers/char/epxapld.c + * + * Copyright (C) 2001 Altera Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define PLD_CONF00_TYPE (volatile unsigned int *) +#define MODE_CTRL00_TYPE (volatile unsigned int *) +//#define DEBUG(x) x +#define DEBUG(x) + +#include +#include +#ifdef CONFIG_PLD_HOTSWAP +#include +#endif +#include + +/* + * Macros + */ + + +#define PLD_BASE (IO_ADDRESS(EXC_PLD_CONFIG00_BASE)) +#define CLOCK_DIV_RATIO ((1 + EXC_AHB2_CLK_FREQUENCY/32000000) & CONFIG_CONTROL_CLOCK_RATIO_MSK) +/* + * STRUCTURES + */ + + +struct pld_sbihdr{ + unsigned int fpos; + unsigned int temp; +}; + +static DECLARE_MUTEX(pld_sem); + + +static void lock_pld (void) +{ + /* Lock the pld i/f */ + unsigned int tmp; + + tmp = readl(CONFIG_CONTROL(PLD_BASE)); + tmp |= CONFIG_CONTROL_LK_MSK; + + writel(tmp,CONFIG_CONTROL(PLD_BASE)); +} + +static void unlock_excalibur_pld (void) +{ + /* Unlock the pld i/f */ + + if (readl(CONFIG_CONTROL(PLD_BASE)) & CONFIG_CONTROL_LK_MSK ){ + writel(CONFIG_UNLOCK_MAGIC, CONFIG_UNLOCK(PLD_BASE)); + while (readl(CONFIG_CONTROL(PLD_BASE)) & CONFIG_CONTROL_LK_MSK); + } +} + + +static int place_pld_into_configure_mode (void) +{ + unsigned int tmp; + + + if( readl(CONFIG_CONTROL(PLD_BASE)) & CONFIG_CONTROL_CO_MSK ){ + /* + * Already being configured!!! + */ + printk(KERN_WARNING "pld0: Device already being configured!\n"); + return -EBUSY; + } + + /* Set up the config clock */ + + writel(CLOCK_DIV_RATIO,CONFIG_CONTROL_CLOCK(PLD_BASE)); + while(readl(CONFIG_CONTROL_CLOCK(PLD_BASE)) + !=CLOCK_DIV_RATIO); + /* Tell the device we wish to configure it */ + tmp = readl(CONFIG_CONTROL(PLD_BASE)); + tmp |= CONFIG_CONTROL_CO_MSK; + writel(tmp,CONFIG_CONTROL(PLD_BASE)); + + + /* + * Wait for the busy bit to clear then check for errors. + */ + + while((tmp=readl(CONFIG_CONTROL(PLD_BASE))&CONFIG_CONTROL_B_MSK )); + + if ( tmp & CONFIG_CONTROL_E_MSK ){ + if ( tmp & CONFIG_CONTROL_ES_0_MSK ){ + /* Already being programmed via JTAG */ + printk(KERN_WARNING "pld0:JTAG configuration alreay in progress\n"); + return -EBUSY; + + } + if ( tmp & CONFIG_CONTROL_ES_1_MSK ){ + /* No config clock configured */ + printk(KERN_ERR "pld0:No config clock!\n"); + BUG(); + return -EBUSY; + } + if ( tmp & CONFIG_CONTROL_ES_2_MSK ){ + /* Already being programmed via External device */ + printk(KERN_WARNING "pld0:JTAG configuration alreay in progress\n"); + return -EBUSY; + } + } + + return 0; +} + + +static int write_pld_data_word(unsigned int config_data) +{ + unsigned int tmp; + + do{ + tmp = readl(CONFIG_CONTROL(PLD_BASE)); + } + while ( ( tmp & CONFIG_CONTROL_B_MSK ) && + !( tmp & CONFIG_CONTROL_E_MSK )); + + if ( tmp & CONFIG_CONTROL_E_MSK ){ + printk("pld0: Error writing pld data, CONFIG_CONTROL=%#x\n",tmp); + + return -EILSEQ; + } + + writel(config_data,CONFIG_CONTROL_DATA(PLD_BASE)); + return 0; + +} + + +static int wait_for_device_to_configure (void) +{ + int i=0x10000; + + while(readl(CONFIG_CONTROL(PLD_BASE)) & CONFIG_CONTROL_B_MSK); + + /* + * Wait for the config bit (CO) to go low, indicating that everything + * is Ok. If it doesn't, assume that is screwed up somehow and + * clear the CO bit to abort the configuration. + */ + + while(readl(CONFIG_CONTROL(PLD_BASE)) & CONFIG_CONTROL_B_MSK); + + while (i&&(readl(CONFIG_CONTROL(PLD_BASE)) & CONFIG_CONTROL_CO_MSK)){ + i--; + } + + if (!i){ + writel(0,CONFIG_CONTROL(PLD_BASE)); + printk(KERN_WARNING "pld0: Invalid PLD config data\n"); + return -EILSEQ; + } + + return 0; +} + + + +static int pld_open(struct inode* inode, struct file *filep) +{ + + struct pld_sbihdr* sbihdr; + + /* Check the device minor number */ + if(minor(inode->i_rdev)){ + DEBUG(printk("pld0: minor=%d",minor(inode->i_rdev));) + return -ENODEV; + } + + /* Create our private data and link it to the file structure */ + sbihdr=kmalloc(sizeof(struct pld_sbihdr),GFP_KERNEL); + + if(!sbihdr) + return -ENOMEM; + + filep->private_data=sbihdr; + + sbihdr->fpos=0; + sbihdr->temp=0; + return 0; +} + +static int pld_release(struct inode* inode, struct file *filep){ + int ret_code; + + kfree(filep->private_data); + ret_code=wait_for_device_to_configure(); + lock_pld(); + return ret_code; +} + +static ssize_t pld_write(struct file* filep, const char* data, size_t count, loff_t* ppos){ + + struct pld_sbihdr* sbihdr=filep->private_data; + int bytes_left=count; + int result; + DEBUG(int i=0); + + + /* Can't seek (pwrite) on pld. */ + if (ppos != &filep->f_pos) + return -ESPIPE; + + + /* Check access to the whole are in one go */ + if(!access_ok(VERIFY_READ,(const void*)data, count)){ + return -EFAULT; + } + + /* + * We now lock against writes. + */ + if (down_interruptible(&pld_sem)) + return -ERESTARTSYS; + + if(!sbihdr->fpos){ + /* + * unlock the pld and place in configure mode + */ + unlock_excalibur_pld(); + result=place_pld_into_configure_mode(); + if(result) + return result; + } + DEBUG(printk("data= %#x count=%#x 0ffset=%#x\n",*data, count, *ppos)); + + while(bytes_left){ + char tmp; + __get_user(tmp,data); + /* read our header ! */ + sbihdr->temp|=tmp << (8*(sbihdr->fpos&3)); + if((sbihdr->fpos&3)==3){ + if(write_pld_data_word(sbihdr->temp)) + { + DEBUG(printk("pos=%d\n",sbihdr->fpos);) + return -EILSEQ; + } + DEBUG(if(i<10){) + DEBUG(printk("fpos2 :%#x data=%#x\n",sbihdr->fpos,sbihdr->temp)); + DEBUG(i++); + DEBUG(}); + sbihdr->temp=0; + DEBUG(words_written++); + } + sbihdr->fpos++; + data++; + bytes_left--; + } + + up(&pld_sem); + return (count); +} + +int pld_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg) +{ + + switch(cmd){ + +#ifdef CONFIG_PLD_HOTSWAP + case PLD_IOC_ADD_PLD_DEV: + { + struct pldhs_dev_desc desc; + struct pldhs_dev_info info; + char *name; + void *data; + int result=0; + + result=copy_from_user(&desc,(const void*)arg,sizeof(struct pldhs_dev_desc)); + if(result) + return -EFAULT; + result=copy_from_user(&info,(const void*)desc.info,sizeof(struct pldhs_dev_info)); + if(result) + return -EFAULT; + name=kmalloc(info.nsize,GFP_KERNEL); + if(!name) + return -ENOMEM; + + result=copy_from_user(name,(const void*)desc.name,info.nsize); + if(result){ + result=-EFAULT; + goto ioctl_out; + } + + data=kmalloc(info.pssize,GFP_KERNEL); + if(!name){ + result=-ENOMEM; + goto ioctl_out; + } + + result=copy_from_user(data,(const void*)desc.data,info.pssize); + if(result){ + result=-EFAULT; + goto ioctl_out1; + } + result=pldhs_add_device(&info,name,data); + + ioctl_out1: + kfree(data); + ioctl_out: + kfree(name); + return result; + + } + + case PLD_IOC_REMOVE_PLD_DEVS: + pldhs_remove_devices(); + return 0; + + case PLD_IOC_SET_INT_MODE: + if(cmd==3){ + printk(KERN_INFO "Interrupt mode set to 3 (Six individual interrupts)\n"); + return 0; + }else{ + printk(KERN_INFO "There is no interrupt handler available for this mode (%d). You will need to add one\n to implement whatever scheme you require\n"); + return -EACCES; + } +#endif + default: + return -ENOTTY; + } +} + + +static struct file_operations pld_fops={ + write: pld_write, + ioctl: pld_ioctl, + open: pld_open, + release: pld_release +}; + +static int __init pld_init(void){ + int major; + major=register_chrdev(0,"pld",&pld_fops); + printk(KERN_INFO "Using PLD major num=%d\n",major); + if (major<0){ + return major; + } + return 0; +} + +__initcall(pld_init); diff -urN orig/drivers/pld/pld_hotswap.c linux/drivers/pld/pld_hotswap.c --- orig/drivers/pld/pld_hotswap.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/pld/pld_hotswap.c Tue Feb 19 15:53:46 2002 @@ -0,0 +1,154 @@ +/* + * linux/drivers/pld/pld_hotplug.c + * + * Pld driver for Altera EPXA Excalibur devices + * + * + * Copyright 2001 Altera Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: $ + * + */ + +/* + * pld_hotswap ops contains the basic operation required for adding + * and removing devices from the system. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static struct pld_hotswap_ops pldhs_driver_list={ + list: LIST_HEAD_INIT(pldhs_driver_list.list), + name: "", +}; + +static spinlock_t list_lock=SPIN_LOCK_UNLOCKED; + + + +static struct pld_hotswap_ops * pldhs_find_driver(char* name) +{ + struct pld_hotswap_ops * ptr; + struct list_head *list_pos; + + spin_lock(&list_lock); + list_for_each(list_pos,&pldhs_driver_list.list){ + ptr=(struct pld_hotswap_ops *)list_pos; + if(!strcmp(name, ptr->name)){ + spin_unlock(&list_lock); + return ptr; + + } + } + spin_unlock(&list_lock); + return 0; +} + + + +int pldhs_register_driver(struct pld_hotswap_ops *drv) +{ + + /* Check that the device is not already on the list + * if so, do nothing */ + if(pldhs_find_driver(drv->name)){ + return 0; + } + + printk(KERN_INFO "PLD: Registering hotswap driver %s\n",drv->name); + /* Add this at the start of the list */ + spin_lock(&list_lock); + list_add((struct list_head*)drv,&pldhs_driver_list.list); + spin_unlock(&list_lock); + + return 0; +} + +int pldhs_unregister_driver(char *name) +{ + struct pld_hotswap_ops *ptr; + + ptr=pldhs_find_driver(name); + if(!ptr){ + return -ENODEV; + } + + printk(KERN_INFO "PLD: Unregistering hotswap driver%s\n",name); + spin_lock(&list_lock); + list_del((struct list_head*)ptr); + spin_unlock(&list_lock); + + return 0; +} + +int pldhs_add_device(struct pldhs_dev_info* dev_info,char *drv_name, void* dev_ps_data) +{ + struct pld_hotswap_ops * ptr; + + ptr=pldhs_find_driver(drv_name); + + if(!ptr){ + /* try requesting this module*/ + request_module(drv_name); + /* is the driver there now? */ + ptr=pldhs_find_driver(drv_name); + if(!ptr){ + printk("pld hotswap: Failed to load a driver for %s\n",drv_name); + return -ENODEV; + } + } + + if(!ptr->add_device){ + printk(KERN_WARNING "pldhs: no add_device() function for driver %s\n",drv_name); + return 0; + } + + return ptr->add_device(dev_info,dev_ps_data); +} + +int pldhs_remove_devices(void) +{ + struct list_head *list_pos; + struct pld_hotswap_ops * ptr; + + + spin_lock(&list_lock); + list_for_each(list_pos,&pldhs_driver_list.list){ + ptr=(struct pld_hotswap_ops *)list_pos; + if(ptr->remove_devices) + ptr->remove_devices(); + + } + spin_unlock(&list_lock); + + return 0; +} + + + +EXPORT_SYMBOL(pldhs_register_driver); +EXPORT_SYMBOL(pldhs_unregister_driver); diff -urN orig/drivers/scsi/arm/eesox.c linux/drivers/scsi/arm/eesox.c --- orig/drivers/scsi/arm/eesox.c Tue May 27 10:05:03 2003 +++ linux/drivers/scsi/arm/eesox.c Fri May 16 01:24:07 2003 @@ -449,10 +449,11 @@ p += sprintf(p, "Term : o%s\n", info->control & EESOX_TERM_ENABLE ? "n" : "ff"); - pos += fas216_print_stats(&info->info, buffer + pos); p += fas216_print_stats(&info->info, p); p += fas216_print_devices(&info->info, p); + *start = buffer + offset; + pos = p - buffer - offset; if (pos > length) pos = length; diff -urN orig/drivers/serial/21285.c linux/drivers/serial/21285.c --- orig/drivers/serial/21285.c Tue May 27 10:05:12 2003 +++ linux/drivers/serial/21285.c Tue May 6 19:12:58 2003 @@ -318,13 +318,13 @@ port->ignore_status_mask |= RXSTAT_DUMMY_READ; quot -= 1; - +#if 0 *CSR_UARTCON = 0; *CSR_L_UBRLCR = quot & 0xff; *CSR_M_UBRLCR = (quot >> 8) & 0x0f; *CSR_H_UBRLCR = h_lcr; *CSR_UARTCON = 1; - +#endif spin_unlock_irqrestore(&port->lock, flags); } diff -urN orig/drivers/serial/8250.c linux/drivers/serial/8250.c --- orig/drivers/serial/8250.c Mon May 5 17:39:40 2003 +++ linux/drivers/serial/8250.c Wed Jun 11 20:49:49 2003 @@ -31,8 +31,9 @@ #include #include #include +#include #include -#include +#include #include #include @@ -122,12 +123,15 @@ struct uart_port port; struct timer_list timer; /* "no irq" timer */ struct list_head list; /* ports on this IRQ */ + unsigned int capabilities; /* port capabilities */ unsigned char acr; unsigned char ier; unsigned char rev; unsigned char lcr; + unsigned char mcr; unsigned char mcr_mask; /* mask of user bits */ unsigned char mcr_force; /* mask of forced bits */ + unsigned char efr; unsigned int lsr_break_flag; /* @@ -147,20 +151,20 @@ /* * Here we define the default xmit fifo size used for each type of UART. */ -static const struct serial_uart_config uart_config[PORT_MAX_8250+1] = { +static const struct serial8250_config uart_config[] = { { "unknown", 1, 0 }, { "8250", 1, 0 }, { "16450", 1, 0 }, { "16550", 1, 0 }, { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, { "Cirrus", 1, 0 }, - { "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH }, - { "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, - { "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO }, + { "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH | UART_EFRAFE }, + { "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH | UART_EFRAFE }, + { "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO | UART_MCRAFE }, { "Startech", 1, 0 }, { "16C950/954", 128, UART_CLEAR_FIFO | UART_USE_FIFO }, - { "ST16654", 64, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, - { "XR16850", 128, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, + { "ST16654", 64, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH | UART_EFRAFE }, + { "XR16850", 128, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH | UART_EFRAFE }, { "RSA", 2048, UART_CLEAR_FIFO | UART_USE_FIFO }, { "NS16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO | UART_NATSEMI } }; @@ -557,7 +561,7 @@ if (!up->port.iobase && !up->port.mapbase && !up->port.membase) return; - DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04x, 0x%08lx): ", + DEBUG_AUTOCONF("ttyS%d: autoconf (0x%04x, 0x%p): ", up->port.line, up->port.iobase, up->port.membase); /* @@ -683,6 +687,7 @@ serial_outp(up, UART_LCR, save_lcr); up->port.fifosize = uart_config[up->port.type].dfl_xmit_fifo_size; + up->capabilities = uart_config[up->port.type].flags; if (up->port.type == PORT_UNKNOWN) goto out; @@ -778,14 +783,25 @@ } } +static void transmit_chars(struct uart_8250_port *up); + static void serial8250_start_tx(struct uart_port *port, unsigned int tty_start) { struct uart_8250_port *up = (struct uart_8250_port *)port; + unsigned char lsr, iir; if (!(up->ier & UART_IER_THRI)) { up->ier |= UART_IER_THRI; serial_out(up, UART_IER, up->ier); + + if (up->capabilities & UART_BAD_TX_ENABLE) { + lsr = serial_in(up, UART_LSR); + iir = serial_in(up, UART_IIR); + if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) + transmit_chars(up); + } } + /* * We only do this from uart_start */ @@ -821,6 +837,13 @@ do { if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) { + /* + * FIXME: Deadlock can happen here if we're a + * low-latency port. We're holding the per-port + * spinlock, and we call flush_to_ldisc-> + * n_tty_receive_buf->n_tty_receive_char-> + * opost->uart_put_char. + */ tty->flip.work.func((void *)tty); if (tty->flip.count >= TTY_FLIPBUF_SIZE) return; // if TTY_DONT_FLIP is set @@ -1165,7 +1188,7 @@ if (mctrl & TIOCM_LOOP) mcr |= UART_MCR_LOOP; - mcr = (mcr & up->mcr_mask) | up->mcr_force; + mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr; serial_out(up, UART_MCR, mcr); } @@ -1188,8 +1211,14 @@ { struct uart_8250_port *up = (struct uart_8250_port *)port; unsigned long flags; + unsigned char lsr, iir; int retval; + up->capabilities = uart_config[up->port.type].flags; + up->mcr = 0; + up->efr = 0; + up->ier = 0; + if (up->port.type == PORT_16C950) { /* Wake up and initialize UART */ up->acr = 0; @@ -1215,7 +1244,7 @@ * Clear the FIFO buffers and disable them. * (they will be reeanbled in set_termios()) */ - if (uart_config[up->port.type].flags & UART_CLEAR_FIFO) { + if (up->capabilities & UART_CLEAR_FIFO) { serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); @@ -1276,6 +1305,22 @@ up->port.mctrl |= TIOCM_OUT2; serial8250_set_mctrl(&up->port, up->port.mctrl); + + /* + * Do a quick test to see if we receive an + * interrupt when we enable the TX irq. + */ + serial_outp(up, UART_IER, UART_IER_THRI); + lsr = serial_in(up, UART_LSR); + iir = serial_in(up, UART_IIR); + serial_outp(up, UART_IER, 0); + + if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) { + up->capabilities |= UART_BAD_TX_ENABLE; + printk("ttyS%d - enabling bad tx status workarounds\n", + port->line); + } + spin_unlock_irqrestore(&up->port.lock, flags); /* @@ -1428,7 +1473,7 @@ up->rev == 0x5201) quot ++; - if (uart_config[up->port.type].flags & UART_USE_FIFO) { + if (up->capabilities & UART_USE_FIFO) { if (baud < 2400) fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; #ifdef CONFIG_SERIAL_8250_RSA @@ -1489,10 +1534,29 @@ serial_out(up, UART_IER, up->ier); - if (uart_config[up->port.type].flags & UART_STARTECH) { + if (up->capabilities & UART_MCRAFE) { + /* + * TI16C750 hardware flow control + */ + up->mcr &= ~UART_MCR_AFE; + if (termios->c_cflag & CRTSCTS) + up->mcr |= UART_MCR_AFE; + } + if (up->capabilities & UART_EFRAFE) { + /* + * TI16C752/Startech hardware flow control + * FIXME: + * - TI16C752 requires control thresholds + * to be set for auto-RTS. + * - We only enable auto-CTS here. + * Note: ST16C654 does not allow MCR bit 1 + * to override RTS when UART_EFR_RTS is set. + */ + up->efr &= ~UART_EFR_CTS; + if (termios->c_cflag & CRTSCTS) + up->efr |= UART_EFR_CTS; serial_outp(up, UART_LCR, 0xBF); - serial_outp(up, UART_EFR, - termios->c_cflag & CRTSCTS ? UART_EFR_CTS :0); + serial_outp(up, UART_EFR, up->efr); } if (uart_config[up->port.type].flags & UART_NATSEMI) { @@ -1514,6 +1578,7 @@ } serial_outp(up, UART_FCR, fcr); /* set fcr */ } + serial8250_set_mctrl(&up->port, up->port.mctrl); spin_unlock_irqrestore(&up->port.lock, flags); } @@ -1524,7 +1589,7 @@ struct uart_8250_port *up = (struct uart_8250_port *)port; if (state) { /* sleep */ - if (uart_config[up->port.type].flags & UART_STARTECH) { + if (up->capabilities & UART_STARTECH) { /* Arrange to enter sleep mode */ serial_outp(up, UART_LCR, 0xBF); serial_outp(up, UART_EFR, UART_EFR_ECB); @@ -1543,7 +1608,7 @@ up->pm(port, state, oldstate); } else { /* wake */ - if (uart_config[up->port.type].flags & UART_STARTECH) { + if (up->capabilities & UART_STARTECH) { /* Wake up UART */ serial_outp(up, UART_LCR, 0xBF); serial_outp(up, UART_EFR, UART_EFR_ECB); @@ -1839,6 +1904,10 @@ up->port.iotype = old_serial_port[i].io_type; up->port.regshift = old_serial_port[i].iomem_reg_shift; up->port.ops = &serial8250_pops; + + if (up->port.iotype == SERIAL_IO_MEM && up->port.mapbase) + up->port.flags |= UPF_IOREMAP; + if (share_irqs) up->port.flags |= UPF_SHARE_IRQ; } @@ -2118,6 +2187,49 @@ uart_resume_port(&serial8250_reg, &serial8250_ports[line].port, level); } +static int serial8250_suspend(struct device *dev, u32 state, u32 level) +{ + int i; + + for (i = 0; i < UART_NR; i++) { + if (serial8250_ports[i].port.type != PORT_UNKNOWN) + uart_suspend_port(&serial8250_reg, &serial8250_ports[i].port, level); + } + + return 0; +} + +static int serial8250_resume(struct device *dev, u32 level) +{ + int i; + + for (i = 0; i < UART_NR; i++) { + if (serial8250_ports[i].port.type != PORT_UNKNOWN) + uart_resume_port(&serial8250_reg, &serial8250_ports[i].port, level); + } + + return 0; +} + +static struct device_driver serial8250_isa_driver = { + .name = "serial8250", + .bus = &platform_bus_type, + + .suspend = serial8250_suspend, + .resume = serial8250_resume, +}; + +/* + * This "device" covers _all_ ISA 8250-compatible serial devices. + */ +static struct platform_device serial8250_isa_devs = { + .name = "serial8250", + .id = 0, + .dev = { + .driver = &serial8250_isa_driver, + }, +}; + static int __init serial8250_init(void) { int ret, i; @@ -2125,6 +2237,9 @@ printk(KERN_INFO "Serial: 8250/16550 driver $Revision: 1.90 $ " "IRQ sharing %sabled\n", share_irqs ? "en" : "dis"); + driver_register(&serial8250_isa_driver); + platform_device_register(&serial8250_isa_devs); + for (i = 0; i < NR_IRQS; i++) spin_lock_init(&irq_lists[i].lock); @@ -2143,6 +2258,9 @@ uart_remove_one_port(&serial8250_reg, &serial8250_ports[i].port); uart_unregister_driver(&serial8250_reg); + + platform_device_unregister(&serial8250_isa_devs); + driver_unregister(&serial8250_isa_driver); } module_init(serial8250_init); diff -urN orig/drivers/serial/8250.h linux/drivers/serial/8250.h --- orig/drivers/serial/8250.h Mon Mar 17 23:16:34 2003 +++ linux/drivers/serial/8250.h Tue Mar 11 21:37:28 2003 @@ -12,7 +12,7 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * - * $Id: 8250.h,v 1.8 2002/07/21 21:32:30 rmk Exp $ + * $Id: 8250.h,v 1.9 2002/07/27 12:42:16 rmk Exp $ */ #include @@ -42,6 +42,21 @@ unsigned short iomem_reg_shift; }; +struct serial8250_config { + const char *name; + unsigned int dfl_xmit_fifo_size; + unsigned int flags; +}; + +#define UART_CLEAR_FIFO 0x01 +#define UART_USE_FIFO 0x02 +#define UART_STARTECH 0x04 +#define UART_NATSEMI 0x08 +#define UART_MCRAFE 0x10 /* TI16C750-style auto-flow */ +#define UART_EFRAFE 0x20 /* TI16C752/startech auto-flow */ + +#define UART_BAD_TX_ENABLE 0x80000000 + #undef SERIAL_DEBUG_PCI #if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486)) @@ -64,3 +79,15 @@ #else #define SERIAL8250_SHARE_IRQS 0 #endif + +#if defined(__alpha__) && !defined(CONFIG_PCI) +/* + * Digital did something really horribly wrong with the OUT1 and OUT2 + * lines on at least some ALPHA's. The failure mode is that if either + * is cleared, the machine locks up with endless interrupts. + */ +#define ALPHA_KLUDGE_MCR (UART_MCR_OUT2 | UART_MCR_OUT1) +#else +#define ALPHA_KLUDGE_MCR 0 +#endif + diff -urN orig/drivers/serial/8250_cs.c linux/drivers/serial/8250_cs.c --- orig/drivers/serial/8250_cs.c Mon May 5 17:39:41 2003 +++ linux/drivers/serial/8250_cs.c Wed Jun 4 14:06:37 2003 @@ -43,7 +43,6 @@ #include #include #include -#include #include #include @@ -100,7 +99,7 @@ }; #define MULTI_COUNT (sizeof(multi_id)/sizeof(struct multi_id)) -typedef struct serial_info { +struct serial_info { dev_link_t link; int ndev; int multi; @@ -108,8 +107,7 @@ int manfid; dev_node_t node[4]; int line[4]; - struct work_struct remove; -} serial_info_t; +}; static void serial_config(dev_link_t * link); static int serial_event(event_t event, int priority, @@ -129,13 +127,12 @@ ======================================================================*/ -/* - * This always runs in process context. - */ -static void do_serial_release(void *arg) +static void serial_remove(dev_link_t *link) { - struct serial_info *info = arg; - int i; + struct serial_info *info = link->priv; + int i, ret; + + link->state &= ~DEV_PRESENT; DEBUG(0, "serial_release(0x%p)\n", &info->link); @@ -149,34 +146,21 @@ info->link.dev = NULL; if (!info->slave) { - CardServices(ReleaseConfiguration, info->link.handle); - CardServices(ReleaseIO, info->link.handle, &info->link.io); - CardServices(ReleaseIRQ, info->link.handle, &info->link.irq); + ret = pcmcia_release_configuration(info->link.handle); + if (ret) + cs_error(info->link.handle, ReleaseConfiguration, ret); + ret = pcmcia_release_io(info->link.handle, &info->link.io); + if (ret) + cs_error(info->link.handle, ReleaseIO, ret); + ret = pcmcia_release_irq(info->link.handle, &info->link.irq); + if (ret) + cs_error(info->link.handle, ReleaseIRQ, ret); } info->link.state &= ~DEV_CONFIG; } } -/* - * This may be called from IRQ context. - */ -static void serial_remove(dev_link_t *link) -{ - struct serial_info *info = link->priv; - - link->state &= ~DEV_PRESENT; - - /* - * FIXME: Since the card has probably been removed, - * we should call into the serial layer and hang up - * the ports on the card immediately. - */ - - if (link->state & DEV_CONFIG) - schedule_work(&info->remove); -} - /*====================================================================== serial_attach() creates an "instance" of the driver, allocating @@ -187,7 +171,7 @@ static dev_link_t *serial_attach(void) { - serial_info_t *info; + struct serial_info *info; client_reg_t client_reg; dev_link_t *link; int i, ret; @@ -202,8 +186,6 @@ link = &info->link; link->priv = info; - INIT_WORK(&info->remove, do_serial_release, info); - link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; link->io.NumPorts1 = 8; link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; @@ -233,7 +215,7 @@ client_reg.event_handler = &serial_event; client_reg.Version = 0x0210; client_reg.event_callback_args.client_data = link; - ret = CardServices(RegisterClient, &link->handle, &client_reg); + ret = pcmcia_register_client(&link->handle, &client_reg); if (ret != CS_SUCCESS) { cs_error(link->handle, RegisterClient, ret); serial_detach(link); @@ -254,7 +236,7 @@ static void serial_detach(dev_link_t * link) { - serial_info_t *info = link->priv; + struct serial_info *info = link->priv; dev_link_t **linkp; int ret; @@ -275,10 +257,10 @@ /* * Ensure that the ports have been released. */ - do_serial_release(info); + serial_remove(link); if (link->handle) { - ret = CardServices(DeregisterClient, link->handle); + ret = pcmcia_deregister_client(link->handle); if (ret != CS_SUCCESS) cs_error(link->handle, DeregisterClient, ret); } @@ -290,7 +272,7 @@ /*====================================================================*/ -static int setup_serial(serial_info_t * info, ioaddr_t port, int irq) +static int setup_serial(struct serial_info * info, ioaddr_t port, int irq) { struct serial_struct serial; int line; @@ -326,10 +308,10 @@ i = CardServices(fn, handle, tuple); if (i != CS_SUCCESS) return CS_NO_MORE_ITEMS; - i = CardServices(GetTupleData, handle, tuple); + i = pcmcia_get_tuple_data(handle, tuple); if (i != CS_SUCCESS) return i; - return CardServices(ParseTuple, handle, tuple, parse); + return pcmcia_parse_tuple(handle, tuple, parse); } #define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c) @@ -341,7 +323,7 @@ { static ioaddr_t base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 }; client_handle_t handle = link->handle; - serial_info_t *info = link->priv; + struct serial_info *info = link->priv; tuple_t tuple; u_char buf[256]; cisparse_t parse; @@ -350,7 +332,7 @@ int i, j, try; /* If the card is already configured, look up the port and irq */ - i = CardServices(GetConfigurationInfo, handle, &config); + i = pcmcia_get_configuration_info(handle, &config); if ((i == CS_SUCCESS) && (config.Attributes & CONF_VALID_CLIENT)) { ioaddr_t port = 0; if ((config.BasePort2 != 0) && (config.NumPorts2 == 8)) { @@ -387,9 +369,7 @@ link->io.BasePort1 = cf->io.win[0].base; link->io.IOAddrLines = (try == 0) ? 16 : cf->io.flags & CISTPL_IO_LINES_MASK; - i = - CardServices(RequestIO, link->handle, - &link->io); + i = pcmcia_request_io(link->handle, &link->io); if (i == CS_SUCCESS) goto found_port; } @@ -409,8 +389,7 @@ for (j = 0; j < 5; j++) { link->io.BasePort1 = base[j]; link->io.IOAddrLines = base[j] ? 16 : 3; - i = CardServices(RequestIO, link->handle, - &link->io); + i = pcmcia_request_io(link->handle, &link->io); if (i == CS_SUCCESS) goto found_port; } @@ -426,14 +405,14 @@ return -1; } - i = CardServices(RequestIRQ, link->handle, &link->irq); + i = pcmcia_request_irq(link->handle, &link->irq); if (i != CS_SUCCESS) { cs_error(link->handle, RequestIRQ, i); link->irq.AssignedIRQ = 0; } if (info->multi && (info->manfid == MANFID_3COM)) link->conf.ConfigIndex &= ~(0x08); - i = CardServices(RequestConfiguration, link->handle, &link->conf); + i = pcmcia_request_configuration(link->handle, &link->conf); if (i != CS_SUCCESS) { cs_error(link->handle, RequestConfiguration, i); return -1; @@ -445,7 +424,7 @@ static int multi_config(dev_link_t * link) { client_handle_t handle = link->handle; - serial_info_t *info = link->priv; + struct serial_info *info = link->priv; tuple_t tuple; u_char buf[256]; cisparse_t parse; @@ -470,7 +449,7 @@ link->io.BasePort1 = cf->io.win[0].base; link->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK; - i = CardServices(RequestIO, link->handle, &link->io); + i = pcmcia_request_io(link->handle, &link->io); base2 = link->io.BasePort1 + 8; if (i == CS_SUCCESS) break; @@ -490,9 +469,7 @@ link->io.BasePort2 = cf->io.win[1].base; link->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK; - i = - CardServices(RequestIO, link->handle, - &link->io); + i = pcmcia_request_io(link->handle, &link->io); base2 = link->io.BasePort2; if (i == CS_SUCCESS) break; @@ -506,7 +483,7 @@ return -1; } - i = CardServices(RequestIRQ, link->handle, &link->irq); + i = pcmcia_request_irq(link->handle, &link->irq); if (i != CS_SUCCESS) { printk(KERN_NOTICE "serial_cs: no usable port range found, giving up\n"); @@ -518,7 +495,7 @@ link->conf.Present |= PRESENT_EXT_STATUS; link->conf.ExtStatus = ESR_REQ_ATTN_ENA; } - i = CardServices(RequestConfiguration, link->handle, &link->conf); + i = pcmcia_request_configuration(link->handle, &link->conf); if (i != CS_SUCCESS) { cs_error(link->handle, RequestConfiguration, i); return -1; @@ -542,13 +519,10 @@ ======================================================================*/ -#define CS_CHECK(fn, args...) \ -while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed - void serial_config(dev_link_t * link) { client_handle_t handle = link->handle; - serial_info_t *info = link->priv; + struct serial_info *info = link->priv; tuple_t tuple; u_short buf[128]; cisparse_t parse; @@ -618,10 +592,18 @@ if (info->manfid == MANFID_IBM) { conf_reg_t reg = { 0, CS_READ, 0x800, 0 }; - CS_CHECK(AccessConfigurationRegister, link->handle, ®); + last_ret = pcmcia_access_configuration_register(link->handle, ®); + if (last_ret) { + last_fn = AccessConfigurationRegister; + goto cs_failed; + } reg.Action = CS_WRITE; reg.Value = reg.Value | 1; - CS_CHECK(AccessConfigurationRegister, link->handle, ®); + last_ret = pcmcia_access_configuration_register(link->handle, ®); + if (last_ret) { + last_fn = AccessConfigurationRegister; + goto cs_failed; + } } link->dev = &info->node[0]; @@ -631,7 +613,7 @@ cs_failed: cs_error(link->handle, last_fn, last_ret); failed: - do_serial_release(info); + serial_remove(link); } /*====================================================================== @@ -647,7 +629,7 @@ serial_event(event_t event, int priority, event_callback_args_t * args) { dev_link_t *link = args->client_data; - serial_info_t *info = link->priv; + struct serial_info *info = link->priv; DEBUG(1, "serial_event(0x%06x)\n", event); @@ -666,7 +648,7 @@ /* Fall through... */ case CS_EVENT_RESET_PHYSICAL: if ((link->state & DEV_CONFIG) && !info->slave) - CardServices(ReleaseConfiguration, link->handle); + pcmcia_release_configuration(link->handle); break; case CS_EVENT_PM_RESUME: @@ -674,8 +656,7 @@ /* Fall through... */ case CS_EVENT_CARD_RESET: if (DEV_OK(link) && !info->slave) - CardServices(RequestConfiguration, link->handle, - &link->conf); + pcmcia_request_configuration(link->handle, &link->conf); break; } return 0; diff -urN orig/drivers/serial/8250_pci.c linux/drivers/serial/8250_pci.c --- orig/drivers/serial/8250_pci.c Mon May 5 17:39:41 2003 +++ linux/drivers/serial/8250_pci.c Mon May 5 17:43:40 2003 @@ -1397,6 +1397,15 @@ } /* + * If there is exactly one port region of 8 bytes, use that. + */ + if (num_port == 1 && pci_resource_len(dev, first_port) == 8) { + board->flags = first_port; + board->num_ports = 1; + return 0; + } + + /* * If there is 1 or 0 iomem regions, and exactly one port, * use it. We guess the number of ports based on the IO * region size. @@ -1971,17 +1980,14 @@ { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_X3201_MDM, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_1_115200 }, - /* - * Xircom RBM56G cardbus modem - Dirk Arnold (temp entry) - */ - { PCI_VENDOR_ID_XIRCOM, PCI_DEVICE_ID_XIRCOM_RBM56G, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - pbn_b0_1_115200 }, /* * Untested PCI modems, sent in from various folks... */ + /* USR 56k Voice Internal PCI model 5610 Device ID 0x1008 removed */ + /* Xircom RBM56G cardbus modem - Dirk Arnold (should be guessed) */ + /* * Elsa Model 56K PCI Modem, from Andreas Rath */ diff -urN orig/drivers/serial/Kconfig linux/drivers/serial/Kconfig --- orig/drivers/serial/Kconfig Mon May 5 17:39:41 2003 +++ linux/drivers/serial/Kconfig Thu Jun 12 14:53:56 2003 @@ -174,7 +174,7 @@ config SERIAL_AMBA tristate "ARM AMBA serial port support" - depends on ARM && ARCH_INTEGRATOR + depends on ARM_AMBA help This selects the ARM(R) AMBA(R) PrimeCell UART. If you have an Integrator platform, say Y or M here. @@ -198,7 +198,7 @@ config SERIAL_INTEGRATOR bool - depends on SERIAL_AMBA=y + depends on SERIAL_AMBA && ARCH_INTEGRATOR default y config SERIAL_CLPS711X @@ -224,6 +224,17 @@ help ::: To be written ::: +config SERIAL_DZ + bool "DECstation DZ serial driver" + help + ::: To be written / taken from mips / alpha CONFIG_DZ ::: + +config SERIAL_DZ_CONSOLE + bool "Support console on DECstation DZ serial driver" + depends on SERIAL_DZ=y + help + ::: To be written / taken from mips / alpha CONFIG_SERIAL_DEC_CONSOLE ::: + config SERIAL_21285 tristate "DC21285 serial port support" depends on ARM && FOOTBRIDGE @@ -385,12 +396,12 @@ config SERIAL_CORE tristate - default m if SERIAL_AMBA!=y && SERIAL_CLPS711X!=y && SERIAL_21285!=y && !SERIAL_SA1100 && !SERIAL_ANAKIN && !SERIAL_UART00 && SERIAL_8250!=y && SERIAL_MUX!=y && !SERIAL_ROCKETPORT && !SERIAL_SUNCORE && !V850E_NB85E_UART && (SERIAL_AMBA=m || SERIAL_CLPS711X=m || SERIAL_21285=m || SERIAL_8250=m || SERIAL_MUX=m || SERIAL98=m) - default y if SERIAL_AMBA=y || SERIAL_CLPS711X=y || SERIAL_21285=y || SERIAL_SA1100 || SERIAL_ANAKIN || SERIAL_UART00 || SERIAL_8250=y || SERIAL_MUX=y || SERIAL_ROCKETPORT || SERIAL_SUNCORE || V850E_NB85E_UART || SERIAL98=y + default m if SERIAL_AMBA!=y && SERIAL_CLPS711X!=y && SERIAL_21285!=y && !SERIAL_SA1100 && !SERIAL_ANAKIN && !SERIAL_UART00 && SERIAL_8250!=y && SERIAL_MUX!=y && !SERIAL_ROCKETPORT && !SERIAL_SUNCORE && !V850E_NB85E_UART && !SERIAL_DZ && (SERIAL_AMBA=m || SERIAL_CLPS711X=m || SERIAL_21285=m || SERIAL_8250=m || SERIAL_MUX=m || SERIAL98=m) + default y if SERIAL_AMBA=y || SERIAL_CLPS711X=y || SERIAL_21285=y || SERIAL_SA1100 || SERIAL_ANAKIN || SERIAL_UART00 || SERIAL_8250=y || SERIAL_MUX=y || SERIAL_ROCKETPORT || SERIAL_SUNCORE || V850E_NB85E_UART || SERIAL_DZ || SERIAL98=y config SERIAL_CORE_CONSOLE bool - depends on SERIAL_AMBA_CONSOLE || SERIAL_CLPS711X_CONSOLE || SERIAL_21285_CONSOLE || SERIAL_SA1100_CONSOLE || SERIAL_ANAKIN_CONSOLE || SERIAL_UART00_CONSOLE || SERIAL_8250_CONSOLE || SERIAL_MUX_CONSOLE || SERIAL_SUNCORE || V850E_NB85E_UART_CONSOLE || SERIAL98_CONSOLE + depends on SERIAL_AMBA_CONSOLE || SERIAL_CLPS711X_CONSOLE || SERIAL_21285_CONSOLE || SERIAL_SA1100_CONSOLE || SERIAL_ANAKIN_CONSOLE || SERIAL_UART00_CONSOLE || SERIAL_8250_CONSOLE || SERIAL_MUX_CONSOLE || SERIAL_SUNCORE || V850E_NB85E_UART_CONSOLE || SERIAL_DZ_CONSOLE || SERIAL98_CONSOLE default y config SERIAL_68328 diff -urN orig/drivers/serial/Makefile linux/drivers/serial/Makefile --- orig/drivers/serial/Makefile Sat Apr 12 14:46:58 2003 +++ linux/drivers/serial/Makefile Thu Jun 12 15:34:26 2003 @@ -28,3 +28,5 @@ obj-$(CONFIG_SERIAL_COLDFIRE) += mcfserial.o obj-$(CONFIG_V850E_NB85E_UART) += nb85e_uart.o obj-$(CONFIG_SERIAL98) += serial98.o +obj-$(CONFIG_SERIAL_DZ) += dz.o + diff -urN orig/drivers/serial/amba.c linux/drivers/serial/amba.c --- orig/drivers/serial/amba.c Tue May 27 10:05:12 2003 +++ linux/drivers/serial/amba.c Thu Jun 12 14:54:40 2003 @@ -38,10 +38,12 @@ #include #include #include +#include #include #include #include +#include #if defined(CONFIG_SERIAL_AMBA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) #define SUPPORT_SYSRQ @@ -49,7 +51,7 @@ #include -#include +#include #define UART_NR 2 @@ -62,25 +64,25 @@ /* * Access macros for the AMBA UARTs */ -#define UART_GET_INT_STATUS(p) readb((p)->membase + AMBA_UARTIIR) -#define UART_PUT_ICR(p, c) writel((c), (p)->membase + AMBA_UARTICR) -#define UART_GET_FR(p) readb((p)->membase + AMBA_UARTFR) -#define UART_GET_CHAR(p) readb((p)->membase + AMBA_UARTDR) -#define UART_PUT_CHAR(p, c) writel((c), (p)->membase + AMBA_UARTDR) -#define UART_GET_RSR(p) readb((p)->membase + AMBA_UARTRSR) -#define UART_GET_CR(p) readb((p)->membase + AMBA_UARTCR) -#define UART_PUT_CR(p,c) writel((c), (p)->membase + AMBA_UARTCR) -#define UART_GET_LCRL(p) readb((p)->membase + AMBA_UARTLCR_L) -#define UART_PUT_LCRL(p,c) writel((c), (p)->membase + AMBA_UARTLCR_L) -#define UART_GET_LCRM(p) readb((p)->membase + AMBA_UARTLCR_M) -#define UART_PUT_LCRM(p,c) writel((c), (p)->membase + AMBA_UARTLCR_M) -#define UART_GET_LCRH(p) readb((p)->membase + AMBA_UARTLCR_H) -#define UART_PUT_LCRH(p,c) writel((c), (p)->membase + AMBA_UARTLCR_H) -#define UART_RX_DATA(s) (((s) & AMBA_UARTFR_RXFE) == 0) -#define UART_TX_READY(s) (((s) & AMBA_UARTFR_TXFF) == 0) -#define UART_TX_EMPTY(p) ((UART_GET_FR(p) & AMBA_UARTFR_TMSK) == 0) +#define UART_GET_INT_STATUS(p) readb((p)->membase + UART010_IIR) +#define UART_PUT_ICR(p, c) writel((c), (p)->membase + UART010_ICR) +#define UART_GET_FR(p) readb((p)->membase + UART01x_FR) +#define UART_GET_CHAR(p) readb((p)->membase + UART01x_DR) +#define UART_PUT_CHAR(p, c) writel((c), (p)->membase + UART01x_DR) +#define UART_GET_RSR(p) readb((p)->membase + UART01x_RSR) +#define UART_GET_CR(p) readb((p)->membase + UART010_CR) +#define UART_PUT_CR(p,c) writel((c), (p)->membase + UART010_CR) +#define UART_GET_LCRL(p) readb((p)->membase + UART010_LCRL) +#define UART_PUT_LCRL(p,c) writel((c), (p)->membase + UART010_LCRL) +#define UART_GET_LCRM(p) readb((p)->membase + UART010_LCRM) +#define UART_PUT_LCRM(p,c) writel((c), (p)->membase + UART010_LCRM) +#define UART_GET_LCRH(p) readb((p)->membase + UART010_LCRH) +#define UART_PUT_LCRH(p,c) writel((c), (p)->membase + UART010_LCRH) +#define UART_RX_DATA(s) (((s) & UART01x_FR_RXFE) == 0) +#define UART_TX_READY(s) (((s) & UART01x_FR_TXFF) == 0) +#define UART_TX_EMPTY(p) ((UART_GET_FR(p) & UART01x_FR_TMSK) == 0) -#define UART_DUMMY_RSR_RX 256 +#define UART_DUMMY_RSR_RX /*256*/0 #define UART_PORT_SIZE 64 /* @@ -103,47 +105,47 @@ unsigned int old_status; }; -static void ambauart_stop_tx(struct uart_port *port, unsigned int tty_stop) +static void pl010_stop_tx(struct uart_port *port, unsigned int tty_stop) { unsigned int cr; cr = UART_GET_CR(port); - cr &= ~AMBA_UARTCR_TIE; + cr &= ~UART010_CR_TIE; UART_PUT_CR(port, cr); } -static void ambauart_start_tx(struct uart_port *port, unsigned int tty_start) +static void pl010_start_tx(struct uart_port *port, unsigned int tty_start) { unsigned int cr; cr = UART_GET_CR(port); - cr |= AMBA_UARTCR_TIE; + cr |= UART010_CR_TIE; UART_PUT_CR(port, cr); } -static void ambauart_stop_rx(struct uart_port *port) +static void pl010_stop_rx(struct uart_port *port) { unsigned int cr; cr = UART_GET_CR(port); - cr &= ~(AMBA_UARTCR_RIE | AMBA_UARTCR_RTIE); + cr &= ~(UART010_CR_RIE | UART010_CR_RTIE); UART_PUT_CR(port, cr); } -static void ambauart_enable_ms(struct uart_port *port) +static void pl010_enable_ms(struct uart_port *port) { unsigned int cr; cr = UART_GET_CR(port); - cr |= AMBA_UARTCR_MSIE; + cr |= UART010_CR_MSIE; UART_PUT_CR(port, cr); } static void #ifdef SUPPORT_SYSRQ -ambauart_rx_chars(struct uart_port *port, struct pt_regs *regs) +pl010_rx_chars(struct uart_port *port, struct pt_regs *regs) #else -ambauart_rx_chars(struct uart_port *port) +pl010_rx_chars(struct uart_port *port) #endif { struct tty_struct *tty = port->info->tty; @@ -170,26 +172,26 @@ * out of the main execution path */ rsr = UART_GET_RSR(port) | UART_DUMMY_RSR_RX; - if (rsr & AMBA_UARTRSR_ANY) { - if (rsr & AMBA_UARTRSR_BE) { - rsr &= ~(AMBA_UARTRSR_FE | AMBA_UARTRSR_PE); + if (rsr & UART01x_RSR_ANY) { + if (rsr & UART01x_RSR_BE) { + rsr &= ~(UART01x_RSR_FE | UART01x_RSR_PE); port->icount.brk++; if (uart_handle_break(port)) goto ignore_char; - } else if (rsr & AMBA_UARTRSR_PE) + } else if (rsr & UART01x_RSR_PE) port->icount.parity++; - else if (rsr & AMBA_UARTRSR_FE) + else if (rsr & UART01x_RSR_FE) port->icount.frame++; - if (rsr & AMBA_UARTRSR_OE) + if (rsr & UART01x_RSR_OE) port->icount.overrun++; rsr &= port->read_status_mask; - if (rsr & AMBA_UARTRSR_BE) + if (rsr & UART01x_RSR_BE) *tty->flip.flag_buf_ptr = TTY_BREAK; - else if (rsr & AMBA_UARTRSR_PE) + else if (rsr & UART01x_RSR_PE) *tty->flip.flag_buf_ptr = TTY_PARITY; - else if (rsr & AMBA_UARTRSR_FE) + else if (rsr & UART01x_RSR_FE) *tty->flip.flag_buf_ptr = TTY_FRAME; } @@ -201,7 +203,7 @@ tty->flip.char_buf_ptr++; tty->flip.count++; } - if ((rsr & AMBA_UARTRSR_OE) && + if ((rsr & UART01x_RSR_OE) && tty->flip.count < TTY_FLIPBUF_SIZE) { /* * Overrun is special, since it's reported @@ -219,7 +221,7 @@ return; } -static void ambauart_tx_chars(struct uart_port *port) +static void pl010_tx_chars(struct uart_port *port) { struct circ_buf *xmit = &port->info->xmit; int count; @@ -231,7 +233,7 @@ return; } if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { - ambauart_stop_tx(port, 0); + pl010_stop_tx(port, 0); return; } @@ -248,17 +250,17 @@ uart_write_wakeup(port); if (uart_circ_empty(xmit)) - ambauart_stop_tx(port, 0); + pl010_stop_tx(port, 0); } -static void ambauart_modem_status(struct uart_port *port) +static void pl010_modem_status(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; unsigned int status, delta; UART_PUT_ICR(&uap->port, 0); - status = UART_GET_FR(&uap->port) & AMBA_UARTFR_MODEM_ANY; + status = UART_GET_FR(&uap->port) & UART01x_FR_MODEM_ANY; delta = status ^ uap->old_status; uap->old_status = status; @@ -266,68 +268,68 @@ if (!delta) return; - if (delta & AMBA_UARTFR_DCD) - uart_handle_dcd_change(&uap->port, status & AMBA_UARTFR_DCD); + if (delta & UART01x_FR_DCD) + uart_handle_dcd_change(&uap->port, status & UART01x_FR_DCD); - if (delta & AMBA_UARTFR_DSR) + if (delta & UART01x_FR_DSR) uap->port.icount.dsr++; - if (delta & AMBA_UARTFR_CTS) - uart_handle_cts_change(&uap->port, status & AMBA_UARTFR_CTS); + if (delta & UART01x_FR_CTS) + uart_handle_cts_change(&uap->port, status & UART01x_FR_CTS); wake_up_interruptible(&uap->port.info->delta_msr_wait); } -static irqreturn_t ambauart_int(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t pl010_int(int irq, void *dev_id, struct pt_regs *regs) { struct uart_port *port = dev_id; unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT; status = UART_GET_INT_STATUS(port); do { - if (status & (AMBA_UARTIIR_RTIS | AMBA_UARTIIR_RIS)) + if (status & (UART010_IIR_RTIS | UART010_IIR_RIS)) #ifdef SUPPORT_SYSRQ - ambauart_rx_chars(port, regs); + pl010_rx_chars(port, regs); #else - ambauart_rx_chars(port); + pl010_rx_chars(port); #endif - if (status & AMBA_UARTIIR_MIS) - ambauart_modem_status(port); - if (status & AMBA_UARTIIR_TIS) - ambauart_tx_chars(port); + if (status & UART010_IIR_MIS) + pl010_modem_status(port); + if (status & UART010_IIR_TIS) + pl010_tx_chars(port); if (pass_counter-- == 0) break; status = UART_GET_INT_STATUS(port); - } while (status & (AMBA_UARTIIR_RTIS | AMBA_UARTIIR_RIS | - AMBA_UARTIIR_TIS)); + } while (status & (UART010_IIR_RTIS | UART010_IIR_RIS | + UART010_IIR_TIS)); return IRQ_HANDLED; } -static unsigned int ambauart_tx_empty(struct uart_port *port) +static unsigned int pl010_tx_empty(struct uart_port *port) { - return UART_GET_FR(port) & AMBA_UARTFR_BUSY ? 0 : TIOCSER_TEMT; + return UART_GET_FR(port) & UART01x_FR_BUSY ? 0 : TIOCSER_TEMT; } -static unsigned int ambauart_get_mctrl(struct uart_port *port) +static unsigned int pl010_get_mctrl(struct uart_port *port) { unsigned int result = 0; unsigned int status; status = UART_GET_FR(port); - if (status & AMBA_UARTFR_DCD) + if (status & UART01x_FR_DCD) result |= TIOCM_CAR; - if (status & AMBA_UARTFR_DSR) + if (status & UART01x_FR_DSR) result |= TIOCM_DSR; - if (status & AMBA_UARTFR_CTS) + if (status & UART01x_FR_CTS) result |= TIOCM_CTS; return result; } -static void ambauart_set_mctrl(struct uart_port *port, unsigned int mctrl) +static void pl010_set_mctrl(struct uart_port *port, unsigned int mctrl) { struct uart_amba_port *uap = (struct uart_amba_port *)port; unsigned int ctrls = 0, ctrlc = 0; @@ -346,7 +348,7 @@ __raw_writel(ctrlc, SC_CTRLC); } -static void ambauart_break_ctl(struct uart_port *port, int break_state) +static void pl010_break_ctl(struct uart_port *port, int break_state) { unsigned long flags; unsigned int lcr_h; @@ -354,14 +356,14 @@ spin_lock_irqsave(&port->lock, flags); lcr_h = UART_GET_LCRH(port); if (break_state == -1) - lcr_h |= AMBA_UARTLCR_H_BRK; + lcr_h |= UART01x_LCRH_BRK; else - lcr_h &= ~AMBA_UARTLCR_H_BRK; + lcr_h &= ~UART01x_LCRH_BRK; UART_PUT_LCRH(port, lcr_h); spin_unlock_irqrestore(&port->lock, flags); } -static int ambauart_startup(struct uart_port *port) +static int pl010_startup(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; int retval; @@ -369,25 +371,25 @@ /* * Allocate the IRQ */ - retval = request_irq(port->irq, ambauart_int, 0, "amba", port); + retval = request_irq(port->irq, pl010_int, 0, "amba", port); if (retval) return retval; /* * initialise the old status of the modem signals */ - uap->old_status = UART_GET_FR(port) & AMBA_UARTFR_MODEM_ANY; + uap->old_status = UART_GET_FR(port) & UART01x_FR_MODEM_ANY; /* * Finally, enable interrupts */ - UART_PUT_CR(port, AMBA_UARTCR_UARTEN | AMBA_UARTCR_RIE | - AMBA_UARTCR_RTIE); + UART_PUT_CR(port, UART01x_CR_UARTEN | UART010_CR_RIE | + UART010_CR_RTIE); return 0; } -static void ambauart_shutdown(struct uart_port *port) +static void pl010_shutdown(struct uart_port *port) { /* * Free the interrupt @@ -401,11 +403,11 @@ /* disable break condition and fifos */ UART_PUT_LCRH(port, UART_GET_LCRH(port) & - ~(AMBA_UARTLCR_H_BRK | AMBA_UARTLCR_H_FEN)); + ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN)); } static void -ambauart_set_termios(struct uart_port *port, struct termios *termios, +pl010_set_termios(struct uart_port *port, struct termios *termios, struct termios *old) { unsigned int lcr_h, old_cr; @@ -420,27 +422,27 @@ switch (termios->c_cflag & CSIZE) { case CS5: - lcr_h = AMBA_UARTLCR_H_WLEN_5; + lcr_h = UART01x_LCRH_WLEN_5; break; case CS6: - lcr_h = AMBA_UARTLCR_H_WLEN_6; + lcr_h = UART01x_LCRH_WLEN_6; break; case CS7: - lcr_h = AMBA_UARTLCR_H_WLEN_7; + lcr_h = UART01x_LCRH_WLEN_7; break; default: // CS8 - lcr_h = AMBA_UARTLCR_H_WLEN_8; + lcr_h = UART01x_LCRH_WLEN_8; break; } if (termios->c_cflag & CSTOPB) - lcr_h |= AMBA_UARTLCR_H_STP2; + lcr_h |= UART01x_LCRH_STP2; if (termios->c_cflag & PARENB) { - lcr_h |= AMBA_UARTLCR_H_PEN; + lcr_h |= UART01x_LCRH_PEN; if (!(termios->c_cflag & PARODD)) - lcr_h |= AMBA_UARTLCR_H_EPS; + lcr_h |= UART01x_LCRH_EPS; } if (port->fifosize > 1) - lcr_h |= AMBA_UARTLCR_H_FEN; + lcr_h |= UART01x_LCRH_FEN; spin_lock_irqsave(&port->lock, flags); @@ -449,26 +451,26 @@ */ uart_update_timeout(port, termios->c_cflag, baud); - port->read_status_mask = AMBA_UARTRSR_OE; + port->read_status_mask = UART01x_RSR_OE; if (termios->c_iflag & INPCK) - port->read_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE; + port->read_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE; if (termios->c_iflag & (BRKINT | PARMRK)) - port->read_status_mask |= AMBA_UARTRSR_BE; + port->read_status_mask |= UART01x_RSR_BE; /* * Characters to ignore */ port->ignore_status_mask = 0; if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= AMBA_UARTRSR_FE | AMBA_UARTRSR_PE; + port->ignore_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE; if (termios->c_iflag & IGNBRK) { - port->ignore_status_mask |= AMBA_UARTRSR_BE; + port->ignore_status_mask |= UART01x_RSR_BE; /* * If we're ignoring parity and break indicators, * ignore overruns too (for real raw support). */ if (termios->c_iflag & IGNPAR) - port->ignore_status_mask |= AMBA_UARTRSR_OE; + port->ignore_status_mask |= UART01x_RSR_OE; } /* @@ -478,10 +480,10 @@ port->ignore_status_mask |= UART_DUMMY_RSR_RX; /* first, disable everything */ - old_cr = UART_GET_CR(port) & ~AMBA_UARTCR_MSIE; + old_cr = UART_GET_CR(port) & ~UART010_CR_MSIE; if (UART_ENABLE_MS(port, termios->c_cflag)) - old_cr |= AMBA_UARTCR_MSIE; + old_cr |= UART010_CR_MSIE; UART_PUT_CR(port, 0); @@ -501,7 +503,7 @@ spin_unlock_irqrestore(&port->lock, flags); } -static const char *ambauart_type(struct uart_port *port) +static const char *pl010_type(struct uart_port *port) { return port->type == PORT_AMBA ? "AMBA" : NULL; } @@ -509,7 +511,7 @@ /* * Release the memory region(s) being used by 'port' */ -static void ambauart_release_port(struct uart_port *port) +static void pl010_release_port(struct uart_port *port) { release_mem_region(port->mapbase, UART_PORT_SIZE); } @@ -517,7 +519,7 @@ /* * Request the memory region(s) being used by 'port' */ -static int ambauart_request_port(struct uart_port *port) +static int pl010_request_port(struct uart_port *port) { return request_mem_region(port->mapbase, UART_PORT_SIZE, "serial_amba") != NULL ? 0 : -EBUSY; @@ -526,18 +528,18 @@ /* * Configure/autoconfigure the port. */ -static void ambauart_config_port(struct uart_port *port, int flags) +static void pl010_config_port(struct uart_port *port, int flags) { if (flags & UART_CONFIG_TYPE) { port->type = PORT_AMBA; - ambauart_request_port(port); + pl010_request_port(port); } } /* * verify the new serial_struct (for TIOCSSERIAL). */ -static int ambauart_verify_port(struct uart_port *port, struct serial_struct *ser) +static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser) { int ret = 0; if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA) @@ -549,23 +551,23 @@ return ret; } -static struct uart_ops amba_pops = { - .tx_empty = ambauart_tx_empty, - .set_mctrl = ambauart_set_mctrl, - .get_mctrl = ambauart_get_mctrl, - .stop_tx = ambauart_stop_tx, - .start_tx = ambauart_start_tx, - .stop_rx = ambauart_stop_rx, - .enable_ms = ambauart_enable_ms, - .break_ctl = ambauart_break_ctl, - .startup = ambauart_startup, - .shutdown = ambauart_shutdown, - .set_termios = ambauart_set_termios, - .type = ambauart_type, - .release_port = ambauart_release_port, - .request_port = ambauart_request_port, - .config_port = ambauart_config_port, - .verify_port = ambauart_verify_port, +static struct uart_ops amba_pl010_pops = { + .tx_empty = pl010_tx_empty, + .set_mctrl = pl010_set_mctrl, + .get_mctrl = pl010_get_mctrl, + .stop_tx = pl010_stop_tx, + .start_tx = pl010_start_tx, + .stop_rx = pl010_stop_rx, + .enable_ms = pl010_enable_ms, + .break_ctl = pl010_break_ctl, + .startup = pl010_startup, + .shutdown = pl010_shutdown, + .set_termios = pl010_set_termios, + .type = pl010_type, + .release_port = pl010_release_port, + .request_port = pl010_request_port, + .config_port = pl010_config_port, + .verify_port = pl010_verify_port, }; static struct uart_amba_port amba_ports[UART_NR] = { @@ -577,7 +579,7 @@ .irq = IRQ_UARTINT0, .uartclk = 14745600, .fifosize = 16, - .ops = &amba_pops, + .ops = &amba_pl010_pops, .flags = ASYNC_BOOT_AUTOCONF, .line = 0, }, @@ -592,7 +594,7 @@ .irq = IRQ_UARTINT1, .uartclk = 14745600, .fifosize = 16, - .ops = &amba_pops, + .ops = &amba_pl010_pops, .flags = ASYNC_BOOT_AUTOCONF, .line = 1, }, @@ -604,7 +606,7 @@ #ifdef CONFIG_SERIAL_AMBA_CONSOLE static void -ambauart_console_write(struct console *co, const char *s, unsigned int count) +pl010_console_write(struct console *co, const char *s, unsigned int count) { struct uart_port *port = &amba_ports[co->index].port; unsigned int status, old_cr; @@ -614,7 +616,7 @@ * First save the CR then disable the interrupts */ old_cr = UART_GET_CR(port); - UART_PUT_CR(port, AMBA_UARTCR_UARTEN); + UART_PUT_CR(port, UART01x_CR_UARTEN); /* * Now, do each character @@ -638,27 +640,27 @@ */ do { status = UART_GET_FR(port); - } while (status & AMBA_UARTFR_BUSY); + } while (status & UART01x_FR_BUSY); UART_PUT_CR(port, old_cr); } static void __init -ambauart_console_get_options(struct uart_port *port, int *baud, +pl010_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits) { - if (UART_GET_CR(port) & AMBA_UARTCR_UARTEN) { + if (UART_GET_CR(port) & UART01x_CR_UARTEN) { unsigned int lcr_h, quot; lcr_h = UART_GET_LCRH(port); *parity = 'n'; - if (lcr_h & AMBA_UARTLCR_H_PEN) { - if (lcr_h & AMBA_UARTLCR_H_EPS) + if (lcr_h & UART01x_LCRH_PEN) { + if (lcr_h & UART01x_LCRH_EPS) *parity = 'e'; else *parity = 'o'; } - if ((lcr_h & 0x60) == AMBA_UARTLCR_H_WLEN_7) + if ((lcr_h & 0x60) == UART01x_LCRH_WLEN_7) *bits = 7; else *bits = 8; @@ -668,7 +670,7 @@ } } -static int __init ambauart_console_setup(struct console *co, char *options) +static int __init pl010_console_setup(struct console *co, char *options) { struct uart_port *port; int baud = 38400; @@ -688,7 +690,7 @@ if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); else - ambauart_console_get_options(port, &baud, &parity, &bits); + pl010_console_get_options(port, &baud, &parity, &bits); return uart_set_options(port, co, baud, parity, bits, flow); } @@ -696,20 +698,20 @@ extern struct uart_driver amba_reg; static struct console amba_console = { .name = "ttyAM", - .write = ambauart_console_write, + .write = pl010_console_write, .device = uart_console_device, - .setup = ambauart_console_setup, + .setup = pl010_console_setup, .flags = CON_PRINTBUFFER, .index = -1, .data = &amba_reg, }; -static int __init ambauart_console_init(void) +static int __init pl010_console_init(void) { register_console(&amba_console); return 0; } -console_initcall(ambauart_console_init); +console_initcall(pl010_console_init); #define AMBA_CONSOLE &amba_console #else @@ -726,23 +728,55 @@ .cons = AMBA_CONSOLE, }; -static int __init ambauart_init(void) +static int pl010_probe(struct amba_device *dev, void *id) +{ + return 0; +} + +static int pl010_remove(struct amba_device *dev) +{ + return 0; +} + +static struct amba_id pl010_ids[] __initdata = { + { + .id = 0x00041010, + .mask = 0x000fffff, + }, + { 0, 0 }, +}; + +static struct amba_driver pl010_driver = { + .drv = { + .name = "uart-pl010", + }, + .id_table = pl010_ids, + .probe = pl010_probe, + .remove = pl010_remove, +}; + +static int __init pl010_init(void) { int ret; printk(KERN_INFO "Serial: AMBA driver $Revision: 1.41 $\n"); - ret = uart_register_driver(&amba_reg); + ret = amba_driver_register(&pl010_driver); if (ret == 0) { - int i; - - for (i = 0; i < UART_NR; i++) - uart_add_one_port(&amba_reg, &amba_ports[i].port); + ret = uart_register_driver(&amba_reg); + if (ret == 0) { + int i; + + for (i = 0; i < UART_NR; i++) + uart_add_one_port(&amba_reg, &amba_ports[i].port); + } else { + amba_driver_unregister(&pl010_driver); + } } return ret; } -static void __exit ambauart_exit(void) +static void __exit pl010_exit(void) { int i; @@ -750,10 +784,11 @@ uart_remove_one_port(&amba_reg, &amba_ports[i].port); uart_unregister_driver(&amba_reg); + amba_driver_unregister(&pl010_driver); } -module_init(ambauart_init); -module_exit(ambauart_exit); +module_init(pl010_init); +module_exit(pl010_exit); MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd"); MODULE_DESCRIPTION("ARM AMBA serial port driver $Revision: 1.41 $"); diff -urN orig/drivers/serial/anakin.c linux/drivers/serial/anakin.c --- orig/drivers/serial/anakin.c Mon May 5 17:39:41 2003 +++ linux/drivers/serial/anakin.c Mon May 5 17:43:40 2003 @@ -19,7 +19,7 @@ * SA_INTERRUPT. Works reliably now. No longer requires * changes to the serial_core API. * - * $Id: anakin.c,v 1.32 2002/07/28 10:03:27 rmk Exp $ + * $Id: anakin.c,v 1.33 2002/08/01 17:20:34 rmk Exp $ */ #include @@ -196,6 +196,8 @@ status |= (anakin_in(port, 0x10) & CTS ? TIOCM_CTS : 0); status |= (anakin_in(port, 0x18) & DCD ? TIOCM_CAR : 0); + + /* these two are bogus --rmk */ status |= (anakin_in(port, 0x18) & DTR ? TIOCM_DTR : 0); status |= (anakin_in(port, 0x18) & RTS ? TIOCM_RTS : 0); @@ -214,6 +216,7 @@ else status &= ~RTS; + /* this is bogus --rmk */ if (mctrl & TIOCM_CAR) status |= DCD; else @@ -523,7 +526,7 @@ { int ret; - printk(KERN_INFO "Serial: Anakin driver $Revision: 1.32 $\n"); + printk(KERN_INFO "Serial: Anakin driver $Revision: 1.33 $\n"); ret = uart_register_driver(&anakin_reg); if (ret == 0) { diff -urN orig/drivers/serial/core.c linux/drivers/serial/core.c --- orig/drivers/serial/core.c Tue May 27 10:05:13 2003 +++ linux/drivers/serial/core.c Tue May 27 10:13:30 2003 @@ -529,8 +529,7 @@ { struct uart_state *state = tty->driver_data; - if (tty) - __uart_put_char(state->port, &state->info->xmit, ch); + __uart_put_char(state->port, &state->info->xmit, ch); } static void uart_flush_chars(struct tty_struct *tty) @@ -545,7 +544,7 @@ struct uart_state *state = tty->driver_data; int ret; - if (!tty || !state->info->xmit.buf) + if (!state->info->xmit.buf) return 0; if (from_user) @@ -837,9 +836,10 @@ * instead of clearing it, then bitch about it. No * need to rate-limit; it's CAP_SYS_ADMIN only. */ if (port->flags & UPF_SPD_MASK) { - printk(KERN_NOTICE "%s sets custom speed on %s%d. This is deprecated.\n", - current->comm, state->info->tty->driver->name, - state->port->line); + printk(KERN_NOTICE + "%s sets custom speed on %s. This " + "is deprecated.\n", current->comm, + state->info->tty->name); } uart_change_speed(state, NULL); } diff -urN orig/drivers/serial/dz.c linux/drivers/serial/dz.c --- orig/drivers/serial/dz.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/serial/dz.c Mon Jan 6 22:16:15 2003 @@ -0,0 +1,821 @@ +/* + * dz.c: Serial port driver for DECStations equiped + * with the DZ chipset. + * + * Copyright (C) 1998 Olivier A. D. Lebaillif + * + * Email: olivier.lebaillif@ifrsys.com + * + * [31-AUG-98] triemer + * Changed IRQ to use Harald's dec internals interrupts.h + * removed base_addr code - moving address assignment to setup.c + * Changed name of dz_init to rs_init to be consistent with tc code + * [13-NOV-98] triemer fixed code to receive characters + * after patches by harald to irq code. + * [09-JAN-99] triemer minor fix for schedule - due to removal of timeout + * field from "current" - somewhere between 2.1.121 and 2.1.131 + Qua Jun 27 15:02:26 BRT 2001 + * [27-JUN-2001] Arnaldo Carvalho de Melo - cleanups + * + * Parts (C) 1999 David Airlie, airlied@linux.ie + * [07-SEP-99] Bugfixes + * + * [06-Jan-2002] Russell King + * Converted to new serial core + */ + +#define DEBUG_DZ 1 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CONSOLE_LINE (3) /* for definition of struct console */ + +#include "dz.h" + +#define DZ_INTR_DEBUG 1 + +static char *dz_name = "DECstation DZ serial driver version "; +static char *dz_version = "1.02"; + +struct dz_port { + struct uart_port port; + unsigned int cflag; +}; + +static struct dz_port dz_ports[DZ_NB_PORT]; + +#ifdef DEBUG_DZ +/* + * debugging code to send out chars via prom + */ +static void debug_console(const char *s, int count) +{ + unsigned i; + + for (i = 0; i < count; i++) { + if (*s == 10) + prom_printf("%c", 13); + prom_printf("%c", *s++); + } +} +#endif + +/* + * ------------------------------------------------------------ + * dz_in () and dz_out () + * + * These routines are used to access the registers of the DZ + * chip, hiding relocation differences between implementation. + * ------------------------------------------------------------ + */ + +static inline unsigned short dz_in(struct dz_port *dport, unsigned offset) +{ + volatile unsigned short *addr = + (volatile unsigned short *) (dport->port.base + offset); + return *addr; +} + +static inline void dz_out(struct dz_port *dport, unsigned offset, + unsigned short value) +{ + volatile unsigned short *addr = + (volatile unsigned short *) (dport->port.base + offset); + *addr = value; +} + +/* + * ------------------------------------------------------------ + * rs_stop () and rs_start () + * + * These routines are called before setting or resetting + * tty->stopped. They enable or disable transmitter interrupts, + * as necessary. + * ------------------------------------------------------------ + */ + +static void dz_stop_tx(struct uart_port *uport, unsigned int tty_stop) +{ + struct dz_port *dport = (struct dz_port *)uport); + unsigned short tmp, mask = 1 << dport->port.line; + unsigned long flags; + + spin_lock_irqsave(&dport->port.lock, flags); + tmp = dz_in(dport, DZ_TCR); /* read the TX flag */ + tmp &= ~mask; /* clear the TX flag */ + dz_out(dport, DZ_TCR, tmp); + spin_unlock_irqrestore(&dport->port.lock, flags); +} + +static void dz_start_tx(struct uart_port *uport, unsigned int tty_start) +{ + struct dz_port *dport = (struct dz_port *)uport); + unsigned short tmp, mask = 1 << port->line; + unsigned long flags; + + spin_lock_irqsave(&dport->port.lock, flags); + tmp = dz_in(dport, DZ_TCR); /* read the TX flag */ + tmp |= mask; /* set the TX flag */ + dz_out(dport, DZ_TCR, tmp); + spin_unlock_irqrestore(&dport->port.lock, flags); +} + +static void dz_stop_rx(struct uart_port *uport) +{ + struct dz_port *dport = (struct dz_port *)uport); + unsigned long flags; + + spin_lock_irqsave(&dport->port.lock, flags); + dport->cflag &= ~DZ_CREAD; + dz_out(dport, DZ_LPR, dport->cflags); + spin_unlock_irqrestore(&dport->port.lock, flags); +} + +static void dz_enable_ms(struct uart_port *port) +{ + /* nothing to do */ +} + +/* + * ------------------------------------------------------------ + * Here starts the interrupt handling routines. All of the + * following subroutines are declared as inline and are folded + * into dz_interrupt. They were separated out for readability's + * sake. + * + * Note: rs_interrupt() is a "fast" interrupt, which means that it + * runs with interrupts turned off. People who may want to modify + * rs_interrupt() should try to keep the interrupt handler as fast as + * possible. After you are done making modifications, it is not a bad + * idea to do: + * + * make drivers/serial/dz.s + * + * and look at the resulting assemble code in dz.s. + * + * ------------------------------------------------------------ + */ + +/* + * ------------------------------------------------------------ + * receive_char () + * + * This routine deals with inputs from any lines. + * ------------------------------------------------------------ + */ +static inline void dz_receive_chars(struct dz_port *dport) +{ + struct tty_struct *tty = NULL; + struct async_icount *icount; + int ignore = 0; + unsigned short status, tmp; + unsigned char ch; + + /* this code is going to be a problem... + the call to tty_flip_buffer is going to need + to be rethought... + */ + do { + status = dz_in(info_in, DZ_RBUF); + dport = dz_ports[LINE(status)]; + + /* punt so we don't get duplicate characters */ + if (!(status & DZ_DVAL)) + goto ignore_char; + + + ch = UCHAR(status); /* grab the char */ + +#if 0 + if (info->is_console) { + if (ch == 0) + return; /* it's a break ... */ + } +#endif + + tty = dport->port.info->tty;/* now tty points to the proper dev */ + icount = &dport->port.icount; + + if (!tty) + break; + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + break; + + *tty->flip.char_buf_ptr = ch; + *tty->flip.flag_buf_ptr = 0; + icount->rx++; + + /* keep track of the statistics */ + if (status & (DZ_OERR | DZ_FERR | DZ_PERR)) { + if (status & DZ_PERR) /* parity error */ + icount->parity++; + else if (status & DZ_FERR) /* frame error */ + icount->frame++; + if (status & DZ_OERR) /* overrun error */ + icount->overrun++; + + /* check to see if we should ignore the character + and mask off conditions that should be ignored + */ + + if (status & dport->port.ignore_status_mask) { + if (++ignore > 100) + break; + goto ignore_char; + } + /* mask off the error conditions we want to ignore */ + tmp = status & dport->port.read_status_mask; + + if (tmp & DZ_PERR) { + *tty->flip.flag_buf_ptr = TTY_PARITY; + debug_console("PERR\n", 5); + } else if (tmp & DZ_FERR) { + *tty->flip.flag_buf_ptr = TTY_FRAME; + debug_console("FERR\n", 5); + } + if (tmp & DZ_OERR) { + debug_console("OERR\n", 5); + if (tty->flip.count < TTY_FLIPBUF_SIZE) { + tty->flip.count++; + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + *tty->flip.flag_buf_ptr = TTY_OVERRUN; + } + } + } + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + ignore_char: + } while (status & DZ_DVAL); + + if (tty) + tty_flip_buffer_push(tty); +} + +/* + * ------------------------------------------------------------ + * transmit_char () + * + * This routine deals with outputs to any lines. + * ------------------------------------------------------------ + */ +static inline void dz_transmit_chars(struct dz_port *dport) +{ + struct circ_buf *xmit = &dport->port.info->xmit; + unsigned char tmp; + + if (dport->port.x_char) { /* XON/XOFF chars */ + dz_out(dport, DZ_TDR, dport->port.x_char); + dport->port.icount.tx++; + dport->port.x_char = 0; + return; + } + /* if nothing to do or stopped or hardware stopped */ + if (uart_circ_empty(xmit) || uart_tx_stopped(&dport->port)) { + dz_stop_tx(&dport->port, 0); + return; + } + + /* + * if something to do ... (rember the dz has no output fifo so we go + * one char at a time :-< + */ + tmp = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (DZ_XMIT_SIZE - 1); + dz_out(dport, DZ_TDR, tmp); + dport->port.icount.tx++; + + if (uart_circ_chars_pending(xmit) < DZ_WAKEUP_CHARS) + uart_write_wakeup(&dport->port); + + /* Are we done */ + if (uart_circ_empty(xmit)) + dz_stop_tx(&dport->port, 0); +} + +/* + * ------------------------------------------------------------ + * check_modem_status () + * + * Only valid for the MODEM line duh ! + * ------------------------------------------------------------ + */ +static inline void check_modem_status(struct dz_port *dport) +{ + unsigned short status; + + /* if not ne modem line just return */ + if (dport->port.line != DZ_MODEM) + return; + + status = dz_in(dport, DZ_MSR); + + /* it's easy, since DSR2 is the only bit in the register */ + if (status) + dport->port.icount.dsr++; +} + +/* + * ------------------------------------------------------------ + * dz_interrupt () + * + * this is the main interrupt routine for the DZ chip. + * It deals with the multiple ports. + * ------------------------------------------------------------ + */ +static void dz_interrupt(int irq, void *dev, struct pt_regs *regs) +{ + struct dz_port *dport; + unsigned short status; + + /* get the reason why we just got an irq */ + status = dz_in((struct dz_port *)dev, DZ_CSR); + dport = &dz_ports[LINE(status)]; + + if (status & DZ_RDONE) + dz_receive_chars(dport); + + if (status & DZ_TRDY) + dz_transmit_chars(port); + + /* FIXME: what about check modem status??? --rmk */ +} + +/* + * ------------------------------------------------------------------- + * Here ends the DZ interrupt routines. + * ------------------------------------------------------------------- + */ + +static unsigned int dz_get_mctrl(struct uart_port *uport) +{ + struct dz_port *dport = (struct dz_port *)uport; + unsigned int mctrl = TIOCM_CAR | TIOCM_DSR | TIOCM_CTS; + + if (dport->port.line == DZ_MODEM) { + /* + * CHECKME: This is a guess from the other code... --rmk + */ + if (dz_in(dport, DZ_MSR) & DZ_MODEM_DSR) + mctrl &= ~TIOCM_DSR; + } + + return mctrl; +} + +static void dz_set_mctrl(struct uart_port *uport, unsigned int mctrl) +{ + struct dz_port *dport = (struct dz_port *)uport; + unsigned short tmp; + + if (dport->port.line == DZ_MODEM) { + tmp = dz_in(dport, DZ_TCR); + if (mctrl & TIOCM_DTR) + tmp &= ~DZ_MODEM_DTR; + else + tmp |= DZ_MODEM_DTR; + dz_out(dport, DZ_TCR, tmp); + } +} + +/* + * ------------------------------------------------------------------- + * startup () + * + * various initialization tasks + * ------------------------------------------------------------------- + */ +static int dz_startup(struct uart_port *uport) +{ + struct dz_port *dport = (struct dz_port *)uport; + unsigned long flags; + unsigned short tmp; + + /* The dz lines for the mouse/keyboard must be + * opened using their respective drivers. + */ + if ((dport->port.line == DZ_KEYBOARD) || + (dport->port.line == DZ_MOUSE)) + return -ENODEV; + + spin_lock_irqsave(&dport->port.lock); + + /* enable the interrupt and the scanning */ + tmp = dz_in(dport, DZ_CSR); + tmp |= DZ_RIE | DZ_TIE | DZ_MSE; + dz_out(dport, DZ_CSR, tmp); + + spin_unlock_irqrestore(&dport->port.lock); + + return 0; +} + +/* + * ------------------------------------------------------------------- + * shutdown () + * + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. + * ------------------------------------------------------------------- + */ +static void dz_shutdown(struct uart_port *uport) +{ + dz_stop(uport, 0); +} + +/* + * get_lsr_info - get line status register info + * + * Purpose: Let user call ioctl() to get info when the UART physically + * is emptied. On bus types like RS485, the transmitter must + * release the bus after transmitting. This must be done when + * the transmit shift register is empty, not be done when the + * transmit holding register is empty. This functionality + * allows an RS485 driver to be written in user space. + */ +static unsigned int dz_tx_empty(struct uart_port *uport) +{ + struct dz_port *dport = (struct dz_port *)uport; + unsigned short status = dz_in(dport, DZ_LPR); + + /* FIXME: this appears to be obviously broken --rmk. */ + return status ? TIOCSER_TEMT : 0; +} + +static void dz_break_ctl(struct uart_port *port, int break_state) +{ + unsigned long flags; + unsigned short tmp, mask = 1 << port->line; + + spin_lock_irqsave(&port->lock, flags); + tmp = dz_in(port, DZ_TCR); + if (break_state) + tmp |= mask; + else + tmp &= ~mask; + dz_out(port, DZ_TCR, tmp); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void dz_set_termios(struct uart_port *uport, struct termios *termios, + struct termios *old_termios) +{ + struct dz_port *dport = (struct dz_port *)uport; + unsigned long flags; + unsigned int cflag, baud; + + cflag = dport->port.line; + + switch (termios->c_cflag & CSIZE) { + case CS5: + cflag |= DZ_CS5; + break; + case CS6: + cflag |= DZ_CS6; + break; + case CS7: + cflag |= DZ_CS7; + break; + case CS8: + default: + cflag |= DZ_CS8; + } + + if (termios->c_cflag & CSTOPB) + cflag |= DZ_CSTOPB; + if (termios->c_cflag & PARENB) + cflag |= DZ_PARENB; + if (termios->c_cflag & PARODD) + cflag |= DZ_PARODD; + + baud = uart_get_baud_rate(termios); + switch (baud) { + case 50: + cflag |= DZ_B50; + break; + case 75: + cflag |= DZ_B75; + break; + case 110: + cflag |= DZ_B110; + break; + case 134: + cflag |= DZ_B134; + break; + case 150: + cflag |= DZ_B150; + break; + case 300: + cflag |= DZ_B300; + break; + case 600: + cflag |= DZ_B600; + break; + case 1200: + cflag |= DZ_B1200; + break; + case 1800: + cflag |= DZ_B1800; + break; + case 2000: + cflag |= DZ_B2000; + break; + case 2400: + cflag |= DZ_B2400; + break; + case 3600: + cflag |= DZ_B3600; + break; + case 4800: + cflag |= DZ_B4800; + break; + case 7200: + cflag |= DZ_B7200; + break; + case 9600: + default: + cflag |= DZ_B9600; + } + + if (termios->c_cflag & CREAD) + cflag |= DZ_RXENAB; + + spin_lock_irqsave(&dport->port.lock, flags); + + info->cflags = cflag; + dz_out(info, DZ_LPR, info->cflags); + + /* setup accept flag */ + dport->port.read_status_mask = DZ_OERR; + if (termios->c_iflag & INPCK) + dport->port.read_status_mask |= DZ_FERR | DZ_PERR; + + /* characters to ignore */ + info->ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + dport->port.ignore_status_mask |= DZ_FERR | DZ_PERR; + + spin_unlock_irqrestore(&dport->port.lock, flags); +} + +static const char *dz_type(struct uart_port *port) +{ + return "DZ"; +} + +static void dz_release_port(struct uart_port *port) +{ + /* nothing to do */ +} + +static int dz_request_port(struct uart_port *port) +{ + return 0; +} + +static void dz_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) + port->type = PORT_DZ; +} + +/* + * verify the new serial_struct (for TIOCSSERIAL). + */ +static int dz_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + int ret = 0; + if (ser->type != PORT_UNKNOWN && ser->type != PORT_DZ) + ret = -EINVAL; + if (ser->irq != port->irq) + ret = -EINVAL; + return ret; +} + +static struct uart_ops dz_ops = { + .tx_empty = dz_tx_empty, + .get_mctrl = dz_get_mctrl, + .set_mctrl = dz_set_mctrl, + .stop_tx = dz_stop_tx, + .start_tx = dz_start_tx, + .stop_rx = dz_stop_rx, + .enable_ms = dz_enable_ms, + .break_ctl = dz_break_ctl, + .startup = dz_startup, + .shutdown = dz_shutdown, + .set_termios = dz_set_termios, + .type = dz_type, + .release_port = dz_release_port, + .request_port = dz_request_port, + .config_port = dz_config_port, + .verify_port = dz_verify_port, +}; + +static void __init dz_init_ports(void) +{ + static int first = 1; + struct dz_port *dport; + unsigned long base; + int i; + + if (!first) + return; + first = 0; + + if (mips_machtype == MACH_DS23100 || + mips_machtype == MACH_DS5100) + base = (unsigned long) KN01_DZ11_BASE; + else + base = (unsigned long) KN02_DZ11_BASE; + + for (i = 0, dport = dz_ports; i < DZ_NB_PORT; i++, dport++) { + spin_lock_init(&dport->port.lock); + dport->port.base = base; + dport->port.iotype = SERIAL_IO_PORT, + dport->port.irq = dec_interrupt[DEC_IRQ_DZ11]; + dport->port.line = i; + dport->port.fifosize = 1, + dport->port.ops = &dz_ops, + dport->port.flags = UPF_BOOT_AUTOCONF, + } +} + +static void dz_reset(struct dz_port *dport) +{ + dz_out(dport, DZ_CSR, DZ_CLR); + + while (dz_in(dport, DZ_CSR) & DZ_CLR); + /* FIXME: cpu_relax? */ + + iob(); + + /* enable scanning */ + dz_out(dport, DZ_CSR, DZ_MSE); +} + +#ifdef CONFIG_SERIAL_DZ_CONSOLE +static void dz_console_put_char(struct dz_port *dport, unsigned char ch) +{ + unsigned long flags; + int loops = 2500; + unsigned short tmp = ch; + /* this code sends stuff out to serial device - spinning its + wheels and waiting. */ + + spin_lock_irqsave(&dport->port.lock, flags); + + /* spin our wheels */ + while (((dz_in(dz_console, DZ_CSR) & DZ_TRDY) != DZ_TRDY) && loops--); + /* FIXME: cpu_relax, udelay? --rmk */ + + /* Actually transmit the character. */ + dz_out(dz_console, DZ_TDR, tmp); + + spin_unlock_irqrestore(&dport->port.lock, flags); +} +/* + * ------------------------------------------------------------------- + * dz_console_print () + * + * dz_console_print is registered for printk. + * The console must be locked when we get here. + * ------------------------------------------------------------------- + */ +static void dz_console_print(struct console *cons, + const char *str, + unsigned int count) +{ + struct dz_port *dport = &dz_ports[CONSOLE_LINE]; +#ifdef DEBUG_DZ + prom_printf((char *) str); +#endif + while (count--) { + if (*str == '\n') + dz_console_put_char(dport, '\r'); + dz_console_put_char(dport, *str++); + } +} + +static kdev_t dz_console_device(struct console *c) +{ + return mk_kdev(TTY_MAJOR, 64 + c->index); +} + +static int __init dz_console_setup(struct console *co, char *options) +{ + struct dz_port *dport = &dz_ports[CONSOLE_LINE]; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + int ret; + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + dz_reset(dport); + + ret = uart_set_options(&dport->port, co, baud, parity, bits, flow); + if (ret == 0) { + mask = 1 << dport->port.line; + tmp = dz_in(dport, DZ_TCR); /* read the TX flag */ + if (!(tmp & mask)) { + tmp |= mask; /* set the TX flag */ + dz_out(dport, DZ_TCR, tmp); + } + } + + return ret; +} + +static struct console dz_sercons = +{ + .name = "ttyS", + .write = dz_console_print, + .device = dz_console_device, + .setup = dz_console_setup, + .flags = CON_CONSDEV | CON_PRINTBUFFER, + .index = CONSOLE_LINE, +}; + +void __init dz_serial_console_init(void) +{ + dz_init_ports(); + + register_console(&dz_sercons); +} + +#define SERIAL_DZ_CONSOLE &dz_sercons +#else +#define SERIAL_DZ_CONSOLE NULL +#endif /* CONFIG_SERIAL_DZ_CONSOLE */ + +static struct uart_driver dz_reg = { + .owner = THIS_MODULE, + .driver_name = "serial", +#ifdef CONFIG_DEVFS + .dev_name = "tts/%d", +#else + .dev_name = "ttyS%d", +#endif + .major = TTY_MAJOR, + .minor = 64, + .nr = DZ_NB_PORT, + .cons = SERIAL_DZ_CONSOLE, +}; + +int __init dz_init(void) +{ + unsigned short tmp; + int ret, i; + + printk("%s%s\n", dz_name, dz_version); + + dz_init_ports(); + + save_flags(flags); + cli(); + +#ifndef CONFIG_SERIAL_DZ_CONSOLE + /* reset the chip */ + dz_reset(&dz_ports[0]); +#endif + + /* order matters here... the trick is that flags + is updated... in request_irq - to immediatedly obliterate + it is unwise. */ + restore_flags(flags); + + if (request_irq(dz_ports[0].port.irq, dz_interrupt, + SA_INTERRUPT, "DZ", &dz_ports[0])) + panic("Unable to register DZ interrupt"); + + ret = uart_register_driver(&dz_reg); + if (ret != 0) + return ret; + + for (i = 0; i < DZ_NB_PORT; i++) + uart_add_one_port(&dz_reg, &dz_port[i].port); + + return ret; +} + +MODULE_DESCRIPTION("DECstation DZ serial driver"); +MODULE_LICENSE("GPL"); diff -urN orig/drivers/serial/dz.h linux/drivers/serial/dz.h --- orig/drivers/serial/dz.h Thu Jan 1 01:00:00 1970 +++ linux/drivers/serial/dz.h Mon Jan 6 16:30:23 2003 @@ -0,0 +1,118 @@ +/* + * dz.h: Serial port driver for DECStations equiped + * with the DZ chipset. + * + * Copyright (C) 1998 Olivier A. D. Lebaillif + * + * Email: olivier.lebaillif@ifrsys.com + * + */ +#ifndef DZ_SERIAL_H +#define DZ_SERIAL_H + +/* + * Definitions for the Control and Status Received. + */ +#define DZ_TRDY 0x8000 /* Transmitter empty */ +#define DZ_TIE 0x4000 /* Transmitter Interrupt Enable */ +#define DZ_RDONE 0x0080 /* Receiver data ready */ +#define DZ_RIE 0x0040 /* Receive Interrupt Enable */ +#define DZ_MSE 0x0020 /* Master Scan Enable */ +#define DZ_CLR 0x0010 /* Master reset */ +#define DZ_MAINT 0x0008 /* Loop Back Mode */ + +/* + * Definitions for the Received buffer. + */ +#define DZ_RBUF_MASK 0x00FF /* Data Mask in the Receive Buffer */ +#define DZ_LINE_MASK 0x0300 /* Line Mask in the Receive Buffer */ +#define DZ_DVAL 0x8000 /* Valid Data indicator */ +#define DZ_OERR 0x4000 /* Overrun error indicator */ +#define DZ_FERR 0x2000 /* Frame error indicator */ +#define DZ_PERR 0x1000 /* Parity error indicator */ + +#define LINE(x) (x & DZ_LINE_MASK) >> 8 /* Get the line number from the input buffer */ +#define UCHAR(x) (unsigned char)(x & DZ_RBUF_MASK) + +/* + * Definitions for the Transmit Register. + */ +#define DZ_LINE_KEYBOARD 0x0001 +#define DZ_LINE_MOUSE 0x0002 +#define DZ_LINE_MODEM 0x0004 +#define DZ_LINE_PRINTER 0x0008 + +#define DZ_MODEM_DTR 0x0400 /* DTR for the modem line (2) */ + +/* + * Definitions for the Modem Status Register. + */ +#define DZ_MODEM_DSR 0x0200 /* DSR for the modem line (2) */ + +/* + * Definitions for the Transmit Data Register. + */ +#define DZ_BRK0 0x0100 /* Break assertion for line 0 */ +#define DZ_BRK1 0x0200 /* Break assertion for line 1 */ +#define DZ_BRK2 0x0400 /* Break assertion for line 2 */ +#define DZ_BRK3 0x0800 /* Break assertion for line 3 */ + +/* + * Definitions for the Line Parameter Register. + */ +#define DZ_KEYBOARD 0x0000 /* line 0 = keyboard */ +#define DZ_MOUSE 0x0001 /* line 1 = mouse */ +#define DZ_MODEM 0x0002 /* line 2 = modem */ +#define DZ_PRINTER 0x0003 /* line 3 = printer */ + +#define DZ_CSIZE 0x0018 /* Number of bits per byte (mask) */ +#define DZ_CS5 0x0000 /* 5 bits per byte */ +#define DZ_CS6 0x0008 /* 6 bits per byte */ +#define DZ_CS7 0x0010 /* 7 bits per byte */ +#define DZ_CS8 0x0018 /* 8 bits per byte */ + +#define DZ_CSTOPB 0x0020 /* 2 stop bits instead of one */ + +#define DZ_PARENB 0x0040 /* Parity enable */ +#define DZ_PARODD 0x0080 /* Odd parity instead of even */ + +#define DZ_CBAUD 0x0E00 /* Baud Rate (mask) */ +#define DZ_B50 0x0000 +#define DZ_B75 0x0100 +#define DZ_B110 0x0200 +#define DZ_B134 0x0300 +#define DZ_B150 0x0400 +#define DZ_B300 0x0500 +#define DZ_B600 0x0600 +#define DZ_B1200 0x0700 +#define DZ_B1800 0x0800 +#define DZ_B2000 0x0900 +#define DZ_B2400 0x0A00 +#define DZ_B3600 0x0B00 +#define DZ_B4800 0x0C00 +#define DZ_B7200 0x0D00 +#define DZ_B9600 0x0E00 + +#define DZ_CREAD 0x1000 /* Enable receiver */ +#define DZ_RXENAB 0x1000 /* enable receive char */ +/* + * Addresses for the DZ registers + */ +#define DZ_CSR 0x00 /* Control and Status Register */ +#define DZ_RBUF 0x08 /* Receive Buffer */ +#define DZ_LPR 0x08 /* Line Parameters Register */ +#define DZ_TCR 0x10 /* Transmitter Control Register */ +#define DZ_MSR 0x18 /* Modem Status Register */ +#define DZ_TDR 0x18 /* Transmit Data Register */ + +#define DZ_NB_PORT 4 + +#define DZ_XMIT_SIZE 4096 /* buffer size */ +#define DZ_WAKEUP_CHARS DZ_XMIT_SIZE/4 + +#ifdef MODULE +int init_module (void) +void cleanup_module (void) +#endif + +#endif /* DZ_SERIAL_H */ diff -urN orig/drivers/serial/rocket.c linux/drivers/serial/rocket.c --- orig/drivers/serial/rocket.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/serial/rocket.c Sat Nov 2 10:42:36 2002 @@ -0,0 +1,1090 @@ +/* + * linux/drivers/serial/rocket.c + * + * Driver for Comtrol Rocket serial ports + * + * Based on drivers/char/rocket.c, by Linus Torvalds, Theodore Ts'o. + * + * Copyright (C) 1995, 1996, 1997 by Comtrol, Inc. + * Copyright (C) 2002 Russell King. + * + * 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 + * + * $Id: rocket.c,v 1.31 2002/02/05 17:44:23 rmk Exp $ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +static struct timer_list rocket_timer; + +/* + * Assigned major numbers for the Comtrol Rocketport + */ +#define TTY_ROCKET_MAJOR 46 +#define CUA_ROCKET_MAJOR 47 +#define MAX_RP_PORTS 128 + +#define DUMMY_RX_STATUS 0x80000000 + +typedef struct { + unsigned int TxRxCount; + unsigned int TxRxData; + unsigned int ChanStat; + unsigned int IntID; + + unsigned int TxPrioCnt; + unsigned int TxPrioBuf; + + /* + * Per-AIOP + */ +} CHANNEL_T; + +struct rocket_aiop; +struct rocket_board; + +/* + * We wrap our port structure around the generic uart_port. + */ +struct rocket_port { + struct uart_port port; + unsigned int chan; + struct rocket_aiop *aiop; + u32 txctl; + u32 rxctl; + + CHANNEL_T channel; + unsigned int intmask; + unsigned int cd_status; + +}; + +struct rocket_aiop { + unsigned long base; + int nr; + + struct rocket_board *board; + struct rocket_port *port[8]; +}; + +struct rocket_board { + unsigned long base; + int nr; + + struct list_head all; + + struct pci_dev *pci; + struct rocket_aiop *aiop[4]; +}; + +#define INTID_RXFTRIG 0x20 +#define INTID_TXFIFOEMT 0x10 +#define INTID_SRCINT 0x08 +#define INTID_DELTACD 0x04 +#define INTID_DELTACTS 0x02 +#define INTID_DELTADSR 0x01 + +#define CSTAT_RDA 0x0001 +#define CSTAT_TXSHREMT 0x0002 +#define CSTAT_TXFIFOEMT 0x0004 +#define CSTAT_CDACT 0x0008 +#define CSTAT_DSRACT 0x0010 +#define CSTAT_CTSACT 0x0020 +#define CSTAT_RXPARITY 0x0100 +#define CSTAT_RXFRAME 0x0200 +#define CSTAT_RXBREAK 0x0400 +#define CSTAT_RX1MATCH 0x0800 +#define CSTAT_RX2MATCH 0x1000 +#define CSTAT_RXOVERFL 0x2000 +#define CSTAT_STATMODE 0x8000 + +#define STM_PARITY 0x0001 +#define STM_RCVROVR 0x0002 +#define STM_FRAME 0x0004 +#define STM_BREAK 0x0008 +#define STM_PARITYH 0x0100 +#define STM_RCVROVRH 0x0200 +#define STM_FRAMEH 0x0400 +#define STM_BREAKH 0x0800 + +#define TXFIFO_SIZE 255 + +#define RX_FIFO(chnl) ((chnl << 12) | 0x000) +#define TX_FIFO(chnl) ((chnl << 12) | 0x800) +#define RX_FIFO_OUTP(chnl) ((chnl << 12) | 0x990) +#define RX_FIFO_INP(chnl) ((chnl << 12) | 0x992) +#define TX_FIFO_OUTP(chnl) ((chnl << 12) | 0x994) +#define TX_FIFO_INP(chnl) ((chnl << 12) | 0x995) +#define TXP_CNT(chnl) ((chnl << 12) | 0x996) +#define TXP_FIFO_PTR(chnl) ((chnl << 12) | 0x997) +#define TXP_BUF(chnl) ((chnl << 12) | 0x9c0) +#define TX_CTRL(chnl) ((chnl << 12) | 0xff0) +#define RX_CTRL(chnl) ((chnl << 12) | 0xff2) +#define BAUD(chnl) ((chnl << 12) | 0xff4) +#define CLK_PRE(chnl) ((chnl << 12) | 0xff6) + +#define TXCTL_DATA8BIT 0x00010000 +#define TXCTL_EVENPAR 0x00020000 +#define TXCTL_PARITYEN 0x00040000 +#define TXCTL_STOP2 0x00080000 +#define TXCTL_TXINTEN 0x00100000 +#define TXCTL_RTSTOGEN 0x00400000 +#define TXCTL_CTSFCEN 0x00800000 +#define TXCTL_ENABLE 0x01000000 +#define TXCTL_SETRTS 0x02000000 +#define TXCTL_SETDTR 0x04000000 +#define TXCTL_LOCALLOOP 0x08000000 +#define TXCTL_SETBREAK 0x10000000 + +#define RXCTL_MCINTEN 0x00010000 +#define RXCTL_RXINTEN 0x00020000 +#define RXCTL_SRCINTEN 0x00040000 +#define RXCTL_TRIG0 0x00000000 +#define RXCTL_TRIG1 0x00080000 +#define RXCTL_TRIG1_2 0x00100000 +#define RXCTL_TRIG7_8 0x00180000 +#define RXCTL_TRIGMASK 0x00180000 +#define RXCTL_RXPROCEN 0x00200000 +#define RXCTL_RTSFCEN 0x00400000 + +#define TXPCNT_PRIPEND 0x0080 + +/* + * Per-channel AIOP registers + */ +#define CHAN_TDRD(aiop,chan) ((aiop)->base + (chan) * 2 + 0x00) +#define CHAN_FIFOCNT(aiop,chan) ((aiop)->base + (chan) * 2 + 0x10) +#define CHAN_STAT(aiop,chan) ((aiop)->base + (chan) * 2 + 0x20) +#define CHAN_INTID(aiop,chan) ((aiop)->base + (chan) + 0x30) + +/* + * Global AIOP registers + */ +#define AIOP_CMD(aiop) ((aiop)->base + 0x38) +#define AIOP_INTCHAN(aiop) ((aiop)->base + 0x39) +#define AIOP_INTMASK(aiop) ((aiop)->base + 0x3a) +#define AIOP_INDEX(aiop) ((aiop)->base + 0x3c) +#define AIOP_IDATA(aiop) ((aiop)->base + 0x3e) + +#define AIOPC_RESETALL 0x80 +#define AIOPC_TXOVERIDE 0x40 +#define AIOPC_RESETUART 0x20 +#define AIOPC_RESTXFCNT 0x10 +#define AIOPC_RESRXFCNT 0x08 + +/* + * PCI specific registers + * (Note, the PCI_INT_STATUS overlaps with the first AIOP_INTMASK - + * we must use a 16-bit read to access this register) + */ +#define PCI_INT_STATUS(board) ((board)->base + 0x3a) + +static LIST_HEAD(rocket_boards); + +static void rocket_flush_fifos(struct rocket_port *rp) +{ + struct rocket_aiop *aiop = rp->aiop; + + if (inw_p(rp->channel.TxRxCount)) { +#warning Rx foo + /* sFlushRxFIFO */ + outb_p(rp->chan | AIOPC_RESRXFCNT, AIOP_CMD(aiop)); + outb_p(rp->chan, AIOP_CMD(aiop)); + outw_p(RX_FIFO_OUTP(rp->chan), AIOP_INDEX(aiop)); + outw_p(0, AIOP_IDATA(aiop)); + outw_p(RX_FIFO_INP(rp->chan), AIOP_INDEX(aiop)); + outw_p(0, AIOP_IDATA(aiop)); + /* if (RxFIFOEnabled) sEnRxFIFO(rp); */ + } + if (inb_p(rp->channel.TxRxCount)) { +#warning Tx foo + /* sFlushTxFIFO */ + /* sStopRxProcessor(rp); */ + udelay(4); + outb_p(rp->chan | AIOPC_RESTXFCNT, AIOP_CMD(aiop)); + outb_p(rp->chan, AIOP_CMD(aiop)); + outw_p(TX_FIFO_OUTP(rp->chan), AIOP_INDEX(aiop)); + outw_p(0, AIOP_IDATA(aiop)); + /* if (TxEnabled) sEnTransmit(rp); */ + /* sStartRxProcessor(rp); */ + } +} + +static inline void rocket_clear_xoff(struct rocket_port *rp) +{ + outb_p(rp->chan | AIOPC_TXOVERIDE, AIOP_CMD(rp->aiop)); + outb_p(rp->chan, AIOP_CMD(rp->aiop)); +} + +static void rocket_stop_tx(struct uart_port *port, unsigned int tty_stop) +{ + struct rocket_port *rp = (struct rocket_port *)port; + unsigned long flags; + + spin_lock_irqsave(&rp->port.lock, flags); + if (inb_p(rp->channel.TxRxCount)) { + rp->txctl &= ~TXCTL_ENABLE; + outl_p(rp->txctl, AIOP_INDEX(rp->aiop)); + } + spin_unlock_irqrestore(&rp->port.lock, flags); +} + +static void rocket_start_tx(struct uart_port *port, unsigned int tty_start) +{ + struct rocket_port *rp = (struct rocket_port *)port; + + rp->txctl |= TXCTL_ENABLE; + outl_p(rp->txctl, AIOP_INDEX(rp->aiop)); +} + +static void rocket_send_xchar(struct uart_port *port, char ch) +{ + struct rocket_port *rp = (struct rocket_port *)port; + + if (ch) { + if (inb_p(rp->channel.TxRxCount)) { + outw_p(TXP_CNT(rp->chan), AIOP_INDEX(rp->aiop)); + if (inb_p(AIOP_IDATA(rp->aiop)) & TXPCNT_PRIPEND) + /* FIXME: do we want to queue this normally? */ + return; + + outl_p(TXP_BUF(rp->chan) | ch << 16, AIOP_INDEX(rp->aiop)); + outl_p(TXP_CNT(rp->chan) | TXPCNT_PRIPEND << 16 | 1 << 16, + AIOP_INDEX(rp->aiop)); + } else { + outb_p(ch, rp->channel.TxRxData); + } + } +} + +static void rocket_stop_rx(struct uart_port *port) +{ + struct rocket_port *rp = (struct rocket_port *)port; + + rp->intmask &= ~INTID_RXFTRIG; +} + +static void rocket_enable_ms(struct uart_port *port) +{ + struct rocket_port *rp = (struct rocket_port *)port; + + rp->intmask |= INTID_DELTACD | INTID_DELTACTS | INTID_DELTADSR; +} + +static void rocket_receive(struct rocket_port *rp) +{ + struct tty_struct *tty = rp->port.info->tty; + unsigned int chars, status; + unsigned char *cbuf; + char *fbuf; + + /* + * word read of TxRxCount reveals received character count + */ + chars = inw_p(rp->channel.TxRxCount); + status = inw_p(rp->channel.ChanStat); + + /* + * determine how many we can actually read in. Note that + * we use the pair of flip buffers as one whole buffer. + */ + if (chars > 2*TTY_FLIPBUF_SIZE) + chars = 2*TTY_FLIPBUF_SIZE; + + cbuf = tty->flip.char_buf; + fbuf = tty->flip.flag_buf; + + /* + * Fast path - no errors. + */ + if (likely((status & (CSTAT_RXOVERFL | CSTAT_RXBREAK | + CSTAT_RXFRAME | CSTAT_RXPARITY | + CSTAT_STATMODE)) == 0)) { + unsigned int wrx = chars >> 1; + + if (wrx) { + insw(rp->channel.TxRxData, cbuf, wrx); + } + if (chars & 1) { + cbuf[chars - 1] = inb_p(rp->channel.TxRxData); + } + memset(fbuf, TTY_NORMAL, chars); + tty->ldisc.receive_buf(tty, cbuf, fbuf, chars); + return; + } + + /* + * We have an error, or are in status mode. + * A word in FIFO holds character and status. + */ + if ((status & CSTAT_STATMODE) == 0) + outw_p(CSTAT_STATMODE, rp->channel.ChanStat); + + /* + * Limit the number of characters to half. + */ + if (chars > TTY_FLIPBUF_SIZE) + chars = TTY_FLIPBUF_SIZE; + + while (chars) { + unsigned int word; + + word = inw_p(rp->channel.TxRxData) | DUMMY_RX_STATUS; + chars -= 1; + + *cbuf = word & 255; + *fbuf = TTY_NORMAL; + + if (word & (STM_BREAKH | STM_FRAMEH | STM_PARITYH | STM_RCVROVRH)) { + if (word & STM_BREAKH) { + word &= ~(STM_FRAMEH | STM_PARITYH); + rp->port.icount.brk++; + uart_handle_break(&rp->port); + } else if (word & STM_PARITYH) { + rp->port.icount.parity++; + } else if (word & STM_FRAMEH) { + rp->port.icount.frame++; + } + if (word & STM_RCVROVRH) + rp->port.icount.overrun++; + + word &= rp->port.read_status_mask; + + if (word & STM_BREAKH) + *fbuf = TTY_BREAK; + else if (word & STM_PARITYH) + *fbuf = TTY_PARITY; + else if (word & STM_FRAMEH) + *fbuf = TTY_OVERRUN; + } + + if ((word & rp->port.ignore_status_mask) == 0) { + cbuf++; + fbuf++; + } + if (word & STM_RCVROVRH) { + *cbuf++ = 0; + *fbuf++ = TTY_OVERRUN; + } + } + + /* + * If we've emptied the FIFO in status mode, turn status + * mode back off. + * I think this should be unconditional... --rmk + */ + if (inw_p(rp->channel.TxRxCount) == 0) + outw_p(0, rp->channel.ChanStat); + + tty->ldisc.receive_buf(tty, tty->flip.char_buf, tty->flip.flag_buf, + cbuf - tty->flip.char_buf); +} + +static void rocket_transmit(struct rocket_port *rp) +{ + struct circ_buf *xmit = &rp->port.info->xmit; + int chars = uart_circ_chars_pending(xmit); + int room = TXFIFO_SIZE; + + /* + * Byte read of TxRxCount reveals Tx count + */ + room -= inb_p(rp->channel.TxRxCount); + + if (chars > room) + chars = room; + + do { + int c; + if (uart_tx_stopped(&rp->port)) + break; + c = min(chars, UART_XMIT_SIZE - xmit->tail); + if (c <= 0) + break; + outsw(rp->channel.TxRxData, xmit->buf + xmit->tail, c / 2); + if (c & 1) { + outb_p(xmit->buf[xmit->tail + c - 1], rp->channel.TxRxData); + } + xmit->tail = (xmit->tail + c) & (UART_XMIT_SIZE - 1); + rp->port.icount.tx += c; + chars -= c; + } while (chars); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&rp->port); + + if (uart_circ_empty(xmit)) + rp->xmit = 0; +} + +static void rocket_handle_port(struct rocket_port *rp) +{ + unsigned int mask, status; + + mask = inb_p(rp->channel.IntID) & rp->intmask; + + if (mask & INTID_RXFTRIG) { + /* + * Rx FIFO trigger level + */ + rocket_receive(rp); + } + + if (mask & (INTID_DELTACD | INTID_DELTACTS | INTID_DELTADSR)) { + status = inw_p(rp->channel.ChanStat); + if (mask & INTID_DELTACD) + uart_handle_dcd_change(&rp->port, status & CSTAT_CDACT); + + if (mask & INTID_DELTACTS) + uart_handle_cts_change(&rp->port, status & CSTAT_CTSACT); + + if (mask & INTID_DELTADSR) + rp->port.icount.dsr++; + + wake_up_interruptible(&rp->port.info->delta_msr_wait); + } +} + +static inline void rocket_handle_aiop(struct rocket_aiop *aiop) +{ + unsigned int amask = inb_p(AIOP_INTCHAN(aiop)); + int i; + + for (i = 0; amask; amask >>= 1, i++) + if (amask & 1) + rocket_handle_port(aiop->port[i]); +} + +static inline void rocket_handle_board(struct rocket_board *board) +{ + unsigned int mask; + int i; + + if (board->pci) + mask = inw_p(PCI_INT_STATUS(board)); + else + mask = inb_p(board->MReg1IO); + + mask &= 15; + + for (i = 0; mask; mask >>= 1, i++) + if (mask & 1) + rocket_handle_aiop(board->aiop[i]); +} + +static void rocket_poll(unsigned long dummy) +{ + struct list_head *l; + + list_for_each(l, &rocket_boards) { + struct rocket_board *board = list_entry(l, struct rocket_board, all); + + rocket_handle_board(board); + } + + mod_timer(&rocket_timer, jiffies + 1); +} + +static unsigned int rocket_tx_empty(struct uart_port *port) +{ + struct rocket_port *rp = (struct rocket_port *)port; + +#define DRAINED (CSTAT_TXFIFOEMT | CSTAT_TXSHREMT) + + return (inb_p(rp->channel.TxRxCount) != 0 || + (inw_p(rp->channel.ChanStat) & DRAINED) != 0) + ? 0 : TIOCSER_TEMT; +} + +static unsigned int rocket_get_mctrl(struct uart_port *port) +{ + struct rocket_port *rp = (struct rocket_port *)port; + unsigned int result = 0; + unsigned int status; + + status = inw_p(rp->channel.ChanStat); + if (status & CSTAT_CDACT) + result |= TIOCM_CAR; + if (status & CSTAT_DSRACT) + result |= TIOCM_DSR; + if (status & CSTAT_CTSACT) + result |= TIOCM_CTS; + + return result; +} + +static void rocket_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + struct rocket_port *rp = (struct rocket_port *)port; + + if (mctrl & TIOCM_RTS) + rp->txctl |= TXCTL_SETRTS; + else + rp->txctl &= ~TXCTL_SETRTS; + + if (mctrl & TIOCM_DTR) + rp->txctl |= TXCTL_SETDTR; + else + rp->txctl &= ~TXCTL_SETDTR; + + outl_p(rp->txctl, AIOP_INDEX(rp->aiop)); +} + +static void rocket_break_ctl(struct uart_port *port, int break_state) +{ + struct rocket_port *rp = (struct rocket_port *)port; + unsigned long flags; + + spin_lock_irqsave(&rp->port.lock, flags); + if (break_state == -1) + rp->txctl |= TXCTL_SETBREAK; + else + rp->txctl &= ~TXCTL_SETBREAK; + + outl_p(rp->txctl, AIOP_INDEX(rp->aiop)); + spin_unlock_irqrestore(&rp->port.lock, flags); +} + +static int rocket_startup(struct uart_port *port) +{ + struct rocket_port *rp = (struct rocket_port *)port; + unsigned int mask; + + rp->rxctl = (rp->rxctl & ~RXCTL_TRIGMASK) | RXCTL_TRIG1; + outl_p(rp->rxctl, AIOP_INDEX(rp->aiop)); + + if (inw_p(rp->channel.ChanStat) & CSTAT_CDACT) + rp->cd_status = 1; + else + rp->cd_status = 0; + + outw_p(0, rp->channel.ChanStat); + rocket_flush_fifos(rp); + + rp->rxctl |= RXCTL_MCINTEN | RXCTL_RXINTEN | RXCTL_SRCINTEN; + rp->txctl |= TXCTL_TXINTEN; + outl_p(rp->rxctl, AIOP_INDEX(rp->aiop)); + outl_p(rp->txctl, AIOP_INDEX(rp->aiop)); + + /* + * enable channel interrupt + */ + mask = inb_p(AIOP_INTMASK(rp->aiop)); + outb_p(mask | 1 << rp->chan, AIOP_INTMASK(rp->aiop)); + + rp->rxctl = (rp->rxctl & ~RXCTL_TRIGMASK) | RXCTL_TRIG1; + outl_p(rp->rxctl, AIOP_INDEX(rp->aiop)); + + inw_p(rp->channel.ChanStat); + outw_p(0, rp->channel.ChanStat); + rocket_clear_xoff(rp); + + rp->txctl &= ~TXCTL_CTSFCEN; + sDisTxSoftFlowCtl(&rp->channel); + + sEnRxFIFO(&rp->channel); + + rp->txctl |= TXCTL_ENABLE; + outl_p(rp->txctl, AIOP_INDEX(rp->aiop)); + + rp->intmask = INTID_RXFTRIG | INTID_TXFIFOEMT | INTID_SRCINT; + + /* + * Set the timer to fire off immediately + */ + mod_timer(&rocket_timer, jiffies + 1); + + return 0; +} + +static void rocket_shutdown(struct uart_port *port) +{ + struct rocket_port *rp = (struct rocket_port *)port; + unsigned int mask; + + rp->rxctl |= RXCTL_MCINTEN | RXCTL_RXINTEN | RXCTL_SRCINTEN; + rp->txctl |= TXCTL_TXINTEN; + outl_p(rp->rxctl, AIOP_INDEX(rp->aiop)); + outl_p(rp->txctl, AIOP_INDEX(rp->aiop)); + + /* + * disable channel interrupt + */ + mask = inb_p(AIOP_INTMASK(rp->aiop)); + outb_p(mask & ~(1 << rp->chan), AIOP_INTMASK(rp->aiop)); + + rp->txctl &= ~TXCTL_CTSFCEN; + sDisTxSoftFlowCtl(&rp->channel); + + rocket_clear_xoff(rp); + rocket_flush_fifos(rp); + + rp->txctl &= ~TXCTL_SETBREAK; + outl_p(rp->txctl, AIOP_INDEX(rp->aiop)); +} + +static void +rocket_settermios(struct uart_port *port, struct termios *termios, + struct termios *old_termios) +{ + struct rocket_port *rp = (struct rocket_port *)port; + unsigned long flags; + unsigned int quot; + + switch (termios->c_cflag & CSIZE) { + case CS7: + rp->txctl &= ~TXCTL_DATA8BIT; + break; + + case CS5: + case CS6: + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= CS8; + default: + rp->txctl |= TXCTL_DATA8BIT; + break; + } + + if (termios->c_cflag & CSTOPB) { + rp->txctl |= TXCTL_STOP2; + } else { + rp->txctl &= ~TXCTL_STOP2; + } + + if (termios->c_cflag & PARENB) { + rp->txctl |= TXCTL_PARITYEN; + if (termios->c_cflag & PARODD) { + rp->txctl &= ~TXCTL_EVENPAR; + } else { + rp->txctl |= TXCTL_EVENPAR; + } + } else { + rp->txctl &= ~TXCTL_PARITYEN; + } + + if (termios->c_cflag & CRTSCTS) { + rp->intmask |= INTID_DELTACTS; + rp->txctl |= TXCTL_CTSFCEN; + } else { + rp->intmask &= ~INTID_DELTACTS; + rp->txctl &= ~TXCTL_CTSFCEN; + } + + /* + * Ask the core to calculate the divisor for us. + */ + quot = uart_get_divisor(port, termios, old_termios); + uart_update_timeout(port, termios->c_cflag, quot); + + quot -= 1; + outl_p(rp->txctl, AIOP_INDEX(rp->aiop)); + outl_p(BAUD(rp->chan) | (quot << 16), AIOP_INDEX(rp->aiop)); + + if (termios->c_cflag & CLOCAL) { + rp->intmask &= ~INTID_DELTACD; + } else { + spin_lock_irqsave(&rp->port.lock, flags); + if (inw_p(rp->channel.ChanStat) & CSTAT_CDACT) + rp->cd_status = 1; + else + rp->cd_status = 0; + rp->intmask |= INTID_DELTACD; + spin_unlock_irqrestore(&rp->port.lock, flags); + } + +#ifdef ROCKET_SOFT_FLOW + /* + * Handle software flow control in the board + */ + if (termios->c_iflag & IXON) { + if (termios->c_iflag & IXANY) { + sEnIXANY(&rp->channel); + } else { + sDisIXANY(&rp->channel); + } + sSetTxXONChar(&rp->channel, termios->c_cc[VSTART]); + sSetTxXOFFChar(&rp->channel, termios->c_cc[VSTOP]); + sEnTxSoftFlowCtl(&rp->channel); + } else { + sDisTxSoftFlowCtl(&rp->channel); + sDisIXANY(&rp->channel); + rocket_clear_xoff(rp); + } +#endif + + /* + * Set up ignore/read mask words + */ + rp->port.read_status_mask = STM_RCVROVRH | 0xFF; + if (termios->c_iflag & INPCK) + rp->port.read_status_mask |= STM_FRAMEH | STM_PARITYH; + if (termios->c_iflag & (BRKINT | PARMRK)) + rp->port.read_status_mask |= STM_BREAKH; + + /* + * Characters to ignore + */ + rp->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + rp->port.ignore_status_mask |= STM_FRAMEH | STM_PARITYH; + if (termios->c_iflag & IGNBRK) { + rp->port.ignore_status_mask |= STM_BREAKH; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + rp->port.ignore_status_mask |= STM_RCVROVRH; + } + + /* + * Ignore all characters if CREAD is not set. + */ + if ((termios->c_cflag & CREAD) == 0) + rp->port.ignore_status_mask |= DUMMY_RX_STATUS; +} + +static const char *rocket_type(struct uart_port *port) +{ + return port->type == PORT_ROCKET ? "ROCKET" : NULL; +} + +/* + * Release the memory region(s) being used by 'port' + */ +static void rocket_release_port(struct uart_port *port) +{ +} + +/* + * Request the memory region(s) being used by 'port' + */ +static int rocket_request_port(struct uart_port *port) +{ + return 0; +} + +/* + * Configure/autoconfigure the port. + */ +static void rocket_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) + port->type = PORT_ROCKET; +} + +/* + * verify the new serial_struct (for TIOCSSERIAL). + */ +static int rocket_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + return -EINVAL; +} + +static int rocket_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg) +{ + return -ENOIOCTLCMD; +} + +static struct uart_ops rocket_pops = { + tx_empty: rocket_tx_empty, + set_mctrl: rocket_set_mctrl, + get_mctrl: rocket_get_mctrl, + stop_tx: rocket_stop_tx, + start_tx: rocket_start_tx, + send_xchar: rocket_send_xchar, + stop_rx: rocket_stop_rx, + enable_ms: rocket_enable_ms, + break_ctl: rocket_break_ctl, + startup: rocket_startup, + shutdown: rocket_shutdown, + settermios: rocket_settermios, + type: rocket_type, + release_port: rocket_release_port, + request_port: rocket_request_port, + config_port: rocket_config_port, + verify_port: rocket_verify_port, + ioctl: rocket_ioctl, +}; + +static struct uart_driver rocket_reg = { + owner: THIS_MODULE, + driver_name: "ttyR", + major: TTY_ROCKET_MAJOR, +#ifdef CONFIG_DEVFS_FS + dev_name: "tts/R%d", +#else + dev_name: "ttyR", +#endif + minor: 0, + nr: MAX_RP_PORTS, +}; + +static struct rocket_port *rocket_ports[MAX_RP_PORTS]; + +static int __devinit rocket_init_one_port(struct rocket_aiop *aiop, int chan) +{ + struct rocket_port *rp; + unsigned int line = aiop->board->nr | aiop->nr | chan; + int ret; + + rp = kmalloc(sizeof(struct rocket_port), GFP_KERNEL); + if (!rp) { + printk("Couldn't allocate port struct for line ttyR%u\n", line); + return -ENOMEM; + } + + memset(rp, 0, sizeof(struct rocket_port)); + + aiop->port[chan] = rp; + + rp->port.line = line; + rp->port.uartclk = 460800 * 16; /*FIXME*/ + rp->port.fifosize = 1; + rp->port.ops = &rocket_pops; + rp->aiop = aiop; + rp->rxctl = TX_CTRL(chan); + rp->txctl = RX_CTRL(chan); + + rp->channel.TxRxData = CHAN_TDRD(aiop, chan); + rp->channel.TxRxCount = CHAN_FIFOCNT(aiop, chan); + rp->channel.ChanStat = CHAN_STAT(aiop, chan); + rp->channel.IntID = CHAN_INTID(aiop, chan); + + if (sInitChan(ctlp, &rp->channel, aiop, chan) == 0) { + kfree(rp); + return -ENODEV; + } + + ret = uart_add_one_port(&rocket_reg, &rp->port); + if (ret) { + aiop->port[chan] = NULL; + kfree(rp); + } + + return ret; +} + +static void __devexit rocket_remove_one_port(struct rocket_port *rp) +{ + uart_remove_one_port(&rocket_reg, &rp->port); + + kfree(rp); +} + +static int __devinit rocket_init_one_aiop(struct rocket_aiop *aiop) +{ + unsigned int tmp; + int i, ret, nr_chan = 4; + + /* + * Check to see if we have a valid AIOP. + */ + outb_p(AIOPC_RESETALL, AIOP_CMD(aiop)); + outb_p(0, AIOP_CMD(aiop)); + tmp = inb_p(CHAN_STAT(aiop, 0)) & 7; + if (tmp != 6) + return 0; + + /* + * Detect number of channels in the AIOP. It will be either + * 4 or 8. We read SRAM for channels 0 and 4 - if different, + * then we must have an 8 channel AIOP. + */ + outl_p(0x12340000, AIOP_INDEX(aiop)); + outw_p(0, AIOP_INDEX(aiop)); + tmp = inw_p(AIOP_IDATA(aiop)); + outw_p(0x4000, AIOP_INDEX(aiop)); + if (tmp != inw_p(AIOP_IDATA(aiop))) + nr_chan = 8; + + for (i = 0; i < nr_chan; i++) { + ret = rocket_init_one_port(aiop, i); + if (ret) + break; + } + return ret; +} + +static void __devexit rocket_remove_one_aiop(struct rocket_aiop *aiop) +{ + int i; + + for (i = 0; i < 8; i++) + if (aiop->port[i]) { + rocket_remove_one_port(aiop->port[i]); + aiop->port[i] = NULL; + } + + kfree(aiop); +} + +#ifdef CONFIG_PCI +static int __devinit +rocket_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct rocket_board *board; + struct rocket_aiop *aiop; + unsigned int ioaddr; + int ret, i; + + ret = pci_enable_device(dev); + if (ret) + return ret; + + ioaddr = pci_resource_start(dev, 0); + + board = kmalloc(sizeof(struct rocket_board) + + sizeof(struct rocket_aiop) * id->nr, GFP_KERNEL); + if (!board) + return -ENOMEM; + + memset(board, 0, sizeof(struct rocket_board) + + sizeof(struct rocket_aiop) * id->nr); + + INIT_LIST_HEAD(&board->all); + + pci_set_drvdata(dev, board); + + board->base = ioaddr; + board->pci = dev; + + for (i = 0; i < id->nr; i++) { + board->aiop = aiop; + + aiop->nr = i; + aiop->board = board; + aiop->base = ioaddr + i * 64; + + ret = rocket_init_one_aiop(aiop); + if (ret) + break; + + aiop++; + } + + list_add_tail(&board->all, &rocket_boards); +} + +static void __devexit rocket_pci_remove(struct pci_dev *dev) +{ + struct rocket_board *board = pci_get_drvdata(dev); + int i; + + list_del(&board->all); + + pci_set_drvdata(dev, NULL); + + for (i = 0; i < 4; i++) { + if (board->aiop[i]) + rocket_remove_one_aiop(board->aiop[i]); + } + kfree(board); + + pci_disable_device(dev); +} + +#define RP_PCI_DEV(dev, aiops) \ + { PCI_VENDOR_ID_RP, dev, PCI_ANY_ID, PCI_ANY_ID, 0, 0, aiops } + +static struct pci_device_id rocket_pci_ids[] = { + RP_PCI_DEV(PCI_DEVICE_ID_RP32INTF, 4), + RP_PCI_DEV(PCI_DEVICE_ID_RP8INTF, 1), + RP_PCI_DEV(PCI_DEVICE_ID_RP16INTF, 2), + RP_PCI_DEV(PCI_DEVICE_ID_RP8OCTA, 1), + RP_PCI_DEV(PCI_DEVICE_ID_RP4QUAD, 1), + RP_PCI_DEV(PCI_DEVICE_ID_RP8J, 1), + RP_PCI_DEV(PCI_DEVICE_ID_RPP4, 1), + RP_PCI_DEV(PCI_DEVICE_ID_RPP8, 1), + RP_PCI_DEV(PCI_DEVICE_ID_RP8M, 1), +}; + +static struct pci_driver rocket_pci_driver { + name: "rocket", + id_table: &rocket_pci_ids, + probe: rocket_pci_probe, + remove: rocket_pci_remove, +}; + +MODULE_DEVICE_TABLE(pci, rocket_pci_ids); +#endif + +static int __init rocket_init(void) +{ + int ret; + + init_timer(&rocket_timer); + rocket_timer.function = rocket_poll; + + ret = uart_register_driver(&rocket_reg); + if (ret == 0) { +#ifdef CONFIG_PCI + ret = pci_module_init(&rocket_pci_driver); +#endif + } + return ret; +} + +static void __exit rocket_exit(void) +{ + int i; + + del_timer_sync(&rocket_timer); + +#ifdef CONFIG_PCI + pci_unregister_driver(&rocket_pci_driver); +#endif + + for (i = 0; i < MAX_RP_PORTS; i++) + if (rocket_ports[i]) + uart_remove_one_port(&rocket_reg, + &rocket_ports[i]->port); + + uart_unregister_driver(&rocket_reg); +} + +module_init(rocket_init); +module_exit(rocket_exit); + +EXPORT_NO_SYMBOLS; + +MODULE_AUTHOR("Comtrol/Ted T'so/Russell King"); +MODULE_DESCRIPTION("Comtrol Rocketport driver"); +MODULE_LICENSE("GPL"); diff -urN orig/drivers/serial/sa1100.c linux/drivers/serial/sa1100.c --- orig/drivers/serial/sa1100.c Tue May 27 10:05:13 2003 +++ linux/drivers/serial/sa1100.c Wed Jun 11 20:47:40 2003 @@ -398,7 +398,7 @@ * Allocate the IRQ */ retval = request_irq(sport->port.irq, sa1100_int, 0, - "serial_sa1100", sport); + "sa11x0-uart", sport); if (retval) return retval; @@ -448,6 +448,15 @@ unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8; /* + * If we don't support modem control lines, don't allow + * these to be set. + */ + if (0) { + termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR); + termios->c_cflag |= CLOCAL; + } + + /* * We only support CS7 and CS8. */ while ((termios->c_cflag & CSIZE) != CS7 && @@ -568,7 +577,7 @@ struct sa1100_port *sport = (struct sa1100_port *)port; return request_mem_region(sport->port.mapbase, UART_PORT_SIZE, - "serial_sa1100") != NULL ? 0 : -EBUSY; + "sa11x0-uart") != NULL ? 0 : -EBUSY; } /* @@ -858,71 +867,89 @@ .cons = SA1100_CONSOLE, }; -static int sa1100_serial_suspend(struct device *dev, u32 state, u32 level) +static int sa1100_serial_suspend(struct device *_dev, u32 state, u32 level) { - int i; + struct sa1100_port *sport = dev_get_drvdata(_dev); - for (i = 0; i < NR_PORTS; i++) - uart_suspend_port(&sa1100_reg, &sa1100_ports[i].port, level); + if (sport) + uart_suspend_port(&sa1100_reg, &sport->port, level); return 0; } -static int sa1100_serial_resume(struct device *dev, u32 level) +static int sa1100_serial_resume(struct device *_dev, u32 level) { + struct sa1100_port *sport = dev_get_drvdata(_dev); + + if (sport) + uart_resume_port(&sa1100_reg, &sport->port, level); + + return 0; +} + +static int sa1100_serial_probe(struct device *_dev) +{ + struct platform_device *dev = to_platform_device(_dev); + struct resource *res = dev->resource; int i; - for (i = 0; i < NR_PORTS; i++) - uart_resume_port(&sa1100_reg, &sa1100_ports[i].port, level); + for (i = 0; i < dev->num_resources; i++, res++) + if (res->flags & IORESOURCE_MEM) + break; + + if (i < dev->num_resources) { + for (i = 0; i < NR_PORTS; i++) { + if (sa1100_ports[i].port.mapbase != res->start) + continue; + + uart_add_one_port(&sa1100_reg, &sa1100_ports[i].port); + } + } + + return 0; +} + +static int sa1100_serial_remove(struct device *_dev) +{ + struct sa1100_port *sport = dev_get_drvdata(_dev); + + dev_set_drvdata(_dev, NULL); + + if (sport) + uart_remove_one_port(&sa1100_reg, &sport->port); return 0; } static struct device_driver sa11x0_serial_driver = { - .name = "sa11x0_serial", - .bus = &system_bus_type, + .name = "sa11x0-uart", + .bus = &platform_bus_type, + .probe = sa1100_serial_probe, + .remove = sa1100_serial_remove, .suspend = sa1100_serial_suspend, .resume = sa1100_serial_resume, }; -/* - * This "device" covers _all_ ISA 8250-compatible serial devices. - */ -static struct sys_device sa11x0_serial_devs = { - .name = "sa11x0_serial", - .id = 0, - .dev = { - .driver = &sa11x0_serial_driver, - }, -}; - static int __init sa1100_serial_init(void) { int ret; printk(KERN_INFO "Serial: SA11x0 driver $Revision: 1.50 $\n"); - driver_register(&sa11x0_serial_driver); - sys_device_register(&sa11x0_serial_devs); - sa1100_init_ports(); + ret = uart_register_driver(&sa1100_reg); if (ret == 0) { - int i; - - for (i = 0; i < NR_PORTS; i++) - uart_add_one_port(&sa1100_reg, &sa1100_ports[i].port); + ret = driver_register(&sa11x0_serial_driver); + if (ret) + uart_unregister_driver(&sa1100_reg); } return ret; } static void __exit sa1100_serial_exit(void) { - int i; - - for (i = 0; i < NR_PORTS; i++) - uart_remove_one_port(&sa1100_reg, &sa1100_ports[i].port); - + driver_unregister(&sa11x0_serial_driver); uart_unregister_driver(&sa1100_reg); } diff -urN orig/drivers/video/Kconfig linux/drivers/video/Kconfig --- orig/drivers/video/Kconfig Mon Mar 24 23:47:22 2003 +++ linux/drivers/video/Kconfig Sat May 31 11:33:44 2003 @@ -94,6 +94,8 @@ bool "Anakin LCD support" depends on FB && ARM && ARCH_ANAKIN +source drivers/video/arm/Kconfig + config FB_CLPS711X bool "CLPS711X LCD support" depends on FB && ARM && ARCH_CLPS711X diff -urN orig/drivers/video/Makefile linux/drivers/video/Makefile --- orig/drivers/video/Makefile Sat Apr 12 14:46:59 2003 +++ linux/drivers/video/Makefile Sat May 31 14:42:59 2003 @@ -13,6 +13,9 @@ obj-$(CONFIG_PPC) += macmodes.o endif +obj-$(CONFIG_ARM) += arm/ +obj-$(CONFIG_FB_ARMCLCD) += cfbfillrect.o cfbcopyarea.o cfbimgblt.o + obj-$(CONFIG_FB_ACORN) += acornfb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o obj-$(CONFIG_FB_AMIGA) += amifb.o c2p.o obj-$(CONFIG_FB_PM2) += pm2fb.o diff -urN orig/drivers/video/arm/Kconfig linux/drivers/video/arm/Kconfig --- orig/drivers/video/arm/Kconfig Thu Jan 1 01:00:00 1970 +++ linux/drivers/video/arm/Kconfig Fri Jun 6 22:43:09 2003 @@ -0,0 +1,26 @@ +config FB_ARMCLCD + tristate "ARM PrimeCell PL110 CLCD support" + depends on FB && ARM_PRIMECELL + help + This framebuffer device driver is for the ARM PrimeCell PL110 + Colour LCD controller. ARM PrimeCells provide the building + blocks for System on a Chip devices. + + If you want to compile this as a module (=code which can be + inserted into and removed from the running kernel), say M + here and read . The module + will be called clcd-core. + +config FB_ARMIMPD1 + tristate + depends on FB_ARMCLCD && INTEGRATOR_IMPD1 + default m if FB_ARMCLCD=m || INTEGRATOR_IMPD1=m + default y if FB_ARMCLCD=y && INTEGRATOR_IMPD1=y + help + This provides the implementation specific parts for the PL110 + Colour LCD controller found on the Integrator/IM-PD1 module. + + If you want to compile this as a module (=code which can be + inserted into and removed from the running kernel), say M + here and read . The module + will be called clcd-impd1. diff -urN orig/drivers/video/arm/Makefile linux/drivers/video/arm/Makefile --- orig/drivers/video/arm/Makefile Thu Jan 1 01:00:00 1970 +++ linux/drivers/video/arm/Makefile Sat May 31 18:16:53 2003 @@ -0,0 +1,3 @@ +obj-$(CONFIG_FB_ARMCLCD) := clcd-core.o +obj-$(CONFIG_FB_ARMIMPD1) += clcd-impd1.o +clcd-n += cintegrator.o diff -urN orig/drivers/video/arm/cintegrator.c linux/drivers/video/arm/cintegrator.c --- orig/drivers/video/arm/cintegrator.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/video/arm/cintegrator.c Sat May 31 11:35:43 2003 @@ -0,0 +1,169 @@ +/* + * linux/drivers/video/arm/cintegrator.c -- Integrator LCD panel. + * + * David A Rusling + * + * Copyright (C) 2001 ARM Limited + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// Integrator CLCD definitions +#include "clcd.h" + +#define BITS_PER_PIXEL 16 + +static struct clcd_panel vga = { + .mode = { + .name = "VGA", + .refresh = 0, + .xres = 640, + .yres = 480, + .pixclock = 40000, + .left_margin = 20, /* 63 */ + .right_margin = 44, /* 31 */ + .upper_margin = 13, /* 11 */ + .lower_margin = 28, /* 9 */ + .hsync_len = 96, /* 63 */ + .vsync_len = 2, /* 24 */ + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED, + }, + .width = -1, + .height = -1, + .tim2 = TIM2_BCD | TIM2_IPC, + .cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1), +}; + +static void cintegrator_clcd_setvco(struct clcd_fb *fb, unsigned long pixclock) +{ +#warning FIXME: No CINTEGRATOR VCO control +} + +static struct clcd_board cintegrator_board = { + .setvco = cintegrator_clcd_setvco, +}; + +static LIST_HEAD(clcd); + +static unsigned long regsbase = CINTEGRATOR_CLCD_BASE; +static unsigned long framesize = SZ_4M; + +int __init cintclcdfb_init(void) +{ + struct clcd_fb *fb; + int ret; + + if (!request_mem_region(regsbase, SZ_4K, "clcd regs")) { + printk(KERN_ERR "clcd: unable to reserve regs region\n"); + ret = -EBUSY; + goto out; + } + + fb = (struct clcd_fb *) kmalloc(sizeof(struct clcd_fb), GFP_KERNEL); + if (!fb) { + printk(KERN_INFO "clcd: could not allocate new clcd_fb struct\n"); + ret = -ENOMEM; + goto free_1; + } + memset(fb, 0, sizeof (struct clcd_fb)); + + fb->regs = ioremap(regsbase, SZ_4K); + if (!fb->regs) { + printk(KERN_ERR "clcd: unable to remap registers\n"); + ret = -ENOMEM; + goto unmap; + } + + /* + * XXX FIXME: Doesn't work as a module. + */ + fb->fb.screen_base = alloc_bootmem_pages(framesize); + if (!fb->fb.screen_base) { + printk(KERN_ERR "clcd: unable to map framebuffer\n"); + ret = -ENOMEM; + goto unmap; + } + + printk(KERN_INFO "CIntegrator Colour LCD panel found\n"); + + fb->fb.fix.smem_start = virt_to_phys(fb->fb.screen_base); + fb->fb.fix.smem_len = framesize; + fb->fb.fix.mmio_start = regsbase; + fb->fb.fix.mmio_len = SZ_4K; + fb->panel = &vga; + fb->board = &cintegrator_board; + + fb->fb.var.bits_per_pixel = BITS_PER_PIXEL; + fb->fb.var.grayscale = 0; + clcdfb_setup(fb); + + /* Say what sort of panel we are assuming */ + printk(KERN_INFO "Using settings for %s display\n", + fb->panel->mode.name); + + ret = clcdfb_register(fb); + if (ret < 0) { + printk(KERN_ERR "CLCD: cannot register framebuffer (%d)\n", ret); + goto unmap; + } + + list_add(&fb->node, &clcd); + + return 0; + + unmap: + /* XXX FIXME: free memory allocated for fb->fb.screenbase */ + if (fb->regs) + iounmap(fb->regs); + kfree(fb); + free_1: + release_mem_region(regsbase, SZ_1K); + out: + return ret; +} + +#ifdef MODULE +module_init(cintclcdfb_init); +#else +int __init cintclcdfb_setup(char *options) +{ + return 0; +} +#endif + +static void __exit cintclcdfb_exit(void) +{ + struct list_head *l, *n; + + list_for_each_safe(l, n, &clcd) { + struct clcd_fb *fb = list_entry(l, struct clcd_fb, node); + + clcdfb_unregister(fb); + /* XXX FIXME: free memory allocated for fb->fb.screenbase */ + iounmap(fb->regs); + release_mem_region(fb->fb.fix.mmio_start, fb->fb.fix.mmio_len); + list_del(&fb->node); + kfree(fb); + } +} + +module_exit(cintclcdfb_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("CIntegrator Colour LCD framebuffer driver"); diff -urN orig/drivers/video/arm/clcd-core.c linux/drivers/video/arm/clcd-core.c --- orig/drivers/video/arm/clcd-core.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/video/arm/clcd-core.c Fri Jun 6 23:31:41 2003 @@ -0,0 +1,359 @@ +/* + * linux/drivers/video/arm/clcd-core.c + * + * Copyright (C) 2001 ARM Limited, by David A Rusling + * Updated to 2.5, Deep Blue Solutions Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + * + * ARM PrimeCell PL110 Color LCD Controller + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clcd.h" + +#define to_clcd(info) container_of(info, struct clcd_fb, fb) + +/* This is limited to 16 characters when displayed by X startup */ +static const char *clcd_name = "CLCD FB"; + +static inline void clcdfb_sleep(unsigned int ms) +{ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((ms * HZ + 999) / 1000); +} + +static inline void clcdfb_set_start(struct clcd_fb *fb) +{ + unsigned long ustart = fb->fb.fix.smem_start; + unsigned long lstart; + + ustart += fb->fb.var.yoffset * fb->fb.fix.line_length; + lstart = ustart + fb->fb.var.yres * fb->fb.fix.line_length / 2; + + writel(ustart, fb->regs + CLCD_UBAS); + writel(lstart, fb->regs + CLCD_LBAS); +} + +static void clcdfb_disable(struct clcd_fb *fb) +{ + u32 val; + + if (fb->board->disable) + fb->board->disable(fb); + + val = readl(fb->regs + CLCD_CNTL); + if (val & CNTL_LCDPWR) { + val &= ~CNTL_LCDPWR; + writel(val, fb->regs + CLCD_CNTL); + + clcdfb_sleep(20); + } + if (val & CNTL_LCDEN) { + val &= ~CNTL_LCDEN; + writel(val, fb->regs + CLCD_CNTL); + } +} + +static void clcdfb_enable(struct clcd_fb *fb, u32 cntl) +{ + /* + * Bring up by first enabling.. + */ + cntl |= CNTL_LCDEN; + writel(cntl, fb->regs + CLCD_CNTL); + + clcdfb_sleep(20); + + /* + * and now apply power. + */ + cntl |= CNTL_LCDPWR; + writel(cntl, fb->regs + CLCD_CNTL); + + /* + * finally, enable the interface. + */ + if (fb->board->enable) + fb->board->enable(fb); +} + +static void clcdfb_program(struct clcd_fb *fb, struct clcd_regs *regs) +{ + clcdfb_disable(fb); + + writel(regs->tim0, fb->regs + CLCD_TIM0); + writel(regs->tim1, fb->regs + CLCD_TIM1); + writel(regs->tim2, fb->regs + CLCD_TIM2); + writel(regs->tim3, fb->regs + CLCD_TIM3); + + clcdfb_set_start(fb); + + if (fb->board->setvco) + fb->board->setvco(fb, regs->pixclock); + + fb->clcd_cntl = regs->cntl; + + clcdfb_enable(fb, regs->cntl); + +#ifdef DEBUG + printk(KERN_INFO "CLCD: Registers set to\n" + KERN_INFO " %08x %08x %08x %08x\n" + KERN_INFO " %08x %08x %08x %08x\n", + readl(fb->regs + CLCD_TIM0), readl(fb->regs + CLCD_TIM1), + readl(fb->regs + CLCD_TIM2), readl(fb->regs + CLCD_TIM3), + readl(fb->regs + CLCD_UBAS), readl(fb->regs + CLCD_LBAS), + readl(fb->regs + CLCD_IENB), readl(fb->regs + CLCD_CNTL)); +#endif +} + +static int +clcdfb_set_bitfields(struct clcd_fb *fb, struct fb_var_screeninfo *var) +{ + int ret = 0; + + memset(&var->transp, 0, sizeof(var->transp)); + memset(&var->red, 0, sizeof(var->red)); + memset(&var->green, 0, sizeof(var->green)); + memset(&var->blue, 0, sizeof(var->blue)); + + switch (var->bits_per_pixel) { + case 1: + case 2: + case 4: + case 8: + var->red.length = 8; + var->red.offset = 0; + var->green.length = 8; + var->green.offset = 0; + var->blue.length = 8; + var->blue.offset = 0; + break; + case 16: + var->red.length = 5; + var->green.length = 5; + var->blue.length = 5; + if (fb->panel->cntl & CNTL_BGR) { + var->red.offset = 10; + var->green.offset = 5; + var->blue.offset = 0; + } else { + var->red.offset = 0; + var->green.offset = 5; + var->blue.offset = 10; + } + break; + case 24: + if (fb->panel->cntl & CNTL_LCDTFT) { + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + + if (fb->panel->cntl & CNTL_BGR) { + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + } else { + var->red.offset = 0; + var->green.offset = 8; + var->blue.offset = 16; + } + break; + } + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int clcdfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct clcd_fb *fb = to_clcd(info); + int ret = -EINVAL; + + if (fb->board->check) + ret = fb->board->check(fb, var); + if (ret == 0) + ret = clcdfb_set_bitfields(fb, var); + + return ret; +} + +static int clcdfb_set_par(struct fb_info *info) +{ + struct clcd_fb *fb = to_clcd(info); + struct clcd_regs regs; + + fb->fb.fix.line_length = fb->fb.var.xres_virtual * + fb->fb.var.bits_per_pixel / 8; + + if (fb->fb.var.bits_per_pixel <= 8) + fb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; + else + fb->fb.fix.visual = FB_VISUAL_TRUECOLOR; + + fb->board->decode(fb, ®s); + clcdfb_program(fb, ®s); + + return 0; +} + +static inline u32 convert_bitfield(int val, struct fb_bitfield *bf) +{ + unsigned int mask = (1 << bf->length) - 1; + + return (val >> (16 - bf->length) & mask) << bf->offset; +} + +/* + * Set a single color register. The values supplied have a 16 bit + * magnitude. Return != 0 for invalid regno. + */ +static int +clcdfb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, + unsigned int blue, unsigned int transp, struct fb_info *info) +{ + struct clcd_fb *fb = to_clcd(info); + + if (regno < 16) + fb->cmap[regno] = convert_bitfield(transp, &fb->fb.var.transp) | + convert_bitfield(blue, &fb->fb.var.blue) | + convert_bitfield(green, &fb->fb.var.green) | + convert_bitfield(red, &fb->fb.var.red); + + if (fb->fb.var.bits_per_pixel == 8 && regno < 256) { + int hw_reg = CLCD_PALETTE + ((regno * 2) & ~3); + u32 val, mask, newval; + + newval = (red >> 11) & 0x001f; + newval |= (green >> 6) & 0x03e0; + newval |= (blue >> 1) & 0x7c00; + + /* + * 3.2.11: if we're configured for big endian + * byte order, the palette entries are swapped. + */ + if (fb->clcd_cntl & CNTL_BEBO) + regno ^= 1; + + if (regno & 1) { + newval <<= 16; + mask = 0x0000ffff; + } else { + mask = 0xffff0000; + } + + val = readl(fb->regs + hw_reg) & mask; + writel(val | newval, fb->regs + hw_reg); + } + + return regno > 255; +} + +/* + * Blank the screen if blank_mode != 0, else unblank. If blank == NULL + * then the caller blanks by setting the CLUT (Color Look Up Table) to all + * black. Return 0 if blanking succeeded, != 0 if un-/blanking failed due + * to e.g. a video mode which doesn't support it. Implements VESA suspend + * and powerdown modes on hardware that supports disabling hsync/vsync: + * blank_mode == 2: suspend vsync + * blank_mode == 3: suspend hsync + * blank_mode == 4: powerdown + */ +static int clcdfb_blank(int blank_mode, struct fb_info *info) +{ + struct clcd_fb *fb = to_clcd(info); + int ret = -EINVAL; + + if (fb->board->blank) + ret = fb->board->blank(fb, blank_mode); + + return ret; +} + +static struct fb_ops clcdfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = clcdfb_check_var, + .fb_set_par = clcdfb_set_par, + .fb_setcolreg = clcdfb_setcolreg, + .fb_blank = clcdfb_blank, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_cursor = soft_cursor, +}; + +void clcdfb_setup(struct clcd_fb *fb) +{ + fb->fb.fbops = &clcdfb_ops; + fb->fb.flags = FBINFO_FLAG_DEFAULT; + fb->fb.pseudo_palette = fb->cmap; + + strncpy(fb->fb.fix.id, clcd_name, sizeof(fb->fb.fix.id)); + fb->fb.fix.type = FB_TYPE_PACKED_PIXELS; + fb->fb.fix.type_aux = 0; + fb->fb.fix.xpanstep = 0; + fb->fb.fix.ypanstep = 0; + fb->fb.fix.ywrapstep = 0; + fb->fb.fix.accel = FB_ACCEL_NONE; + + fb->fb.var.xres = fb->panel->mode.xres; + fb->fb.var.yres = fb->panel->mode.yres; + fb->fb.var.xres_virtual = fb->panel->mode.xres; + fb->fb.var.yres_virtual = fb->panel->mode.yres; + fb->fb.var.pixclock = fb->panel->mode.pixclock; + fb->fb.var.left_margin = fb->panel->mode.left_margin; + fb->fb.var.right_margin = fb->panel->mode.right_margin; + fb->fb.var.upper_margin = fb->panel->mode.upper_margin; + fb->fb.var.lower_margin = fb->panel->mode.lower_margin; + fb->fb.var.hsync_len = fb->panel->mode.hsync_len; + fb->fb.var.vsync_len = fb->panel->mode.vsync_len; + fb->fb.var.sync = fb->panel->mode.sync; + fb->fb.var.vmode = fb->panel->mode.vmode; + fb->fb.var.activate = FB_ACTIVATE_NOW; + fb->fb.var.nonstd = 0; + fb->fb.var.height = fb->panel->height; + fb->fb.var.width = fb->panel->width; + fb->fb.var.accel_flags = 0; + + /* + * Make sure that the bitfields are set appropriately. + */ + clcdfb_set_bitfields(fb, &fb->fb.var); +} + +int clcdfb_register(struct clcd_fb *fb) +{ + /* + * Ensure interrupts are disabled. + */ + writel(0, fb->regs + CLCD_IENB); + + return register_framebuffer(&fb->fb); +} + +void clcdfb_unregister(struct clcd_fb *fb) +{ + clcdfb_disable(fb); + unregister_framebuffer(&fb->fb); +} + +EXPORT_SYMBOL(clcdfb_setup); +EXPORT_SYMBOL(clcdfb_register); +EXPORT_SYMBOL(clcdfb_unregister); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ARM PrimeCell PL110 CLCD core driver"); diff -urN orig/drivers/video/arm/clcd-impd1.c linux/drivers/video/arm/clcd-impd1.c --- orig/drivers/video/arm/clcd-impd1.c Thu Jan 1 01:00:00 1970 +++ linux/drivers/video/arm/clcd-impd1.c Fri Jun 6 22:24:32 2003 @@ -0,0 +1,367 @@ +/* + * linux/drivers/video/arm/clcd-impd1.c -- Integrator LCD panel. + * + * David A Rusling + * + * Copyright (C) 2001 ARM Limited + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define PANEL PROSPECTOR +#define BITS_PER_PIXEL 16 + +#include "clcd.h" +#include "clcd-impd1.h" + +#if PANEL == VGA +#define PANELTYPE vga +static struct clcd_panel vga = { + .mode = { + .name = "VGA", + .refresh = 60, + .xres = 640, + .yres = 480, + .pixclock = 39721, + .left_margin = 40, + .right_margin = 24, + .upper_margin = 32, + .lower_margin = 11, + .hsync_len = 96, + .vsync_len = 2, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED, + }, + .width = -1, + .height = -1, + .tim2 = TIM2_BCD | TIM2_IPC, + .cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1), + .connector = IMPD1_CTRL_DISP_VGA, +}; + +#elif PANEL == SVGA +#define PANELTYPE svga +static struct clcd_panel svga = { + .mode = { + .name = "SVGA", + .refresh = 0, + .xres = 800, + .yres = 600, + .pixclock = 27778, + .left_margin = 20, + .right_margin = 20, + .upper_margin = 5, + .lower_margin = 5, + .hsync_len = 164, + .vsync_len = 62, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED, + }, + .width = -1, + .height = -1, + .tim2 = TIM2_BCD, + .cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1), + .connector = IMPD1_CTRL_DISP_VGA, +}; + +#elif PANEL == PROSPECTOR +#define PANELTYPE prospector +static struct clcd_panel prospector = { + .mode = { + .name = "PROSPECTOR", + .refresh = 0, + .xres = 640, + .yres = 480, + .pixclock = 40000, + .left_margin = 33, + .right_margin = 64, + .upper_margin = 36, + .lower_margin = 7, + .hsync_len = 64, + .vsync_len = 25, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED, + }, + .width = -1, + .height = -1, + .tim2 = TIM2_BCD, + .cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1), + .fixedtimings = 1, + .connector = IMPD1_CTRL_DISP_LCD, +}; + +#elif PANEL == LTM10C209 +#define PANELTYPE ltm10c209 +/* + * Untested. + */ +static struct clcd_panel ltm10c209 = { + .mode = { + .name = "LTM10C209", + .refresh = 0, + .xres = 640, + .yres = 480, + .pixclock = 40000, + .left_margin = 20, + .right_margin = 20, + .upper_margin = 19, + .lower_margin = 19, + .hsync_len = 20, + .vsync_len = 10, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED, + }, + .width = -1, + .height = -1, + .tim2 = TIM2_BCD, + .cntl = CNTL_LCDTFT | CNTL_LCDVCOMP(1), + .fixedtimings = 1, + .connector = IMPD1_CTRL_DISP_LCD, +}; +#endif + +/* + * Disable all display connectors on the interface module. + */ +static void impd1fb_clcd_disable(struct clcd_fb *fb) +{ + impd1_tweak_control(fb->dev->parent, IMPD1_CTRL_DISP_MASK, 0); +} + +/* + * Enable the relevant connector on the interface module. + */ +static void impd1fb_clcd_enable(struct clcd_fb *fb) +{ + impd1_tweak_control(fb->dev->parent, IMPD1_CTRL_DISP_MASK, + fb->panel->connector | IMPD1_CTRL_DISP_ENABLE); +} + +/* + * Blank the screen if blank_mode != 0, else unblank. If blank == NULL + * then the caller blanks by setting the CLUT (Color Look Up Table) to all + * black. Return 0 if blanking succeeded, != 0 if un-/blanking failed due + * to e.g. a video mode which doesn't support it. Implements VESA suspend + * and powerdown modes on hardware that supports disabling hsync/vsync: + * blank_mode == 2: suspend vsync + * blank_mode == 3: suspend hsync + * blank_mode == 4: powerdown + */ +static int impd1fb_clcd_blank(struct clcd_fb *fb, int blank_mode) +{ + unsigned int val; + + if (blank_mode != 0) { /* Blank the screen */ + /* First remove power */ + val = fb->clcd_cntl &= ~CNTL_LCDPWR; + writel(val, fb->regs + CLCD_CNTL); + + /* XXX: this must die... */ + mdelay(20); + + /* Now disable */ + val = fb->clcd_cntl &= ~CNTL_LCDEN; + writel(val, fb->regs + CLCD_CNTL); + + impd1fb_clcd_disable(fb); + } else { /* Unblank the screen */ + /* First enable */ + val = fb->clcd_cntl |= CNTL_LCDEN; + writel(val, fb->regs + CLCD_CNTL); + + /* XXX: this must die... */ + mdelay(20); + + /* Now apply power */ + val = fb->clcd_cntl |= CNTL_LCDPWR; + writel(val, fb->regs + CLCD_CNTL); + + impd1fb_clcd_enable(fb); + } + + /* XXX: this must die... */ + mdelay(50); + + return 0; +} + +static void impd1fb_clcd_setvco(struct clcd_fb *fb, unsigned long pixclock) +{ + impd1_set_vco(fb->dev->parent, 1, pixclock); +} + +static struct clcd_board impd1fb_board = { + .blank = impd1fb_clcd_blank, + .disable = impd1fb_clcd_disable, + .enable = impd1fb_clcd_enable, + .setvco = impd1fb_clcd_setvco, + .check = clcdfb_check, + .decode = clcdfb_decode, +}; + +static int impd1_clcd_probe(struct primecell_device *dev, void *id) +{ + struct resource *res = &dev->res; + struct clcd_fb *fb; + unsigned long framebase = res->start + 0x01000000; + unsigned long framesize = SZ_1M; + int ret; + + if (!request_mem_region(res->start, SZ_4K, "CLCD")) { + printk(KERN_ERR "clcd: unable to reserve regs region\n"); + ret = -EBUSY; + goto out; + } + + if (!request_mem_region(framebase, framesize, "clcd framebuffer")) { + printk(KERN_ERR "clcd: unable to reserve framebuffer\n"); + ret = -EBUSY; + goto free_1; + } + + fb = (struct clcd_fb *) kmalloc(sizeof(struct clcd_fb), GFP_KERNEL); + if (!fb) { + printk(KERN_INFO "clcd: could not allocate new clcd_fb struct\n"); + ret = -ENOMEM; + goto free_2; + } + memset(fb, 0, sizeof (struct clcd_fb)); + + fb->dev = &dev->dev; + + fb->regs = ioremap(res->start, SZ_4K); + if (!fb->regs) { + printk(KERN_ERR "clcd: unable to remap registers\n"); + ret = -ENOMEM; + goto unmap; + } + + fb->fb.screen_base = ioremap(framebase, framesize); + if (!fb->fb.screen_base) { + printk(KERN_ERR "clcd: unable to map framebuffer\n"); + ret = -ENOMEM; + goto unmap; + } + + printk(KERN_INFO + "Integrator Colour LCD panel found at membase (0x%08lx/%ldK)\n", + framebase, framesize >> 10); + + fb->fb.fix.smem_start = framebase; + fb->fb.fix.smem_len = framesize; + fb->fb.fix.mmio_start = res->start; + fb->fb.fix.mmio_len = SZ_4K; + fb->panel = &PANELTYPE; + fb->board = &impd1fb_board; + + fb->fb.var.bits_per_pixel = BITS_PER_PIXEL; + fb->fb.var.grayscale = LCDBW; + + fb->fb.monspecs.hfmin = 0; + fb->fb.monspecs.hfmax = 100000; + fb->fb.monspecs.vfmin = 0; + fb->fb.monspecs.vfmax = 100; + fb->fb.monspecs.dclkmin = 2400000; + fb->fb.monspecs.dclkmax = 100000000; + + clcdfb_setup(fb); + + /* Say what sort of panel we are assuming */ + printk(KERN_INFO "Using settings for %s display\n", + fb->panel->mode.name); + + ret = clcdfb_register(fb); + if (ret < 0) { + printk(KERN_ERR "CLCD: cannot register framebuffer (%d)\n", ret); + goto unmap; + } + + return 0; + + unmap: + if (fb->fb.screen_base) + iounmap(fb->fb.screen_base); + if (fb->regs) + iounmap(fb->regs); + kfree(fb); + free_2: + release_mem_region(framebase, framesize); + free_1: + release_mem_region(res->start, SZ_4K); + out: + return ret; +} + +static int impd1_clcd_remove(struct primecell_device *dev) +{ + struct clcd_fb *fb = primecell_get_drvdata(dev); + + primecell_set_drvdata(dev, NULL); + + clcdfb_unregister(fb); + iounmap(fb->fb.screen_base); + iounmap(fb->regs); + release_mem_region(fb->fb.fix.smem_start, fb->fb.fix.smem_len); + release_mem_region(fb->fb.fix.mmio_start, fb->fb.fix.mmio_len); + kfree(fb); + + return 0; +} + +static struct primecell_id impd1_clcd_table[] = { + { + .id = 0x00041110, + .mask = 0x000fffff, + }, + { 0, 0 }, +}; + +static struct primecell_driver impd1_clcd_driver = { + .drv = { + .name = "CLCD", + }, + .probe = impd1_clcd_probe, + .remove = impd1_clcd_remove, + .id_table = impd1_clcd_table, +}; + +static int impd1fb_init(void) +{ + return primecell_driver_register(&impd1_clcd_driver); +} + +#ifdef MODULE +module_init(impd1fb_init); +#else +int __init impd1fb_setup(char *options) +{ + return 0; +} +#endif + +static void __exit impd1fb_exit(void) +{ + primecell_driver_unregister(&impd1_clcd_driver); +} + +module_exit(impd1fb_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Integrator/IM-PD1 colour LCD framebuffer driver"); diff -urN orig/drivers/video/arm/clcd-impd1.h linux/drivers/video/arm/clcd-impd1.h --- orig/drivers/video/arm/clcd-impd1.h Thu Jan 1 01:00:00 1970 +++ linux/drivers/video/arm/clcd-impd1.h Fri Jun 6 20:27:31 2003 @@ -0,0 +1,152 @@ +#define LTM10C209 1 +#define PROSPECTOR 2 +#define SVGA 3 +#define VGA 4 + +#if PANEL == VGA + +#define PANEL_NAME "VGA" + +// Timing Reg 0 +#define PPL (((640/16)-1)) +#define HSW (96-1) +#define HFP (44-1) +#define HBP (20-1) + +// Timing Reg 1 +#define LPP ((480-1)) +#define VSW ((2-1)) +#define VFP (29-1) +#define VBP (14-1) + +// Timing Reg 2 +#define PCD (0) +#define CLKSEL (0) +#define ACB (0) +#define IVS (1) //(0) +#define IHS (1) //(0) +#define IPC (1) +#define IOE (0) +#define CPL ((640-1)) +#define BCD (1) + +#define PIXEL_CLOCK_MHZ 25 +#define CONNECTOR_SELECT IMPD1_CTRL_VGA +#endif + +#if PANEL == SVGA + +#define PANEL_NAME "SVGA" + +// Timing Reg 0 +#define PPL (((800/16)-1)) +#define HSW (164) +#define HFP (20 -1) +#define HBP (20 -1) + +// Timing Reg 1 +#define LPP ((600-1)) +#define VSW ((62-1)) +#define VFP (5) +#define VBP (5) + +// Timing Reg 2 +#define PCD (0) +#define CLKSEL (0) +#define ACB (0) +#define IVS (1) //(0) +#define IHS (1) //(0) +#define IPC (0) +#define IOE (0) +#define CPL ((800-1)) +#define BCD (1) + +/* +** Need higher-than-default pixel clock rate +*/ +#define PIXEL_CLOCK_MHZ 36 +#define CONNECTOR_SELECT IMPD1_CTRL_VGA +#endif + + +#if PANEL == PROSPECTOR + +#define PANEL_NAME "Sharp LQ084V1DG21" + +// Timing Reg 0 +#define PPL (((640/16)-1)) //(((640/16)-1)) +#define HSW (63) //((20-1)) +#define HFP (63) //((20-1)) +#define HBP (32) //((20-1)) + +// Timing Reg 1 +#define LPP ((480-1)) +#define VSW ((25-1)) //((10-1)) +#define VFP (7) //(10) +#define VBP (36) //(10) + +// Timing Reg 2 +#define PCD (0) +#define CLKSEL (0) +#define ACB (0) +#define IVS (0) +#define IHS (0) +#define IPC (0) +#define IOE (0) +#define CPL ((640-1)) +#define BCD (1) + +#define PIXEL_CLOCK_MHZ 25 +#define CONNECTOR_SELECT 0 +#endif + +#if PANEL == LTM10C209 + +#define PANEL_NAME "LTM10C209 - ***UNTESTED***" + +// Timing Reg 0 +#define PPL (((640/16)-1)) +#define HSW ((20-1)) +#define HFP ((20-1)) +#define HBP ((20-1)) + +// Timing Reg 1 +#define LPP ((480-1)) +#define VSW ((10-1)) +#define VFP (10) +#define VBP (10) + +// Timing Reg 2 +#define PCD (0) +#define CLKSEL (0) +#define ACB (0) +#define IVS (0) +#define IHS (0) +#define IPC (0) +#define IOE (0) +#define CPL ((640-1)) +#define BCD (1) + +#define PIXEL_CLOCK_MHZ 25 +#define CONNECTOR_SELECT 0 +#endif + +//Timing Reg 3 +#define LED (0) +#define LEE (0) + +//Control Reg +#define LCDEN (1) +#define LCDBW (0) +#define LCDTFT (1) +#define LCDMONO8 (0) +#define LCDDUAL (0) +#define BGR (0) +#define BEBO (0) +#define BEPO (0) +#define LCDPWR (1) +#define LCDVCOMP (1) +#define LDMAFIFOTME (0) +#define WATERMARK (0) + + diff -urN orig/drivers/video/arm/clcd.h linux/drivers/video/arm/clcd.h --- orig/drivers/video/arm/clcd.h Thu Jan 1 01:00:00 1970 +++ linux/drivers/video/arm/clcd.h Fri Jun 6 20:30:46 2003 @@ -0,0 +1,247 @@ +/* + * linux/drivers/video/arm/clcd.h -- Integrator LCD panel. + * + * David A Rusling + * + * Copyright (C) 2001 ARM Limited + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +/* + * CLCD Controller Internal Register addresses + */ +#define CLCD_TIM0 0x00000000 +#define CLCD_TIM1 0x00000004 +#define CLCD_TIM2 0x00000008 +#define CLCD_TIM3 0x0000000c +#define CLCD_UBAS 0x00000010 +#define CLCD_LBAS 0x00000014 +#define CLCD_IENB 0x00000018 +#define CLCD_CNTL 0x0000001c +#define CLCD_STAT 0x00000020 +#define CLCD_INTR 0x00000024 +#define CLCD_UCUR 0x00000028 +#define CLCD_LCUR 0x0000002C +#define CLCD_PALL 0x00000200 +#define CLCD_PALETTE 0x00000200 + +#define TIM2_CLKSEL (1 << 5) +#define TIM2_IVS (1 << 11) +#define TIM2_IHS (1 << 12) +#define TIM2_IPC (1 << 13) +#define TIM2_IOE (1 << 14) +#define TIM2_BCD (1 << 26) + +#define CNTL_LCDEN (1 << 0) +#define CNTL_LCDBPP1 (0 << 1) +#define CNTL_LCDBPP2 (1 << 1) +#define CNTL_LCDBPP4 (2 << 1) +#define CNTL_LCDBPP8 (3 << 1) +#define CNTL_LCDBPP16 (4 << 1) +#define CNTL_LCDBPP24 (5 << 1) +#define CNTL_LCDBW (1 << 4) +#define CNTL_LCDTFT (1 << 5) +#define CNTL_LCDMONO8 (1 << 6) +#define CNTL_LCDDUAL (1 << 7) +#define CNTL_BGR (1 << 8) +#define CNTL_BEBO (1 << 9) +#define CNTL_BEPO (1 << 10) +#define CNTL_LCDPWR (1 << 11) +#define CNTL_LCDVCOMP(x) ((x) << 12) +#define CNTL_LDMAFIFOTIME (1 << 15) +#define CNTL_WATERMARK (1 << 16) + +struct clcd_panel { + struct fb_videomode mode; + signed short width; /* width in mm */ + signed short height; /* height in mm */ + u32 tim2; + u32 tim3; + u32 cntl; + unsigned int fixedtimings:1; + unsigned int connector; +}; + +struct clcd_regs { + u32 tim0; + u32 tim1; + u32 tim2; + u32 tim3; + u32 cntl; + unsigned long pixclock; +}; + +struct clcd_fb; + +/* + * the board-type specific routines + */ +struct clcd_board { + /* + * Optional. Blank/unblank the display. mode describes the + * desired power down level (0 = unblank, 1 = blank, + * 2 = suspend vsync, 3 = suspend hsync, 4 = powerdown) + */ + int (*blank)(struct clcd_fb *fb, int mode); + + /* + * Optional. Check whether the var structure is acceptable + * for this display. + */ + int (*check)(struct clcd_fb *fb, struct fb_var_screeninfo *var); + + /* + * Compulsary. Decode fb->fb.var into regs->*. In the case of + * fixed timing, set regs->* to the register values required. + */ + void (*decode)(struct clcd_fb *fb, struct clcd_regs *regs); + + /* + * Optional. Set the clock source/VCO for the CLCD based upon + * the pixel clock (which is in picoseconds). + */ + void (*setvco)(struct clcd_fb *, unsigned long pixclock); + + /* + * Optional. Disable any extra display hardware. + */ + void (*disable)(struct clcd_fb *); + + /* + * Optional. Enable any extra display hardware. + */ + void (*enable)(struct clcd_fb *); +}; + +struct device; + +/* this data structure describes each frame buffer device we find */ +struct clcd_fb { + struct fb_info fb; + struct device *dev; + struct clcd_panel *panel; + struct clcd_board *board; + void *board_data; + void *regs; + u32 clcd_cntl; + u32 cmap[16]; +}; + +void clcdfb_setup(struct clcd_fb *); +int clcdfb_register(struct clcd_fb *); +void clcdfb_unregister(struct clcd_fb *); + +static inline void clcdfb_decode(struct clcd_fb *fb, struct clcd_regs *regs) +{ + u32 val; + + /* + * Program the CLCD controller registers and start the CLCD + */ + val = ((fb->fb.var.xres / 16) - 1) << 2; + val |= (fb->fb.var.hsync_len - 1) << 8; + val |= (fb->fb.var.right_margin - 1) << 16; + val |= (fb->fb.var.left_margin - 1) << 24; + regs->tim0 = val; + + val = fb->fb.var.yres - 1; + val |= (fb->fb.var.vsync_len - 1) << 10; + val |= fb->fb.var.lower_margin << 16; + val |= fb->fb.var.upper_margin << 24; + regs->tim1 = val; + + val = fb->panel->tim2; + val |= fb->fb.var.sync & FB_SYNC_HOR_HIGH_ACT ? 0 : TIM2_IHS; + val |= fb->fb.var.sync & FB_SYNC_VERT_HIGH_ACT ? 0 : TIM2_IVS; + + if (fb->panel->cntl & CNTL_LCDTFT) + val |= (fb->fb.var.xres_virtual - 1) << 16; + else if (fb->panel->cntl & CNTL_LCDBW) + printk("what value for CPL for stnmono panels?"); + else + val |= ((fb->fb.var.xres_virtual * 8 / 3) - 1) << 16; + regs->tim2 = val; + + regs->tim3 = fb->panel->tim3; + + val = fb->panel->cntl; + if (fb->fb.var.grayscale) + val |= CNTL_LCDBW; + + switch (fb->fb.var.bits_per_pixel) { + case 1: + val |= CNTL_LCDBPP1; + break; + case 2: + val |= CNTL_LCDBPP2; + break; + case 4: + val |= CNTL_LCDBPP2; + break; + case 8: + val |= CNTL_LCDBPP8; + break; + case 16: + val |= CNTL_LCDBPP16; + break; + case 24: + val |= CNTL_LCDBPP24; + break; + } + + regs->cntl = val; + regs->pixclock = fb->fb.var.pixclock; +} + +static inline int clcdfb_check(struct clcd_fb *fb, struct fb_var_screeninfo *var) +{ + var->xres_virtual = var->xres = (var->xres + 7) & ~7; + var->yres_virtual = var->yres; + +#define CHECK(e,l,h) (var->e < l || var->e > h) + if (CHECK(right_margin, (5+1), 256) || /* back porch */ + CHECK(left_margin, (5+1), 256) || /* front porch */ + CHECK(hsync_len, (5+1), 256) || + var->xres > 4096 || + var->lower_margin > 255 || /* back porch */ + var->upper_margin > 255 || /* front porch */ + var->vsync_len > 32 || + var->yres > 1024) + return -EINVAL; +#undef CHECK + + /* single panel mode: PCD = max(PCD, 1) */ + /* dual panel mode: PCD = max(PCD, 5) */ + + /* + * You can't change the grayscale setting, and + * we can only do non-interlaced video. + */ + if (var->grayscale != fb->fb.var.grayscale || + (var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED) + return -EINVAL; + +#define CHECK(e) (var->e != fb->fb.var.e) + if (fb->panel->fixedtimings && + (CHECK(xres) || + CHECK(yres) || + CHECK(bits_per_pixel) || + CHECK(pixclock) || + CHECK(left_margin) || + CHECK(right_margin) || + CHECK(upper_margin) || + CHECK(lower_margin) || + CHECK(hsync_len) || + CHECK(vsync_len) || + CHECK(sync))) + return -EINVAL; +#undef CHECK + + var->nonstd = 0; + var->accel_flags = 0; + + return 0; +} diff -urN orig/drivers/video/cfbimgblt.c linux/drivers/video/cfbimgblt.c --- orig/drivers/video/cfbimgblt.c Mon May 5 17:39:49 2003 +++ linux/drivers/video/cfbimgblt.c Tue May 13 23:53:23 2003 @@ -325,7 +325,7 @@ else slow_imageblit(image, p, dst1, fgcolor, bgcolor, start_index, pitch_index); - } else if (image->depth == bpp) + } else if (image->depth <= bpp) color_imageblit(image, p, dst1, start_index, pitch_index); } diff -urN orig/drivers/video/fbmem.c linux/drivers/video/fbmem.c --- orig/drivers/video/fbmem.c Tue May 27 10:05:19 2003 +++ linux/drivers/video/fbmem.c Sat May 31 15:01:06 2003 @@ -52,6 +52,8 @@ extern int acornfb_init(void); extern int acornfb_setup(char*); +extern int impd1fb_init(void); +extern int impd1fb_setup(char*); extern int amifb_init(void); extern int amifb_setup(char*); extern int anakinfb_init(void); @@ -187,6 +189,9 @@ #ifdef CONFIG_FB_CYBER2000 { "cyber2000fb", cyber2000fb_init, cyber2000fb_setup }, #endif +#ifdef CONFIG_FB_ARMIMPD1 + { "impd1fb", impd1_init, impd1_setup }, +#endif #ifdef CONFIG_FB_PM2 { "pm2fb", pm2fb_init, pm2fb_setup }, #endif @@ -895,11 +900,15 @@ { int xoffset = var->xoffset; int yoffset = var->yoffset; + int ybottom = yoffset; int err; + if (!(var->vmode & FB_VMODE_YWRAP)) + ybottom += info->var.yres; + if (xoffset < 0 || yoffset < 0 || !info->fbops->fb_pan_display || xoffset + info->var.xres > info->var.xres_virtual || - yoffset + info->var.yres > info->var.yres_virtual) + ybottom > info->var.yres_virtual) return -EINVAL; if ((err = info->fbops->fb_pan_display(var, info))) return err; diff -urN orig/drivers/video/sa1100fb.c linux/drivers/video/sa1100fb.c --- orig/drivers/video/sa1100fb.c Mon May 5 17:39:50 2003 +++ linux/drivers/video/sa1100fb.c Mon May 19 21:44:07 2003 @@ -375,7 +375,6 @@ #endif #ifdef CONFIG_SA1100_FREEBIRD -#warning Please check this carefully static struct sa1100fb_mach_info freebird_info __initdata = { .pixclock = 171521, .bpp = 16, .xres = 240, .yres = 320, @@ -425,7 +424,6 @@ .lccr0 = LCCR0_Color | LCCR0_Dual | LCCR0_Pas, .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2) | 8, -#error FIXME /* * FIXME: please get rid of the '| 8' in preference to an * LCCR3_PixClkDiv() version. --rmk @@ -523,7 +521,6 @@ .lccr0 = LCCR0_Mono | LCCR0_Sngl | LCCR0_Pas | LCCR0_8PixMono, .lccr3 = LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(255) | LCCR3_PixClkDiv(44), -#error FIXME: fix pixclock, ACBsDiv /* * FIXME: I think ACBsDiv is wrong above - should it be 512 (disabled)? * - rmk @@ -586,7 +583,6 @@ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT , .lccr0 = LCCR0_Color | LCCR0_Dual | LCCR0_Pas, -#error Fixme .lccr3 = 0xff00 | 0x18 /* ought to be 0x14 but DMA isn't up to that as yet */ }; @@ -1436,6 +1432,7 @@ */ if (old_state != C_DISABLE && old_state != C_DISABLE_PM) { fbi->state = state; + __sa1100fb_lcd_power(fbi, 0); sa1100fb_disable_controller(fbi); } break; @@ -1449,9 +1446,9 @@ fbi->state = state; __sa1100fb_backlight_power(fbi, 0); + __sa1100fb_lcd_power(fbi, 0); if (old_state != C_DISABLE_CLKCHANGE) sa1100fb_disable_controller(fbi); - __sa1100fb_lcd_power(fbi, 0); } break; @@ -1463,6 +1460,7 @@ if (old_state == C_DISABLE_CLKCHANGE) { fbi->state = C_ENABLE; sa1100fb_enable_controller(fbi); + __sa1100fb_lcd_power(fbi, 1); } break; @@ -1497,8 +1495,8 @@ if (old_state != C_ENABLE) { fbi->state = C_ENABLE; sa1100fb_setup_gpio(fbi); - __sa1100fb_lcd_power(fbi, 1); sa1100fb_enable_controller(fbi); + __sa1100fb_lcd_power(fbi, 1); __sa1100fb_backlight_power(fbi, 1); } break; @@ -1753,25 +1751,7 @@ return fbi; } -static struct device_driver sa1100fb_driver = { - .name = "sa1100fb", - .bus = &system_bus_type, - .suspend = sa1100fb_suspend, - .resume = sa1100fb_resume, -}; - -static struct sys_device sa1100fb_dev = { - .name = "LCD", - .id = 0, - .root = NULL, - .dev = { - .name = "Intel Corporation SA11x0 [LCD]", - .bus_id = "b0100000", - .driver = &sa1100fb_driver, - }, -}; - -int __init sa1100fb_init(void) +static int __init sa1100fb_probe(struct device *dev) { struct sa1100fb_info *fbi; int ret; @@ -1779,16 +1759,11 @@ if (!request_mem_region(0xb0100000, 0x10000, "LCD")) return -EBUSY; - driver_register(&sa1100fb_driver); - sys_device_register(&sa1100fb_dev); - fbi = sa1100fb_init_fbinfo(); ret = -ENOMEM; if (!fbi) goto failed; - dev_set_drvdata(&sa1100fb_dev.dev, fbi); - /* Initialize video memory */ ret = sa1100fb_map_video_memory(fbi); if (ret) @@ -1822,6 +1797,8 @@ */ sa1100fb_check_var(&fbi->fb.var, &fbi->fb); + dev_set_drvdata(dev, fbi); + ret = register_framebuffer(&fbi->fb); if (ret < 0) goto failed; @@ -1839,14 +1816,26 @@ return 0; failed: - sys_device_unregister(&sa1100fb_dev); - driver_unregister(&sa1100fb_driver); + dev_set_drvdata(dev, NULL); if (fbi) kfree(fbi); release_mem_region(0xb0100000, 0x10000); return ret; } +static struct device_driver sa1100fb_driver = { + .name = "sa11x0-fb", + .bus = &platform_bus_type, + .probe = sa1100fb_probe, + .suspend = sa1100fb_suspend, + .resume = sa1100fb_resume, +}; + +int __init sa1100fb_init(void) +{ + return driver_register(&sa1100fb_driver); +} + int __init sa1100fb_setup(char *options) { #if 0 diff -urN orig/fs/adfs/super.c linux/fs/adfs/super.c --- orig/fs/adfs/super.c Tue May 27 10:05:20 2003 +++ linux/fs/adfs/super.c Tue May 27 10:13:43 2003 @@ -403,6 +403,12 @@ asb->s_version = dr->format_version; asb->s_log2sharesize = dr->log2sharesize; + printk(KERN_DEBUG "ADFS: idlen %d map bit size %d sector size %d\n", + dr->idlen, 1 << dr->log2bpmb, 1 << dr->log2secsize); + printk(KERN_DEBUG "ADFS: map size %d map2blk %d version %d share size %d\n", + asb->s_map_size, asb->s_map2blk, + asb->s_version, 1 << asb->s_log2sharesize); + asb->s_map = adfs_read_map(sb, dr); if (!asb->s_map) goto error_free_bh; diff -urN orig/fs/binfmt_aout.c linux/fs/binfmt_aout.c --- orig/fs/binfmt_aout.c Fri Feb 21 19:48:52 2003 +++ linux/fs/binfmt_aout.c Tue Feb 18 00:16:57 2003 @@ -433,7 +433,11 @@ else send_sig(SIGTRAP, current, 0); } +#ifndef __arm__ return 0; +#else + return regs->ARM_r0; +#endif } static int load_aout_library(struct file *file) @@ -463,8 +467,11 @@ /* For QMAGIC, the starting address is 0x20 into the page. We mask this off to get the starting address for the page */ - - start_addr = ex.a_entry & 0xfffff000; +#ifndef __arm__ + start_addr = ex.a_entry & 0xfffff000; +#else + start_addr = ex.a_entry & 0xffff8000; +#endif if ((N_TXTOFF(ex) & ~PAGE_MASK) != 0) { static unsigned long error_time; diff -urN orig/fs/exec.c linux/fs/exec.c --- orig/fs/exec.c Tue May 27 10:05:24 2003 +++ linux/fs/exec.c Tue May 27 10:13:47 2003 @@ -324,6 +324,7 @@ /* no need for flush_tlb */ pte_chain_free(pte_chain); +// memc_update_addr(tsk->mm, *pte, address); return; out: spin_unlock(&tsk->mm->page_table_lock); diff -urN orig/fs/jffs2/file.c linux/fs/jffs2/file.c --- orig/fs/jffs2/file.c Sat Dec 28 17:07:43 2002 +++ linux/fs/jffs2/file.c Fri May 9 14:01:22 2003 @@ -165,6 +165,10 @@ ri->mtime = cpu_to_je32((ivalid & ATTR_MTIME)?iattr->ia_mtime.tv_sec:inode->i_mtime.tv_sec); ri->ctime = cpu_to_je32((ivalid & ATTR_CTIME)?iattr->ia_ctime.tv_sec:inode->i_ctime.tv_sec); ri->compr = JFFS2_COMPR_NONE; + + ri->offset = cpu_to_je32(0); + ri->csize = ri->dsize = cpu_to_je32(mdatalen); + if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) { /* It's an extension. Make it a hole node */ ri->compr = JFFS2_COMPR_ZERO; diff -urN orig/fs/partitions/Kconfig linux/fs/partitions/Kconfig --- orig/fs/partitions/Kconfig Mon Mar 24 23:47:27 2003 +++ linux/fs/partitions/Kconfig Mon Mar 24 23:57:55 2003 @@ -20,7 +20,17 @@ help Support hard disks partitioned under Acorn operating systems. -# bool ' Cumana partition support' CONFIG_ACORN_PARTITION_CUMANA +config ACORN_PARTITION_CUMANA + bool "Cumana partition support" if PARTITION_ADVANCED && ACORN_PARTITION + default y if !PARTITION_ADVANCED && ARCH_ACORN + help + Say Y here if you would like to use hard disks under Linux which + were partitioned using the Cumana interface on Acorn machines. + +config ACORN_PARTITION_EESOX + bool "EESOX partition support" if PARTITION_ADVANCED && ACORN_PARTITION + default y if !PARTITION_ADVANCED && ARCH_ACORN + config ACORN_PARTITION_ICS bool "ICS partition support" if PARTITION_ADVANCED && ACORN_PARTITION default y if !PARTITION_ADVANCED && ARCH_ACORN diff -urN orig/fs/partitions/acorn.c linux/fs/partitions/acorn.c --- orig/fs/partitions/acorn.c Thu Jul 25 20:13:49 2002 +++ linux/fs/partitions/acorn.c Wed May 28 15:20:10 2003 @@ -7,14 +7,25 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * Scan ADFS partitions on hard disk drives. + * Scan ADFS partitions on hard disk drives. Unfortunately, there + * isn't a standard for partitioning drives on Acorn machines, so + * every single manufacturer of SCSI and IDE cards created their own + * method. */ #include #include +#include #include "check.h" #include "acorn.h" +/* + * Partition types. (Oh for reusability) + */ +#define PARTITION_RISCIX_MFM 1 +#define PARTITION_RISCIX_SCSI 2 +#define PARTITION_LINUX 9 + static struct adfs_discrecord * adfs_partition(struct parsed_partitions *state, char *name, char *data, unsigned long first_sector, int slot) @@ -40,6 +51,21 @@ } #ifdef CONFIG_ACORN_PARTITION_RISCIX + +struct riscix_part { + __u32 start; + __u32 length; + __u32 one; + char name[16]; +}; + +struct riscix_record { + __u32 magic; +#define RISCIX_MAGIC (0x4a657320) + __u32 date; + struct riscix_part part[8]; +}; + static int riscix_partition(struct parsed_partitions *state, struct block_device *bdev, unsigned long first_sect, int slot, unsigned long nr_sects) @@ -81,6 +107,15 @@ } #endif +#define LINUX_NATIVE_MAGIC 0xdeafa1de +#define LINUX_SWAP_MAGIC 0xdeafab1e + +struct linux_part { + __u32 magic; + __u32 start_sect; + __u32 nr_sects; +}; + static int linux_partition(struct parsed_partitions *state, struct block_device *bdev, unsigned long first_sect, int slot, unsigned long nr_sects) @@ -114,7 +149,7 @@ } #ifdef CONFIG_ACORN_PARTITION_CUMANA -static int +int adfspart_check_CUMANA(struct parsed_partitions *state, struct block_device *bdev) { unsigned long first_sector = 0; @@ -126,7 +161,7 @@ int slot = 1; /* - * Try Cumana style partitions - sector 3 contains ADFS boot block + * Try Cumana style partitions - sector 6 contains ADFS boot block * with pointer to next 'drive'. * * There are unknowns in this code - is the 'cylinder number' of the @@ -206,7 +241,7 @@ * hda1 = ADFS partition on first drive. * hda2 = non-ADFS partition. */ -static int +int adfspart_check_ADFS(struct parsed_partitions *state, struct block_device *bdev) { unsigned long start_sect, nr_sects, sectscyl, heads; @@ -263,11 +298,18 @@ break; } } + printk("\n"); return 1; } #endif #ifdef CONFIG_ACORN_PARTITION_ICS + +struct ics_part { + __u32 start; + __s32 size; +}; + static int adfspart_check_ICSLinux(struct block_device *bdev, unsigned long block) { Sector sect; @@ -284,6 +326,22 @@ } /* + * Check for a valid ICS partition using the checksum. + */ +static inline int valid_ics_sector(const unsigned char *data) +{ + unsigned long sum; + int i; + + for (i = 0, sum = 0x50617274; i < 508; i++) + sum += data[i]; + + sum -= le32_to_cpu(*(__u32 *)(&data[508])); + + return sum == 0; +} + +/* * Purpose: allocate ICS partitions. * Params : hd - pointer to gendisk structure to store partition info. * dev - device number to access. @@ -293,15 +351,13 @@ * hda2 = ADFS partition 1 on first drive. * ..etc.. */ -static int +int adfspart_check_ICS(struct parsed_partitions *state, struct block_device *bdev) { - Sector sect; - unsigned char *data; - unsigned long sum; - unsigned int i; + const unsigned char *data; + const struct ics_part *p; int slot; - struct ics_part *p; + Sector sect; /* * Try ICS style partitions - sector 0 contains partition info. @@ -310,33 +366,33 @@ if (!data) return -1; - /* - * check for a valid checksum - */ - for (i = 0, sum = 0x50617274; i < 508; i++) - sum += data[i]; - - sum -= le32_to_cpu(*(__u32 *)(&data[508])); - if (sum) { + if (!valid_ics_sector(data)) { put_dev_sector(sect); - return 0; /* not ICS partition table */ + return 0; } printk(" [ICS]"); - for (slot = 1, p = (struct ics_part *)data; p->size; p++) { + for (slot = 1, p = (const struct ics_part *)data; p->size; p++) { u32 start = le32_to_cpu(p->start); - u32 size = le32_to_cpu(p->size); + s32 size = le32_to_cpu(p->size); /* yes, it's signed. */ if (slot == state->limit) break; + /* + * Negative sizes tell the RISC OS ICS driver to ignore + * this partition - in effect it says that this does not + * contain an ADFS filesystem. + */ if (size < 0) { size = -size; /* - * We use the first sector to identify what type - * this partition is... + * Our own extension - We use the first sector + * of the partition to identify what type this + * partition is. We must not make this visible + * to the filesystem. */ if (size > 1 && adfspart_check_ICSLinux(bdev, start)) { start += 1; @@ -349,10 +405,39 @@ } put_dev_sector(sect); + printk("\n"); return 1; } #endif +#ifdef CONFIG_ACORN_PARTITION_POWERTEC +struct ptec_part { + __u32 unused1; + __u32 unused2; + __u32 start; + __u32 size; + __u32 unused5; + char type[8]; +}; + +static inline int valid_ptec_sector(const unsigned char *data) +{ + unsigned char checksum = 0x2a; + int i; + + /* + * If it looks like a PC/BIOS partition, then it + * probably isn't PowerTec. + */ + if (data[510] == 0x55 && data[511] == 0xaa) + return 0; + + for (i = 0; i < 511; i++) + checksum += data[i]; + + return checksum == data[511]; +} + /* * Purpose: allocate ICS partitions. * Params : hd - pointer to gendisk structure to store partition info. @@ -363,14 +448,12 @@ * hda2 = ADFS partition 1 on first drive. * ..etc.. */ -#ifdef CONFIG_ACORN_PARTITION_POWERTEC -static int +int adfspart_check_POWERTEC(struct parsed_partitions *state, struct block_device *bdev) { Sector sect; - unsigned char *data; - struct ptec_partition *p; - unsigned char checksum; + const unsigned char *data; + const struct ptec_part *p; int slot = 1; int i; @@ -378,17 +461,14 @@ if (!data) return -1; - for (checksum = 0x2a, i = 0; i < 511; i++) - checksum += data[i]; - - if (checksum != data[511]) { + if (!valid_ptec_sector(data)) { put_dev_sector(sect); return 0; } printk(" [POWERTEC]"); - for (i = 0, p = (struct ptec_partition *)data; i < 12; i++, p++) { + for (i = 0, p = (const struct ptec_part *)data; i < 12; i++, p++) { u32 start = le32_to_cpu(p->start); u32 size = le32_to_cpu(p->size); @@ -397,46 +477,81 @@ } put_dev_sector(sect); + printk("\n"); return 1; } #endif -static int (*partfn[])(struct parsed_partitions *, struct block_device *) = { -#ifdef CONFIG_ACORN_PARTITION_ICS - adfspart_check_ICS, -#endif -#ifdef CONFIG_ACORN_PARTITION_POWERTEC - adfspart_check_POWERTEC, -#endif -#ifdef CONFIG_ACORN_PARTITION_CUMANA - adfspart_check_CUMANA, -#endif -#ifdef CONFIG_ACORN_PARTITION_ADFS - adfspart_check_ADFS, -#endif - NULL +#ifdef CONFIG_ACORN_PARTITION_EESOX +struct eesox_part { + char magic[6]; + char name[10]; + u32 start; + u32 unused6; + u32 unused7; + u32 unused8; +}; + +/* + * Guess who created this format? + */ +static const char eesox_name[] = { + 'N', 'e', 'i', 'l', ' ', + 'C', 'r', 'i', 't', 'c', 'h', 'e', 'l', 'l', ' ', ' ' }; + /* - * Purpose: initialise all the partitions on an ADFS drive. - * These may be other ADFS partitions or a Linux/RiscBSD/RISCiX - * partition. + * EESOX SCSI partition format. * - * Params : hd - pointer to gendisk structure - * dev - device number to access + * This is a goddamned awful partition format. We don't seem to store + * the size of the partition in this table, only the start addresses. * - * Returns: -1 on error, 0 if not ADFS format, 1 if ok. + * There are two possibilities where the size comes from: + * 1. The individual ADFS boot block entries that are placed on the disk. + * 2. The start address of the next entry. */ -int acorn_partition(struct parsed_partitions *state, struct block_device *bdev) +int +adfspart_check_EESOX(struct parsed_partitions *state, struct block_device *bdev) { - int i; + Sector sect; + const unsigned char *data; + unsigned char buffer[256]; + struct eesox_part *p; + u32 start = 0; + int i, slot = 1; - for (i = 0; partfn[i]; i++) { - int r = partfn[i](state, bdev); - if (r) { - if (r > 0) - printk("\n"); - return r; - } + data = read_dev_sector(bdev, 7, §); + if (!data) + return -1; + + /* + * "Decrypt" the partition table. God knows why... + */ + for (i = 0; i < 256; i++) + buffer[i] = data[i] ^ eesox_name[i & 15]; + + put_dev_sector(sect); + + for (i = 0, p = (struct eesox_part *)buffer; i < 8; i++, p++) { + u32 next; + + if (memcmp(p->magic, "Eesox", 6)) + break; + + next = le32_to_cpu(p->start) + first_sector; + if (i) + put_partition(state, slot++, start, next - start); + start = next; } - return 0; + + if (i != 0) { + unsigned long size; + + size = hd->part[minor(to_kdev_t(bdev->bd_dev))].nr_sects; + add_gd_partition(hd, minor++, start, size - start); + printk("\n"); + } + + return i ? 1 : 0; } +#endif diff -urN orig/fs/partitions/acorn.h linux/fs/partitions/acorn.h --- orig/fs/partitions/acorn.h Thu Jul 25 20:13:49 2002 +++ linux/fs/partitions/acorn.h Thu Jul 25 20:19:05 2002 @@ -1,53 +1,14 @@ /* - * fs/partitions/acorn.h + * linux/fs/partitions/acorn.h * - * Copyright (C) 1996-1998 Russell King - */ -#include - -/* - * Partition types. (Oh for reusability) + * Copyright (C) 1996-2001 Russell King. + * + * I _hate_ this partitioning mess - why can't we have one defined + * format, and everyone stick to it? */ -#define PARTITION_RISCIX_MFM 1 -#define PARTITION_RISCIX_SCSI 2 -#define PARTITION_LINUX 9 - -struct riscix_part { - __u32 start; - __u32 length; - __u32 one; - char name[16]; -}; - -struct riscix_record { - __u32 magic; -#define RISCIX_MAGIC (0x4a657320) - __u32 date; - struct riscix_part part[8]; -}; - -#define LINUX_NATIVE_MAGIC 0xdeafa1de -#define LINUX_SWAP_MAGIC 0xdeafab1e - -struct linux_part { - __u32 magic; - __u32 start_sect; - __u32 nr_sects; -}; - -struct ics_part { - __u32 start; - __s32 size; -}; - -struct ptec_partition { - __u32 unused1; - __u32 unused2; - __u32 start; - __u32 size; - __u32 unused5; - char type[8]; -}; - -int acorn_partition(struct parsed_partitions *state, struct block_device *bdev); +int adfspart_check_CUMANA(struct parsed_partitions *state, struct block_device *bdev); +int adfspart_check_ADFS(struct parsed_partitions *state, struct block_device *bdev); +int adfspart_check_ICS(struct parsed_partitions *state, struct block_device *bdev); +int adfspart_check_POWERTEC(struct parsed_partitions *state, struct block_device *bdev); +int adfspart_check_EESOX(struct parsed_partitions *state, struct block_device *bdev); diff -urN orig/fs/partitions/check.c linux/fs/partitions/check.c --- orig/fs/partitions/check.c Tue May 27 10:05:28 2003 +++ linux/fs/partitions/check.c Wed May 28 15:18:52 2003 @@ -45,9 +45,33 @@ int warn_no_part = 1; /*This is ugly: should make genhd removable media aware*/ static int (*check_part[])(struct parsed_partitions *, struct block_device *) = { -#ifdef CONFIG_ACORN_PARTITION - acorn_partition, + /* + * Probe partition formats with tables at disk address 0 + * that also have an ADFS boot block at 0xdc0. + */ +#ifdef CONFIG_ACORN_PARTITION_ICS + adfspart_check_ICS, #endif +#ifdef CONFIG_ACORN_PARTITION_POWERTEC + adfspart_check_POWERTEC, +#endif +#ifdef CONFIG_ACORN_PARTITION_EESOX + adfspart_check_EESOX, +#endif + + /* + * Now move on to formats that only have partition info at + * disk address 0xdc0. Since these may also have stale + * PC/BIOS partition tables, they need to come before + * the msdos entry. + */ +#ifdef CONFIG_ACORN_PARTITION_CUMANA + adfspart_check_CUMANA, +#endif +#ifdef CONFIG_ACORN_PARTITION_ADFS + adfspart_check_ADFS, +#endif + #ifdef CONFIG_EFI_PARTITION efi_partition, /* this must come before msdos */ #endif diff -urN orig/fs/proc/kcore.c linux/fs/proc/kcore.c --- orig/fs/proc/kcore.c Sat Nov 2 18:58:18 2002 +++ linux/fs/proc/kcore.c Mon May 19 23:30:41 2003 @@ -99,7 +99,13 @@ } #else /* CONFIG_KCORE_AOUT */ +#define KCORE_BASE (PAGE_OFFSET - 0x01000000) +#define in_vmlist_region(x) (((x) >= KCORE_BASE && (x) < PAGE_OFFSET) || ((x) >= VMALLOC_START && (x) < VMALLOC_END)) + +#ifndef KCORE_BASE #define KCORE_BASE PAGE_OFFSET +#define in_vmlist_region(x) ((x) >= VMALLOC_START && (x) < VMALLOC_END) +#endif #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) @@ -394,7 +400,7 @@ tsz = buflen; while (buflen) { - if ((start >= VMALLOC_START) && (start < VMALLOC_END)) { + if (in_vmlist_region(start)) { char * elf_buf; struct vm_struct *m; unsigned long curstart = start; diff -urN orig/include/asm-alpha/ide.h linux/include/asm-alpha/ide.h --- orig/include/asm-alpha/ide.h Tue Feb 25 10:57:57 2003 +++ linux/include/asm-alpha/ide.h Wed Mar 19 11:49:40 2003 @@ -19,35 +19,15 @@ #define MAX_HWIFS CONFIG_IDE_MAX_HWIFS #endif -static __inline__ int ide_default_irq(ide_ioreg_t base) -{ - switch (base) { - case 0x1f0: return 14; - case 0x170: return 15; - case 0x1e8: return 11; - case 0x168: return 10; - default: - return 0; - } -} - -static __inline__ ide_ioreg_t ide_default_io_base(int index) -{ - switch (index) { - case 0: return 0x1f0; - case 1: return 0x170; - case 2: return 0x1e8; - case 3: return 0x168; - default: - return 0; - } -} +#define ide_default_io_base(i) ((ide_ioreg_t)0) +#define ide_default_irq(b) (0) static __inline__ void ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq) { ide_ioreg_t reg = data_port; int i; + memset(hw, 0, sizeof(*hw)); for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { hw->io_ports[i] = reg; reg += 1; @@ -70,13 +50,19 @@ { #ifndef CONFIG_PCI hw_regs_t hw; - int index; - for (index = 0; index < MAX_HWIFS; index++) { - ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, NULL); - hw.irq = ide_default_irq(ide_default_io_base(index)); - ide_register_hw(&hw); - } + ide_init_hwif_ports(&hw, 0x1f0, 0x3f6, NULL); + hw.irq = 14; + ide_register_hw(&hw); + ide_init_hwif_ports(&hw, 0x170, 0x376, NULL); + hw.irq = 15; + ide_register_hw(&hw); + ide_init_hwif_ports(&hw, 0x1e8, 0x3ee, NULL); + hw.irq = 11; + ide_register_hw(&hw); + ide_init_hwif_ports(&hw, 0x168, 0x36e, NULL); + hw.irq = 10; + ide_register_hw(&hw); #endif } diff -urN orig/include/asm-arm/amba.h linux/include/asm-arm/amba.h --- orig/include/asm-arm/amba.h Thu Jan 1 01:00:00 1970 +++ linux/include/asm-arm/amba.h Thu Jun 12 14:55:55 2003 @@ -0,0 +1,44 @@ +/* + * linux/include/asm-arm/amba.h + * + * Copyright (C) 2003 Deep Blue Solutions Ltd, 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. + */ +#ifndef ASMARM_AMBA_H +#define ASMARM_AMBA_H + +struct amba_device { + struct device dev; + struct resource res; + unsigned int irq; + unsigned int periphid; +}; + +struct amba_id { + unsigned int id; + unsigned int mask; + void *data; +}; + +struct amba_driver { + struct device_driver drv; + int (*probe)(struct amba_device *, void *); + int (*remove)(struct amba_device *); + void (*shutdown)(struct amba_device *); + int (*suspend)(struct amba_device *, u32, u32); + int (*resume)(struct amba_device *, u32); + struct amba_id *id_table; +}; + +#define amba_get_drvdata(d) dev_get_drvdata(&d->dev) +#define amba_set_drvdata(d,p) dev_set_drvdata(&d->dev, p) + +int amba_driver_register(struct amba_driver *); +void amba_driver_unregister(struct amba_driver *); +int amba_device_register(struct amba_device *, struct resource *); +void amba_device_unregister(struct amba_device *); + +#endif diff -urN orig/include/asm-arm/arch-integrator/impd1.h linux/include/asm-arm/arch-integrator/impd1.h --- orig/include/asm-arm/arch-integrator/impd1.h Thu Jan 1 01:00:00 1970 +++ linux/include/asm-arm/arch-integrator/impd1.h Fri Jun 6 20:31:39 2003 @@ -0,0 +1,19 @@ +#define IMPD1_OSC1 0x00 +#define IMPD1_OSC2 0x04 +#define IMPD1_LOCK 0x08 +#define IMPD1_LEDS 0x0c +#define IMPD1_INT 0x10 +#define IMPD1_SW 0x14 +#define IMPD1_CTRL 0x18 + +#define IMPD1_CTRL_DISP_LCD (0 << 0) +#define IMPD1_CTRL_DISP_VGA (1 << 0) +#define IMPD1_CTRL_DISP_LCD1 (2 << 0) +#define IMPD1_CTRL_DISP_ENABLE (1 << 2) +#define IMPD1_CTRL_DISP_MASK (7 << 0) + +struct device; + +void impd1_set_vco(struct device *dev, int vconr, unsigned long period); +void impd1_tweak_control(struct device *dev, u32 mask, u32 val); + diff -urN orig/include/asm-arm/arch-iop3xx/iop321.h linux/include/asm-arm/arch-iop3xx/iop321.h --- orig/include/asm-arm/arch-iop3xx/iop321.h Mon May 5 17:40:06 2003 +++ linux/include/asm-arm/arch-iop3xx/iop321.h Wed Jun 11 13:01:37 2003 @@ -14,9 +14,16 @@ #ifndef _IOP321_HW_H_ #define _IOP321_HW_H_ + +#ifndef __ASSEMBLY__ +#define iop_is_321() ((processor_id & 0xfffff7e0) == 0x69052420) +#endif + + /* * IOP321 I/O and Mem space regions for PCI autoconfiguration */ + #define IOP321_PCI_IO_BASE 0x90000000 #define IOP321_PCI_IO_SIZE 0x00010000 #define IOP321_PCI_MEM_BASE 0x40000000 @@ -26,6 +33,7 @@ * IOP321 chipset registers */ #define IOP321_VIRT_MEM_BASE 0xfff00000 /* chip virtual mem address*/ + #define IOP321_PHY_MEM_BASE 0xffffe000 /* chip physical memory address */ #define IOP321_REG_ADDR(reg) (IOP321_VIRT_MEM_BASE | (reg)) @@ -97,12 +105,93 @@ #define IOP321_PCIIRSR (volatile u32 *)IOP321_REG_ADDR(0x000001EC) /* Messaging Unit 0x00000300 through 0x000003FF */ + +/* Reserved 0x00000300 through 0x0000030c */ +#define IOP321_IMR0 (volatile u32 *)IOP321_REG_ADDR(0x00000310) +#define IOP321_IMR1 (volatile u32 *)IOP321_REG_ADDR(0x00000314) +#define IOP321_OMR0 (volatile u32 *)IOP321_REG_ADDR(0x00000318) +#define IOP321_OMR1 (volatile u32 *)IOP321_REG_ADDR(0x0000031C) +#define IOP321_IDR (volatile u32 *)IOP321_REG_ADDR(0x00000320) +#define IOP321_IISR (volatile u32 *)IOP321_REG_ADDR(0x00000324) +#define IOP321_IIMR (volatile u32 *)IOP321_REG_ADDR(0x00000328) +#define IOP321_ODR (volatile u32 *)IOP321_REG_ADDR(0x0000032C) +#define IOP321_OISR (volatile u32 *)IOP321_REG_ADDR(0x00000330) +#define IOP321_OIMR (volatile u32 *)IOP321_REG_ADDR(0x00000334) +/* Reserved 0x00000338 through 0x0000034F */ +#define IOP321_MUCR (volatile u32 *)IOP321_REG_ADDR(0x00000350) +#define IOP321_QBAR (volatile u32 *)IOP321_REG_ADDR(0x00000354) +/* Reserved 0x00000358 through 0x0000035C */ +#define IOP321_IFHPR (volatile u32 *)IOP321_REG_ADDR(0x00000360) +#define IOP321_IFTPR (volatile u32 *)IOP321_REG_ADDR(0x00000364) +#define IOP321_IPHPR (volatile u32 *)IOP321_REG_ADDR(0x00000368) +#define IOP321_IPTPR (volatile u32 *)IOP321_REG_ADDR(0x0000036C) +#define IOP321_OFHPR (volatile u32 *)IOP321_REG_ADDR(0x00000370) +#define IOP321_OFTPR (volatile u32 *)IOP321_REG_ADDR(0x00000374) +#define IOP321_OPHPR (volatile u32 *)IOP321_REG_ADDR(0x00000378) +#define IOP321_OPTPR (volatile u32 *)IOP321_REG_ADDR(0x0000037C) +#define IOP321_IAR (volatile u32 *)IOP321_REG_ADDR(0x00000380) +/* Reserved 0x00000384 through 0x000003FF */ + /* DMA Controller 0x00000400 through 0x000004FF */ +#define IOP321_DMA0_CCR (volatile u32 *)IOP321_REG_ADDR(0x00000400) +#define IOP321_DMA0_CSR (volatile u32 *)IOP321_REG_ADDR(0x00000404) +#define IOP321_DMA0_DAR (volatile u32 *)IOP321_REG_ADDR(0x0000040C) +#define IOP321_DMA0_NDAR (volatile u32 *)IOP321_REG_ADDR(0x00000410) +#define IOP321_DMA0_PADR (volatile u32 *)IOP321_REG_ADDR(0x00000414) +#define IOP321_DMA0_PUADR (volatile u32 *)IOP321_REG_ADDR(0x00000418) +#define IOP321_DMA0_LADR (volatile u32 *)IOP321_REG_ADDR(0X0000041C) +#define IOP321_DMA0_BCR (volatile u32 *)IOP321_REG_ADDR(0x00000420) +#define IOP321_DMA0_DCR (volatile u32 *)IOP321_REG_ADDR(0x00000424) +/* Reserved 0x00000428 through 0x0000043C */ +#define IOP321_DMA1_CCR (volatile u32 *)IOP321_REG_ADDR(0x00000440) +#define IOP321_DMA1_CSR (volatile u32 *)IOP321_REG_ADDR(0x00000444) +#define IOP321_DMA1_DAR (volatile u32 *)IOP321_REG_ADDR(0x0000044C) +#define IOP321_DMA1_NDAR (volatile u32 *)IOP321_REG_ADDR(0x00000450) +#define IOP321_DMA1_PADR (volatile u32 *)IOP321_REG_ADDR(0x00000454) +#define IOP321_DMA1_PUADR (volatile u32 *)IOP321_REG_ADDR(0x00000458) +#define IOP321_DMA1_LADR (volatile u32 *)IOP321_REG_ADDR(0x0000045C) +#define IOP321_DMA1_BCR (volatile u32 *)IOP321_REG_ADDR(0x00000460) +#define IOP321_DMA1_DCR (volatile u32 *)IOP321_REG_ADDR(0x00000464) +/* Reserved 0x00000468 through 0x000004FF */ + /* Memory controller 0x00000500 through 0x0005FF */ + /* Peripheral bus interface unit 0x00000680 through 0x0006FF */ +#define IOP321_PBCR (volatile u32 *)IOP321_REG_ADDR(0x00000680) +#define IOP321_PBISR (volatile u32 *)IOP321_REG_ADDR(0x00000684) +#define IOP321_PBBAR0 (volatile u32 *)IOP321_REG_ADDR(0x00000688) +#define IOP321_PBLR0 (volatile u32 *)IOP321_REG_ADDR(0x0000068C) +#define IOP321_PBBAR1 (volatile u32 *)IOP321_REG_ADDR(0x00000690) +#define IOP321_PBLR1 (volatile u32 *)IOP321_REG_ADDR(0x00000694) +#define IOP321_PBBAR2 (volatile u32 *)IOP321_REG_ADDR(0x00000698) +#define IOP321_PBLR2 (volatile u32 *)IOP321_REG_ADDR(0x0000069C) +#define IOP321_PBBAR3 (volatile u32 *)IOP321_REG_ADDR(0x000006A0) +#define IOP321_PBLR3 (volatile u32 *)IOP321_REG_ADDR(0x000006A4) +#define IOP321_PBBAR4 (volatile u32 *)IOP321_REG_ADDR(0x000006A8) +#define IOP321_PBLR4 (volatile u32 *)IOP321_REG_ADDR(0x000006AC) +#define IOP321_PBBAR5 (volatile u32 *)IOP321_REG_ADDR(0x000006B0) +#define IOP321_PBLR5 (volatile u32 *)IOP321_REG_ADDR(0x000006B4) +#define IOP321_PBDSCR (volatile u32 *)IOP321_REG_ADDR(0x000006B8) +/* Reserved 0x000006BC */ +#define IOP321_PMBR0 (volatile u32 *)IOP321_REG_ADDR(0x000006C0) +/* Reserved 0x000006C4 through 0x000006DC */ +#define IOP321_PMBR1 (volatile u32 *)IOP321_REG_ADDR(0x000006E0) +#define IOP321_PMBR2 (volatile u32 *)IOP321_REG_ADDR(0x000006E4) + +#define IOP321_PBCR_EN 0x1 + +#define IOP321_PBISR_BOOR_ERR 0x1 + + + /* Peripheral performance monitoring unit 0x00000700 through 0x00077F */ /* Internal arbitration unit 0x00000780 through 0x0007BF */ +/* General Purpose I/O Registers */ +#define IOP321_GPOE (volatile u32 *)IOP321_REG_ADDR(0x000007C4) +#define IOP321_GPID (volatile u32 *)IOP321_REG_ADDR(0x000007C8) +#define IOP321_GPOD (volatile u32 *)IOP321_REG_ADDR(0x000007CC) + /* Interrupt Controller */ #define IOP321_INTCTL (volatile u32 *)IOP321_REG_ADDR(0x000007D0) #define IOP321_INTSTR (volatile u32 *)IOP321_REG_ADDR(0x000007D4) @@ -131,11 +220,40 @@ #define IOP321_TU_TISR (volatile u32 *)IOP321_REG_ADDR(0x000007F8) #define IOP321_TU_WDTCR (volatile u32 *)IOP321_REG_ADDR(0x000007FC) + + /* Application accelerator unit 0x00000800 - 0x000008FF */ -#define IOP321_AAUACR (volatile u32 *)IOP321_REG_ADDR(0x00000800) -#define IOP321_AAUASR (volatile u32 *)IOP321_REG_ADDR(0x00000804) -#define IOP321_AAUANDAR (volatile u32 *)IOP321_REG_ADDR(0x0000080C) +#define IOP321_AAU_ACR (volatile u32 *)IOP321_REG_ADDR(0x00000800) +#define IOP321_AAU_ASR (volatile u32 *)IOP321_REG_ADDR(0x00000804) +#define IOP321_AAU_ADAR (volatile u32 *)IOP321_REG_ADDR(0x00000808) +#define IOP321_AAU_ANDAR (volatile u32 *)IOP321_REG_ADDR(0x0000080C) +#define IOP321_AAU_SAR1 (volatile u32 *)IOP321_REG_ADDR(0x00000810) +/* SAR2...SAR32 0x00000814 - 0x000008A4 */ +#define IOP321_AAU_DAR (volatile u32 *)IOP321_REG_ADDR(0x00000820) +#define IOP321_AAU_ABCR (volatile u32 *)IOP321_REG_ADDR(0x00000824) +#define IOP321_AAU_ADCR (volatile u32 *)IOP321_REG_ADDR(0x00000828) +#define IOP321_AAU_EDCR0 (volatile u32 *)IOP321_REG_ADDR(0x0000083c) +#define IOP321_AAU_EDCR1 (volatile u32 *)IOP321_REG_ADDR(0x00000860) +#define IOP321_AAU_EDCR2 (volatile u32 *)IOP321_REG_ADDR(0x00000884) + /* SSP serial port unit 0x00001600 - 0x0000167F */ /* I2C bus interface unit 0x00001680 - 0x000016FF */ +#define IOP321_ICR0 (volatile u32 *)IOP321_REG_ADDR(0x00001680) +#define IOP321_ISR0 (volatile u32 *)IOP321_REG_ADDR(0x00001684) +#define IOP321_ISAR0 (volatile u32 *)IOP321_REG_ADDR(0x00001688) +#define IOP321_IDBR0 (volatile u32 *)IOP321_REG_ADDR(0x0000168C) +/* Reserved 0x00001690 */ +#define IOP321_IBMR0 (volatile u32 *)IOP321_REG_ADDR(0x00001694) +/* Reserved 0x00001698 */ +/* Reserved 0x0000169C */ +#define IOP321_ICR1 (volatile u32 *)IOP321_REG_ADDR(0x000016A0) +#define IOP321_ISR1 (volatile u32 *)IOP321_REG_ADDR(0x000016A4) +#define IOP321_ISAR1 (volatile u32 *)IOP321_REG_ADDR(0x000016A8) +#define IOP321_IDBR1 (volatile u32 *)IOP321_REG_ADDR(0x000016AC) +#define IOP321_IBMR1 (volatile u32 *)IOP321_REG_ADDR(0x000016B4) +/* Reserved 0x000016B8 through 0x000016FC */ + +/* for I2C bit defs see drivers/i2c/i2c-iop3xx.h */ + #endif // _IOP321_HW_H_ diff -urN orig/include/asm-arm/arch-sa1100/time.h linux/include/asm-arm/arch-sa1100/time.h --- orig/include/asm-arm/arch-sa1100/time.h Tue May 27 10:05:33 2003 +++ linux/include/asm-arm/arch-sa1100/time.h Tue May 13 16:11:37 2003 @@ -87,6 +87,8 @@ do_profile(regs); + OSMR1 = OSCR + LATCH * 2; + return IRQ_HANDLED; } diff -urN orig/include/asm-arm/dma-mapping.h linux/include/asm-arm/dma-mapping.h --- orig/include/asm-arm/dma-mapping.h Mon May 5 17:40:06 2003 +++ linux/include/asm-arm/dma-mapping.h Wed Jun 4 11:53:15 2003 @@ -42,12 +42,12 @@ /* * Return whether the given device DMA address mask can be supported * properly. For example, if your device can only drive the low 24-bits - * during PCI bus mastering, then you would pass 0x00ffffff as the mask + * during bus mastering, then you would pass 0x00ffffff as the mask * to this function. */ static inline int dma_supported(struct device *dev, u64 mask) { - return 1; + return dev->dma_mask && *dev->dma_mask != 0; } static inline int dma_set_mask(struct device *dev, u64 dma_mask) @@ -81,14 +81,8 @@ * return the CPU-viewed address, and sets @handle to be the * device-viewed address. */ -static inline void * -dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, int gfp) -{ - if (dev == NULL || dmadev_is_sa1111(dev) || *dev->dma_mask != 0xffffffff) - gfp |= GFP_DMA; - - return consistent_alloc(gfp, size, handle, 0); -} +extern void * +dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, int gfp); /** * dma_free_coherent - free memory allocated by dma_alloc_coherent diff -urN orig/include/asm-arm/glue.h linux/include/asm-arm/glue.h --- orig/include/asm-arm/glue.h Wed Oct 16 09:17:08 2002 +++ linux/include/asm-arm/glue.h Sun Nov 10 12:10:50 2002 @@ -87,7 +87,7 @@ # ifdef CPU_ABORT_HANDLER # define MULTI_ABORT 1 # else -# define CPU_ABORT_HANDLER v5tej_early_abort +# define CPU_ABORT_HANDLER v5tj_early_abort # endif #endif @@ -95,7 +95,7 @@ # ifdef CPU_ABORT_HANDLER # define MULTI_ABORT 1 # else -# define CPU_ABORT_HANDLER xscale_abort +# define CPU_ABORT_HANDLER ev5t_early_abort # endif #endif diff -urN orig/include/asm-arm/hardware/amba_serial.h linux/include/asm-arm/hardware/amba_serial.h --- orig/include/asm-arm/hardware/amba_serial.h Thu Jan 1 01:00:00 1970 +++ linux/include/asm-arm/hardware/amba_serial.h Fri May 23 16:49:17 2003 @@ -0,0 +1,168 @@ +/* + * linux/include/asm-arm/hardware/serial_amba.h + * + * Internal header file for AMBA serial ports + * + * Copyright (C) ARM Limited + * Copyright (C) 2000 Deep Blue Solutions 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef ASM_ARM_HARDWARE_SERIAL_AMBA_H +#define ASM_ARM_HARDWARE_SERIAL_AMBA_H + +/* ------------------------------------------------------------------------------- + * From AMBA UART (PL010) Block Specification + * ------------------------------------------------------------------------------- + * UART Register Offsets. + */ +#define UART01x_DR 0x00 /* Data read or written from the interface. */ +#define UART01x_RSR 0x04 /* Receive status register (Read). */ +#define UART01x_ECR 0x04 /* Error clear register (Write). */ +#define UART010_LCRH 0x08 /* Line control register, high byte. */ +#define UART010_LCRM 0x0C /* Line control register, middle byte. */ +#define UART010_LCRL 0x10 /* Line control register, low byte. */ +#define UART010_CR 0x14 /* Control register. */ +#define UART01x_FR 0x18 /* Flag register (Read only). */ +#define UART010_IIR 0x1C /* Interrupt indentification register (Read). */ +#define UART010_ICR 0x1C /* Interrupt clear register (Write). */ +#define UART01x_ILPR 0x20 /* IrDA low power counter register. */ +#define UART011_IBRD 0x24 /* Integer baud rate divisor register. */ +#define UART011_FBRD 0x28 /* Fractional baud rate divisor register. */ +#define UART011_LCRH 0x2c /* Line control register. */ +#define UART011_CR 0x30 /* Control register. */ +#define UART011_IFLS 0x34 /* Interrupt fifo level select. */ +#define UART011_IMSC 0x38 /* Interrupt mask. */ +#define UART011_RIS 0x3c /* Raw interrupt status. */ +#define UART011_MIS 0x40 /* Masked interrupt status. */ +#define UART011_ICR 0x44 /* Interrupt clear register. */ +#define UART011_DMACR 0x48 /* DMA control register. */ +#define UART011_PID0 0xfe0 /* Peripheral ID reg. */ +#define UART011_PID1 0xfe4 +#define UART011_PID2 0xfe8 +#define UART011_PID3 0xfec +#define UART011_CID0 0xfe0 /* Primecell ID reg. */ +#define UART011_CID1 0xfe4 +#define UART011_CID2 0xfe8 +#define UART011_CID3 0xfec + +#define UART01x_RSR_OE 0x08 +#define UART01x_RSR_BE 0x04 +#define UART01x_RSR_PE 0x02 +#define UART01x_RSR_FE 0x01 + +#define UART011_FR_RI 0x100 +#define UART011_FR_TXFE 0x080 +#define UART011_FR_RXFF 0x040 +#define UART01x_FR_TXFF 0x020 +#define UART01x_FR_RXFE 0x010 +#define UART01x_FR_BUSY 0x008 +#define UART01x_FR_DCD 0x004 +#define UART01x_FR_DSR 0x002 +#define UART01x_FR_CTS 0x001 +#define UART01x_FR_TMSK (UART01x_FR_TXFF + UART01x_FR_BUSY) + +#define UART011_CR_CTSEN 0x8000 /* CTS hardware flow control */ +#define UART011_CR_RTSEN 0x4000 /* RTS hardware flow control */ +#define UART011_CR_OUT2 0x2000 /* OUT2 */ +#define UART011_CR_OUT1 0x1000 /* OUT1 */ +#define UART011_CR_RTS 0x0800 /* RTS */ +#define UART011_CR_DTR 0x0400 /* DTR */ +#define UART011_CR_RXE 0x0200 /* receive enable */ +#define UART011_CR_TXE 0x0100 /* transmit enable */ +#define UART011_CR_LBE 0x0080 /* loopback enable */ +#define UART010_CR_RTIE 0x0040 +#define UART010_CR_TIE 0x0020 +#define UART010_CR_RIE 0x0010 +#define UART010_CR_MSIE 0x0008 +#define UART01x_CR_IIRLP 0x0004 /* SIR low power mode */ +#define UART01x_CR_SIREN 0x0002 /* SIR enable */ +#define UART01x_CR_UARTEN 0x0001 /* UART enable */ + +#define UART011_LCRH_SPS 0x80 +#define UART01x_LCRH_WLEN_8 0x60 +#define UART01x_LCRH_WLEN_7 0x40 +#define UART01x_LCRH_WLEN_6 0x20 +#define UART01x_LCRH_WLEN_5 0x00 +#define UART01x_LCRH_FEN 0x10 +#define UART01x_LCRH_STP2 0x08 +#define UART01x_LCRH_EPS 0x04 +#define UART01x_LCRH_PEN 0x02 +#define UART01x_LCRH_BRK 0x01 + +#define UART010_IIR_RTIS 0x08 +#define UART010_IIR_TIS 0x04 +#define UART010_IIR_RIS 0x02 +#define UART010_IIR_MIS 0x01 + +#define UART011_IFLS_RX1_8 (0 << 3) +#define UART011_IFLS_RX2_8 (1 << 3) +#define UART011_IFLS_RX4_8 (2 << 3) +#define UART011_IFLS_RX6_8 (3 << 3) +#define UART011_IFLS_RX7_8 (4 << 3) +#define UART011_IFLS_TX1_8 (0 << 0) +#define UART011_IFLS_TX2_8 (1 << 0) +#define UART011_IFLS_TX4_8 (2 << 0) +#define UART011_IFLS_TX6_8 (3 << 0) +#define UART011_IFLS_TX7_8 (4 << 0) + +#define UART011_OEIM (1 << 10) /* overrun error interrupt mask */ +#define UART011_BEIM (1 << 9) /* break error interrupt mask */ +#define UART011_PEIM (1 << 8) /* parity error interrupt mask */ +#define UART011_FEIM (1 << 7) /* framing error interrupt mask */ +#define UART011_RTIM (1 << 6) /* receive timeout interrupt mask */ +#define UART011_TXIM (1 << 5) /* transmit interrupt mask */ +#define UART011_RXIM (1 << 4) /* receive interrupt mask */ +#define UART011_DSRMIM (1 << 3) /* DSR interrupt mask */ +#define UART011_DCDMIM (1 << 2) /* DCD interrupt mask */ +#define UART011_CTSMIM (1 << 1) /* CTS interrupt mask */ +#define UART011_RIMIM (1 << 0) /* RI interrupt mask */ + +#define UART011_OEIS (1 << 10) /* overrun error interrupt status */ +#define UART011_BEIS (1 << 9) /* break error interrupt status */ +#define UART011_PEIS (1 << 8) /* parity error interrupt status */ +#define UART011_FEIS (1 << 7) /* framing error interrupt status */ +#define UART011_RTIS (1 << 6) /* receive timeout interrupt status */ +#define UART011_TXIS (1 << 5) /* transmit interrupt status */ +#define UART011_RXIS (1 << 4) /* receive interrupt status */ +#define UART011_DSRMIS (1 << 3) /* DSR interrupt status */ +#define UART011_DCDMIS (1 << 2) /* DCD interrupt status */ +#define UART011_CTSMIS (1 << 1) /* CTS interrupt status */ +#define UART011_RIMIS (1 << 0) /* RI interrupt status */ + +#define UART011_OEIC (1 << 10) /* overrun error interrupt clear */ +#define UART011_BEIC (1 << 9) /* break error interrupt clear */ +#define UART011_PEIC (1 << 8) /* parity error interrupt clear */ +#define UART011_FEIC (1 << 7) /* framing error interrupt clear */ +#define UART011_RTIC (1 << 6) /* receive timeout interrupt clear */ +#define UART011_TXIC (1 << 5) /* transmit interrupt clear */ +#define UART011_RXIC (1 << 4) /* receive interrupt clear */ +#define UART011_DSRMIC (1 << 3) /* DSR interrupt clear */ +#define UART011_DCDMIC (1 << 2) /* DCD interrupt clear */ +#define UART011_CTSMIC (1 << 1) /* CTS interrupt clear */ +#define UART011_RIMIC (1 << 0) /* RI interrupt clear */ + +#define UART011_DMAONERR (1 << 2) /* disable dma on error */ +#define UART011_TXDMAE (1 << 1) /* enable transmit dma */ +#define UART011_RXDMAE (1 << 0) /* enable receive dma */ + +#define UART011_PID_VALUE (0x00000011) /* PID value */ +#define UART011_PID_MASK (0xfff00fff) /* PID mask */ +#define UART011_CID_VALUE (0xb105f00d) /* CID */ + +#define UART01x_RSR_ANY (UART01x_RSR_OE|UART01x_RSR_BE|UART01x_RSR_PE|UART01x_RSR_FE) +#define UART01x_FR_MODEM_ANY (UART01x_FR_DCD|UART01x_FR_DSR|UART01x_FR_CTS) + +#endif diff -urN orig/include/asm-arm/hardware/icst525.h linux/include/asm-arm/hardware/icst525.h --- orig/include/asm-arm/hardware/icst525.h Thu Jan 1 01:00:00 1970 +++ linux/include/asm-arm/hardware/icst525.h Fri May 30 23:25:48 2003 @@ -0,0 +1,36 @@ +/* + * linux/include/asm-arm/hardware/icst525.h + * + * Copyright (C) 2003 Deep Blue Solutions, Ltd, 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. + * + * Support functions for calculating clocks/divisors for the ICST525 + * clock generators. See http://www.icst.com/ for more information + * on these devices. + */ +#ifndef ASMARM_HARDWARE_ICST525_H +#define ASMARM_HARDWARE_ICST525_H + +struct icst525_params { + unsigned long ref; + unsigned long vco_max; /* inclusive */ + unsigned short vd_min; /* inclusive */ + unsigned short vd_max; /* inclusive */ + unsigned char rd_min; /* inclusive */ + unsigned char rd_max; /* inclusive */ +}; + +struct icst525_vco { + unsigned short v; + unsigned char r; + unsigned char s; +}; + +unsigned long icst525_khz(const struct icst525_params *p, struct icst525_vco vco); +struct icst525_vco icst525_khz_to_vco(const struct icst525_params *p, unsigned long freq); +struct icst525_vco icst525_ps_to_vco(const struct icst525_params *p, unsigned long period); + +#endif diff -urN orig/include/asm-arm/hardware/serial_amba.h linux/include/asm-arm/hardware/serial_amba.h --- orig/include/asm-arm/hardware/serial_amba.h Sat Mar 31 23:47:42 2001 +++ linux/include/asm-arm/hardware/serial_amba.h Thu Jan 1 01:00:00 1970 @@ -1,94 +0,0 @@ -/* - * linux/include/asm-arm/hardware/serial_amba.h - * - * Internal header file for AMBA serial ports - * - * Copyright (C) ARM Limited - * Copyright (C) 2000 Deep Blue Solutions 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#ifndef ASM_ARM_HARDWARE_SERIAL_AMBA_H -#define ASM_ARM_HARDWARE_SERIAL_AMBA_H - -/* ------------------------------------------------------------------------------- - * From AMBA UART (PL010) Block Specification (ARM-0001-CUST-DSPC-A03) - * ------------------------------------------------------------------------------- - * UART Register Offsets. - */ -#define AMBA_UARTDR 0x00 /* Data read or written from the interface. */ -#define AMBA_UARTRSR 0x04 /* Receive status register (Read). */ -#define AMBA_UARTECR 0x04 /* Error clear register (Write). */ -#define AMBA_UARTLCR_H 0x08 /* Line control register, high byte. */ -#define AMBA_UARTLCR_M 0x0C /* Line control register, middle byte. */ -#define AMBA_UARTLCR_L 0x10 /* Line control register, low byte. */ -#define AMBA_UARTCR 0x14 /* Control register. */ -#define AMBA_UARTFR 0x18 /* Flag register (Read only). */ -#define AMBA_UARTIIR 0x1C /* Interrupt indentification register (Read). */ -#define AMBA_UARTICR 0x1C /* Interrupt clear register (Write). */ -#define AMBA_UARTILPR 0x20 /* IrDA low power counter register. */ - -#define AMBA_UARTRSR_OE 0x08 -#define AMBA_UARTRSR_BE 0x04 -#define AMBA_UARTRSR_PE 0x02 -#define AMBA_UARTRSR_FE 0x01 - -#define AMBA_UARTFR_TXFF 0x20 -#define AMBA_UARTFR_RXFE 0x10 -#define AMBA_UARTFR_BUSY 0x08 -#define AMBA_UARTFR_DCD 0x04 -#define AMBA_UARTFR_DSR 0x02 -#define AMBA_UARTFR_CTS 0x01 -#define AMBA_UARTFR_TMSK (AMBA_UARTFR_TXFF + AMBA_UARTFR_BUSY) - -#define AMBA_UARTCR_RTIE 0x40 -#define AMBA_UARTCR_TIE 0x20 -#define AMBA_UARTCR_RIE 0x10 -#define AMBA_UARTCR_MSIE 0x08 -#define AMBA_UARTCR_IIRLP 0x04 -#define AMBA_UARTCR_SIREN 0x02 -#define AMBA_UARTCR_UARTEN 0x01 - -#define AMBA_UARTLCR_H_WLEN_8 0x60 -#define AMBA_UARTLCR_H_WLEN_7 0x40 -#define AMBA_UARTLCR_H_WLEN_6 0x20 -#define AMBA_UARTLCR_H_WLEN_5 0x00 -#define AMBA_UARTLCR_H_FEN 0x10 -#define AMBA_UARTLCR_H_STP2 0x08 -#define AMBA_UARTLCR_H_EPS 0x04 -#define AMBA_UARTLCR_H_PEN 0x02 -#define AMBA_UARTLCR_H_BRK 0x01 - -#define AMBA_UARTIIR_RTIS 0x08 -#define AMBA_UARTIIR_TIS 0x04 -#define AMBA_UARTIIR_RIS 0x02 -#define AMBA_UARTIIR_MIS 0x01 - -#define ARM_BAUD_460800 1 -#define ARM_BAUD_230400 3 -#define ARM_BAUD_115200 7 -#define ARM_BAUD_57600 15 -#define ARM_BAUD_38400 23 -#define ARM_BAUD_19200 47 -#define ARM_BAUD_14400 63 -#define ARM_BAUD_9600 95 -#define ARM_BAUD_4800 191 -#define ARM_BAUD_2400 383 -#define ARM_BAUD_1200 767 - -#define AMBA_UARTRSR_ANY (AMBA_UARTRSR_OE|AMBA_UARTRSR_BE|AMBA_UARTRSR_PE|AMBA_UARTRSR_FE) -#define AMBA_UARTFR_MODEM_ANY (AMBA_UARTFR_DCD|AMBA_UARTFR_DSR|AMBA_UARTFR_CTS) - -#endif diff -urN orig/include/asm-arm/hardware.h linux/include/asm-arm/hardware.h --- orig/include/asm-arm/hardware.h Wed Dec 13 00:00:24 2000 +++ linux/include/asm-arm/hardware.h Wed Jun 4 18:44:28 2003 @@ -15,4 +15,12 @@ #include +#ifndef __ASSEMBLY__ + +struct platform_device; + +extern int platform_add_devices(struct platform_device *, int nr); + +#endif + #endif diff -urN orig/include/asm-arm/memory.h linux/include/asm-arm/memory.h --- orig/include/asm-arm/memory.h Fri Jan 3 12:11:04 2003 +++ linux/include/asm-arm/memory.h Mon Jun 9 19:58:59 2003 @@ -27,6 +27,9 @@ /* * These are *only* valid on the kernel direct mapped RAM memory. + * Note: Drivers should NOT use these. They are the wrong + * translation for translating DMA addresses. Use the driver + * DMA support - see dma-mapping.h. */ static inline unsigned long virt_to_phys(void *x) { @@ -38,6 +41,9 @@ return (void *)(__phys_to_virt((unsigned long)(x))); } +/* + * Drivers should NOT use these either. + */ #define __pa(x) __virt_to_phys((unsigned long)(x)) #define __va(x) ((void *)__phys_to_virt((unsigned long)(x))) @@ -72,7 +78,7 @@ #define pfn_valid(pfn) ((pfn) >= PHYS_PFN_OFFSET && (pfn) < (PHYS_PFN_OFFSET + max_mapnr)) #define virt_to_page(kaddr) (pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)) -#define virt_addr_valid(kaddr) ((kaddr) >= PAGE_OFFSET && (kaddr) < (unsigned long)high_memory) +#define virt_addr_valid(kaddr) ((unsigned long)(kaddr) >= PAGE_OFFSET && (unsigned long)(kaddr) < (unsigned long)high_memory) #define PHYS_TO_NID(addr) (0) diff -urN orig/include/asm-arm/uaccess.h linux/include/asm-arm/uaccess.h --- orig/include/asm-arm/uaccess.h Sun Apr 20 16:32:20 2003 +++ linux/include/asm-arm/uaccess.h Sat May 17 23:09:19 2003 @@ -144,13 +144,13 @@ register int __e asm("r0"); \ switch (sizeof(*(__p))) { \ case 1: \ - __put_user_x(__r1, __p, __e, 1, "r2", "lr"); \ + __put_user_x(__r1, __p, __e, 1, "ip", "lr"); \ break; \ case 2: \ - __put_user_x(__r1, __p, __e, 2, "r2", "lr"); \ + __put_user_x(__r1, __p, __e, 2, "ip", "lr"); \ break; \ case 4: \ - __put_user_x(__r1, __p, __e, 4, "r2", "lr"); \ + __put_user_x(__r1, __p, __e, 4, "ip", "lr"); \ break; \ case 8: \ __put_user_x(__r1, __p, __e, 8, "ip", "lr"); \ diff -urN orig/include/asm-i386/ide.h linux/include/asm-i386/ide.h --- orig/include/asm-i386/ide.h Sat Apr 12 14:47:06 2003 +++ linux/include/asm-i386/ide.h Sat Apr 12 15:51:58 2003 @@ -23,41 +23,8 @@ # endif #endif -static __inline__ int ide_default_irq(unsigned long base) -{ - switch (base) { -#ifdef CONFIG_X86_PC9800 - case 0x640: return 9; -#endif - case 0x1f0: return 14; - case 0x170: return 15; - case 0x1e8: return 11; - case 0x168: return 10; - case 0x1e0: return 8; - case 0x160: return 12; - default: - return 0; - } -} - -static __inline__ unsigned long ide_default_io_base(int index) -{ - switch (index) { -#ifdef CONFIG_X86_PC9800 - case 0: - case 1: return 0x640; -#else - case 0: return 0x1f0; - case 1: return 0x170; - case 2: return 0x1e8; - case 3: return 0x168; - case 4: return 0x1e0; - case 5: return 0x160; -#endif - default: - return 0; - } -} +#define ide_default_io_base(i) (0) +#define ide_default_irq(b) (0) static __inline__ void ide_init_hwif_ports(hw_regs_t *hw, unsigned long data_port, unsigned long ctrl_port, int *irq) @@ -68,6 +35,7 @@ unsigned long increment = data_port == 0x640 ? 2 : 1; #endif + memset(hw, 0, sizeof(*hw)); for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { hw->io_ports[i] = reg; #ifdef CONFIG_X86_PC9800 @@ -94,14 +62,31 @@ { #ifndef CONFIG_BLK_DEV_IDEPCI hw_regs_t hw; - int index; - for(index = 0; index < MAX_HWIFS; index++) { - memset(&hw, 0, sizeof hw); - ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, NULL); - hw.irq = ide_default_irq(ide_default_io_base(index)); - ide_register_hw(&hw, NULL); - } +#ifdef CONFIG_X86_PC9800 + ide_init_hwif_ports(&hw, 0x640, 0, NULL); + hw.irq = 9; + ide_register_hw(&hw, NULL); +#else + ide_init_hwif_ports(&hw, 0x1f0, 0, NULL); + hw.irq = 14; + ide_register_hw(&hw, NULL); + ide_init_hwif_ports(&hw, 0x170, 0, NULL); + hw.irq = 15; + ide_register_hw(&hw, NULL); + ide_init_hwif_ports(&hw, 0x1e8, 0, NULL); + hw.irq = 11; + ide_register_hw(&hw, NULL); + ide_init_hwif_ports(&hw, 0x168, 0, NULL); + hw.irq = 10; + ide_register_hw(&hw, NULL); + ide_init_hwif_ports(&hw, 0x1e0, 0, NULL); + hw.irq = 8; + ide_register_hw(&hw, NULL); + ide_init_hwif_ports(&hw, 0x160, 0, NULL); + hw.irq = 12; + ide_register_hw(&hw, NULL); +#endif #endif /* CONFIG_BLK_DEV_IDEPCI */ } diff -urN orig/include/asm-sparc64/ide.h linux/include/asm-sparc64/ide.h --- orig/include/asm-sparc64/ide.h Mon Mar 24 23:47:30 2003 +++ linux/include/asm-sparc64/ide.h Tue Mar 25 00:01:18 2003 @@ -25,21 +25,12 @@ # endif #endif -static __inline__ int ide_default_irq(unsigned long base) -{ - return 0; -} - -static __inline__ unsigned long ide_default_io_base(int index) -{ - return 0; -} - static __inline__ void ide_init_hwif_ports(hw_regs_t *hw, unsigned long data_port, unsigned long ctrl_port, int *irq) { unsigned long reg = data_port; int i; + memset(&hw, 0, sizeof(hw)); for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { hw->io_ports[i] = reg; reg += 1; @@ -60,17 +51,6 @@ */ static __inline__ void ide_init_default_hwifs(void) { -#ifndef CONFIG_BLK_DEV_IDEPCI - hw_regs_t hw; - int index; - - for (index = 0; index < MAX_HWIFS; index++) { - memset(&hw, 0, sizeof hw); - ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, NULL); - hw.irq = ide_default_irq(ide_default_io_base(index)); - ide_register_hw(&hw, NULL); - } -#endif /* CONFIG_BLK_DEV_IDEPCI */ } #define __ide_insl(data_reg, buffer, wcount) \ diff -urN orig/include/asm-v850/pci.h linux/include/asm-v850/pci.h --- orig/include/asm-v850/pci.h Tue Feb 11 16:10:53 2003 +++ linux/include/asm-v850/pci.h Wed Mar 12 19:48:33 2003 @@ -74,4 +74,8 @@ pci_free_consistent (struct pci_dev *pdev, size_t size, void *cpu_addr, dma_addr_t dma_addr); +extern void +pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res); + #endif /* __V850_PCI_H__ */ diff -urN orig/include/linux/device.h linux/include/linux/device.h --- orig/include/linux/device.h Tue May 27 10:05:39 2003 +++ linux/include/linux/device.h Wed Jun 4 23:07:57 2003 @@ -394,6 +394,8 @@ struct resource * resource; }; +#define to_platform_device(x) container_of((x), struct platform_device, dev) + extern int platform_device_register(struct platform_device *); extern void platform_device_unregister(struct platform_device *); diff -urN orig/include/linux/ioport.h linux/include/linux/ioport.h --- orig/include/linux/ioport.h Mon Mar 24 23:47:30 2003 +++ linux/include/linux/ioport.h Tue Mar 25 00:14:56 2003 @@ -41,7 +41,6 @@ #define IORESOURCE_CACHEABLE 0x00004000 #define IORESOURCE_RANGELENGTH 0x00008000 #define IORESOURCE_SHADOWABLE 0x00010000 -#define IORESOURCE_BUS_HAS_VGA 0x00080000 #define IORESOURCE_UNSET 0x20000000 #define IORESOURCE_AUTO 0x40000000 diff -urN orig/include/linux/l3/algo-bit.h linux/include/linux/l3/algo-bit.h --- orig/include/linux/l3/algo-bit.h Thu Jan 1 01:00:00 1970 +++ linux/include/linux/l3/algo-bit.h Tue Oct 23 20:12:46 2001 @@ -0,0 +1,39 @@ +/* + * linux/include/linux/l3/algo-bit.h + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * L3 Bus bit-banging algorithm. Derived from i2c-algo-bit.h by + * Simon G. Vogl. + */ +#ifndef L3_ALGO_BIT_H +#define L3_ALGO_BIT_H 1 + +#include + +struct l3_algo_bit_data { + void (*setdat) (void *data, int state); + void (*setclk) (void *data, int state); + void (*setmode)(void *data, int state); + void (*setdir) (void *data, int in); /* set data direction */ + int (*getdat) (void *data); + + void *data; + + /* bus timings (us) */ + int data_hold; + int data_setup; + int clock_high; + int mode_hold; + int mode_setup; + int mode; +}; + +int l3_bit_add_bus(struct l3_adapter *); +int l3_bit_del_bus(struct l3_adapter *); + +#endif diff -urN orig/include/linux/l3/l3.h linux/include/linux/l3/l3.h --- orig/include/linux/l3/l3.h Thu Jan 1 01:00:00 1970 +++ linux/include/linux/l3/l3.h Sun Aug 5 19:49:09 2001 @@ -0,0 +1,208 @@ +/* + * linux/include/linux/l3/l3.h + * + * Copyright (C) 2001 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + * + * Derived from i2c.h by Simon G. Vogl + */ +#ifndef L3_H +#define L3_H + +struct l3_msg { + unsigned char addr; /* slave address */ + unsigned char flags; +#define L3_M_RD 0x01 +#define L3_M_NOADDR 0x02 + unsigned short len; /* msg length */ + unsigned char *buf; /* pointer to msg data */ +}; + +#ifdef __KERNEL__ + +#include +#include + +struct l3_client; + +struct l3_ops { + int (*open)(struct l3_client *); + int (*command)(struct l3_client *, int cmd, void *arg); + void (*close)(struct l3_client *); +}; + +/* + * A driver is capable of handling one or more physical devices present on + * L3 adapters. This information is used to inform the driver of adapter + * events. + */ +struct l3_driver { + /* + * This name is used to uniquely identify the driver. + * It should be the same as the module name. + */ + char name[32]; + + /* + * Notifies the driver that a new client wishes to use its + * services. Note that the module use count will be increased + * prior to this function being called. In addition, the + * clients driver and adapter fields will have been setup. + */ + int (*attach_client)(struct l3_client *); + + /* + * Notifies the driver that the client has finished with its + * services, and any memory that it allocated for this client + * should be cleaned up. In addition the chip should be + * shut down. + */ + void (*detach_client)(struct l3_client *); + + /* + * Possible operations on the driver. + */ + struct l3_ops *ops; + + /* + * Module structure, if any. + */ + struct module *owner; + + /* + * drivers list + */ + struct list_head drivers; +}; + +struct l3_adapter; + +struct l3_algorithm { + /* textual description */ + char name[32]; + + /* perform bus transactions */ + int (*xfer)(struct l3_adapter *, struct l3_msg msgs[], int num); +}; + +struct semaphore; + +/* + * l3_adapter is the structure used to identify a physical L3 bus along + * with the access algorithms necessary to access it. + */ +struct l3_adapter { + /* + * This name is used to uniquely identify the adapter. + * It should be the same as the module name. + */ + char name[32]; + + /* + * the algorithm to access the bus + */ + struct l3_algorithm *algo; + + /* + * Algorithm specific data + */ + void *algo_data; + + /* + * This may be NULL, or should point to the module struct + */ + struct module *owner; + + /* + * private data for the adapter + */ + void *data; + + /* + * Our lock. Unlike the i2c layer, we allow this to be used for + * other stuff, like the i2c layer lock. Some people implement + * i2c stuff using the same signals as the l3 bus. + */ + struct semaphore *lock; + + /* + * List of attached clients. + */ + struct list_head clients; + + /* + * List of all adapters. + */ + struct list_head adapters; +}; + +/* + * l3_client identifies a single device (i.e. chip) that is connected to an + * L3 bus. The behaviour is defined by the routines of the driver. This + * function is mainly used for lookup & other admin. functions. + */ +struct l3_client { + struct l3_adapter *adapter; /* the adapter we sit on */ + struct l3_driver *driver; /* and our access routines */ + void *driver_data; /* private driver data */ + struct list_head __adap; +}; + + +extern int l3_add_adapter(struct l3_adapter *); +extern int l3_del_adapter(struct l3_adapter *); + +extern int l3_add_driver(struct l3_driver *); +extern int l3_del_driver(struct l3_driver *); + +extern int l3_attach_client(struct l3_client *, const char *, const char *); +extern int l3_detach_client(struct l3_client *); + +extern int l3_transfer(struct l3_adapter *, struct l3_msg msgs[], int); +extern int l3_write(struct l3_client *, int, const char *, int); +extern int l3_read(struct l3_client *, int, char *, int); + +/** + * l3_command - send a command to a L3 device driver + * @client: registered client structure + * @cmd: device driver command + * @arg: device driver arguments + * + * Ask the L3 device driver to perform some function. Further information + * should be sought from the device driver in question. + * + * Returns negative error code on failure. + */ +static inline int l3_command(struct l3_client *clnt, int cmd, void *arg) +{ + struct l3_ops *ops = clnt->driver->ops; + int ret = -EINVAL; + + if (ops && ops->command) + ret = ops->command(clnt, cmd, arg); + + return ret; +} + +static inline int l3_open(struct l3_client *clnt) +{ + struct l3_ops *ops = clnt->driver->ops; + int ret = 0; + + if (ops && ops->open) + ret = ops->open(clnt); + return ret; +} + +static inline void l3_close(struct l3_client *clnt) +{ + struct l3_ops *ops = clnt->driver->ops; + if (ops && ops->close) + ops->close(clnt); +} +#endif + +#endif /* L3_H */ diff -urN orig/include/linux/l3/uda1341.h linux/include/linux/l3/uda1341.h --- orig/include/linux/l3/uda1341.h Thu Jan 1 01:00:00 1970 +++ linux/include/linux/l3/uda1341.h Mon Aug 13 21:23:02 2001 @@ -0,0 +1,50 @@ +/* + * linux/include/linux/l3/uda1341.h + * + * Philips UDA1341 mixer device driver + * + * Copyright (c) 2000 Nicolas Pitre + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + */ + +#define UDA1341_NAME "uda1341" + +struct uda1341_cfg { + unsigned int fs:16; + unsigned int format:3; +}; + +#define FMT_I2S 0 +#define FMT_LSB16 1 +#define FMT_LSB18 2 +#define FMT_LSB20 3 +#define FMT_MSB 4 +#define FMT_LSB16MSB 5 +#define FMT_LSB18MSB 6 +#define FMT_LSB20MSB 7 + +#define L3_UDA1341_CONFIGURE 0x13410001 + +struct l3_gain { + unsigned int left:8; + unsigned int right:8; + unsigned int unused:8; + unsigned int channel:8; +}; + +#define L3_SET_VOLUME 0x13410002 +#define L3_SET_TREBLE 0x13410003 +#define L3_SET_BASS 0x13410004 +#define L3_SET_GAIN 0x13410005 + +struct l3_agc { + unsigned int level:8; + unsigned int enable:1; + unsigned int attack:7; + unsigned int decay:8; + unsigned int channel:8; +}; + +#define L3_INPUT_AGC 0x13410006 diff -urN orig/include/linux/mm.h linux/include/linux/mm.h --- orig/include/linux/mm.h Tue May 27 10:05:42 2003 +++ linux/include/linux/mm.h Tue May 27 10:14:23 2003 @@ -609,5 +609,13 @@ int write); extern int remap_page_range(struct vm_area_struct *vma, unsigned long from, unsigned long to, unsigned long size, pgprot_t prot); + +#ifndef __arm__ +#define memc_update_addr(x,y,z) +#define memc_update_mm(x) +#define memc_clear(x,y) +#endif + #endif /* __KERNEL__ */ #endif /* _LINUX_MM_H */ + diff -urN orig/include/linux/mtd/cfi.h linux/include/linux/mtd/cfi.h --- orig/include/linux/mtd/cfi.h Tue Feb 19 00:44:41 2002 +++ linux/include/linux/mtd/cfi.h Sat Feb 16 14:52:26 2002 @@ -1,7 +1,7 @@ /* Common Flash Interface structures * See http://support.intel.com/design/flash/technote/index.htm - * $Id: cfi.h,v 1.25 2001/09/04 07:06:21 dwmw2 Exp $ + * $Id: cfi.h,v 1.26 2001/11/27 14:55:12 cdavies Exp $ */ #ifndef __MTD_CFI_H__ @@ -206,6 +206,10 @@ __u16 BlkStatusRegMask; __u8 VccOptimal; __u8 VppOptimal; + __u8 NumProtectionFields; + __u16 ProtRegAddr; + __u8 FactProtRegSize; + __u8 UserProtRegSize; } __attribute__((packed)); struct cfi_pri_query { diff -urN orig/include/linux/mtd/compatmac.h linux/include/linux/mtd/compatmac.h --- orig/include/linux/mtd/compatmac.h Sun Apr 20 16:32:31 2003 +++ linux/include/linux/mtd/compatmac.h Sun Apr 20 17:26:50 2003 @@ -2,7 +2,7 @@ /* * mtd/include/compatmac.h * - * $Id: compatmac.h,v 1.4 2000/07/03 10:01:38 dwmw2 Exp $ + * $Id: compatmac.h,v 1.26 2001/11/20 11:05:42 dwmw2 Exp $ * * Extensions and omissions from the normal 'linux/compatmac.h' * files. hopefully this will end up empty as the 'real' one @@ -17,20 +17,156 @@ #ifndef __LINUX_MTD_COMPATMAC_H__ #define __LINUX_MTD_COMPATMAC_H__ -#include /* used later in this header */ +#include #include #ifndef LINUX_VERSION_CODE #include #endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) -#include +#ifndef VERSION_CODE +# define VERSION_CODE(vers,rel,seq) ( ((vers)<<16) | ((rel)<<8) | (seq) ) +#endif +#ifndef KERNEL_VERSION +# define KERNEL_VERSION(a,b,c) VERSION_CODE(a,b,c) #endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,0,0) # error "This kernel is too old: not supported by this file" #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) +#include /* used later in this header */ + +#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c)) +#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c)) + +typedef struct wait_queue * wait_queue_head_t; + +#define DECLARE_WAITQUEUE(x,y) struct wait_queue x = {y,NULL} +#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL +#define init_waitqueue_head init_waitqueue +#define DECLARE_MUTEX(x) struct semaphore x = MUTEX +#define DECLARE_MUTEX_LOCKED(x) struct semaphore x = MUTEX_LOCKED + +/* from sysdep-2.1.h */ +# include +# define access_ok(t,a,sz) (verify_area((t),(a),(sz)) ? 0 : 1) +# define verify_area_20 verify_area +# define copy_to_user(t,f,n) (memcpy_tofs(t,f,n), 0) +# define __copy_to_user(t,f,n) copy_to_user((t),(f),(n)) +# define copy_to_user_ret(t,f,n,r) copy_to_user((t),(f),(n)) +# define copy_from_user(t,f,n) (memcpy_fromfs((t),(f),(n)), 0) +# define __copy_from_user(t,f,n) copy_from_user((t),(f),(n)) +# define copy_from_user_ret(t,f,n,r) copy_from_user((t),(f),(n)) +//xxx # define PUT_USER(val,add) (put_user((val),(add)), 0) +# define Put_user(val,add) (put_user((val),(add)), 0) +# define __PUT_USER(val,add) PUT_USER((val),(add)) +# define PUT_USER_RET(val,add,ret) PUT_USER((val),(add)) +# define GET_USER(dest,add) ((dest)=get_user((add)), 0) +# define __GET_USER(dest,add) GET_USER((dest),(add)) +# define GET_USER_RET(dest,add,ret) GET_USER((dest),(add)) + +#define ioremap(offset,size) vremap(offset,size) +#define iounmap(adr) /* */ + +#define EXPORT_SYMBOL(s) /* */ +#define EXPORT_SYMBOL_NOVERS(s) /* */ + +/* 2.1.10 and 2.1.43 introduced new functions. They are worth using */ + +#if LINUX_VERSION_CODE < VERSION_CODE(2,1,10) + +# include +# ifdef __LITTLE_ENDIAN +# define cpu_to_le16(x) (x) +# define cpu_to_le32(x) (x) +# define cpu_to_be16(x) htons((x)) +# define cpu_to_be32(x) htonl((x)) +# else +# define cpu_to_be16(x) (x) +# define cpu_to_be32(x) (x) + extern inline __u16 cpu_to_le16(__u16 x) { return (x<<8) | (x>>8);} + extern inline __u32 cpu_to_le32(__u32 x) { return((x>>24) | + ((x>>8)&0xff00) | ((x<<8)&0xff0000) | (x<<24));} +# endif + +# define le16_to_cpu(x) cpu_to_le16(x) +# define le32_to_cpu(x) cpu_to_le32(x) +# define be16_to_cpu(x) cpu_to_be16(x) +# define be32_to_cpu(x) cpu_to_be32(x) + +#endif + +#if LINUX_VERSION_CODE < VERSION_CODE(2,1,43) +# define cpu_to_le16p(addr) (cpu_to_le16(*(addr))) +# define cpu_to_le32p(addr) (cpu_to_le32(*(addr))) +# define cpu_to_be16p(addr) (cpu_to_be16(*(addr))) +# define cpu_to_be32p(addr) (cpu_to_be32(*(addr))) + + extern inline void cpu_to_le16s(__u16 *a) {*a = cpu_to_le16(*a);} + extern inline void cpu_to_le32s(__u16 *a) {*a = cpu_to_le32(*a);} + extern inline void cpu_to_be16s(__u16 *a) {*a = cpu_to_be16(*a);} + extern inline void cpu_to_be32s(__u16 *a) {*a = cpu_to_be32(*a);} + +# define le16_to_cpup(x) cpu_to_le16p(x) +# define le32_to_cpup(x) cpu_to_le32p(x) +# define be16_to_cpup(x) cpu_to_be16p(x) +# define be32_to_cpup(x) cpu_to_be32p(x) + +# define le16_to_cpus(x) cpu_to_le16s(x) +# define le32_to_cpus(x) cpu_to_le32s(x) +# define be16_to_cpus(x) cpu_to_be16s(x) +# define be32_to_cpus(x) cpu_to_be32s(x) +#endif + +// from 2.2, linux/types.h +#ifndef __BIT_TYPES_DEFINED__ +#define __BIT_TYPES_DEFINED__ + +typedef __u8 u_int8_t; +typedef __s8 int8_t; +typedef __u16 u_int16_t; +typedef __s16 int16_t; +typedef __u32 u_int32_t; +typedef __s32 int32_t; + +#endif /* !(__BIT_TYPES_DEFINED__) */ + +#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8) + typedef struct { } spinlock_t; + #define SPIN_LOCK_UNLOCKED (spinlock_t) { } +#else + typedef struct { int gcc_is_buggy; } spinlock_t; + #define SPIN_LOCK_UNLOCKED (spinlock_t) { 0 } +#endif + +#define spin_lock_init(lock) do { } while(0) +#define spin_lock(lock) (void)(lock) /* Not "unused variable". */ +#define spin_trylock(lock) (1) +#define spin_unlock_wait(lock) do { } while(0) +#define spin_unlock(lock) do { } while(0) +#define spin_lock_irq(lock) cli() +#define spin_unlock_irq(lock) sti() + +#define spin_lock_irqsave(lock, flags) \ + do { save_flags(flags); cli(); } while (0) +#define spin_unlock_irqrestore(lock, flags) \ + restore_flags(flags) + +// Doesn't work when tqueue.h is included. +// #define queue_task queue_task_irq_off +#define tty_flip_buffer_push(tty) queue_task_irq_off(&tty->flip.tqueue, &tq_timer) +#define signal_pending(current) (current->signal & ~current->blocked) +#define schedule_timeout(to) do {current->timeout = jiffies + (to);schedule ();} while (0) +#define time_after(t1,t2) (((long)t1-t2) > 0) + +#endif // LINUX_VERSION_CODE < 0x020100 + + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) +#include +#endif + /* Modularization issues */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,18) # define __USE_OLD_SYMTAB__ @@ -96,32 +232,60 @@ #include #endif -#if LINUX_VERSION_CODE < 0x20300 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) #define init_MUTEX(x) do {*(x) = MUTEX;} while (0) +#define init_MUTEX_LOCKED(x) do {*(x) = MUTEX_LOCKED;} while (0) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) #define RQFUNC_ARG void #define blkdev_dequeue_request(req) do {CURRENT = req->next;} while (0) #else #define RQFUNC_ARG request_queue_t *q #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,32) +#define blk_cleanup_queue(nr) do {blk_dev[nr].request_fn = 0;} while(0) +#define BLK_DEFAULT_QUEUE(nr) (blk_dev[nr].request_fn) +#define blk_init_queue(q, rq) do {q = rq;} while(0) +#endif + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0) +#ifdef CONFIG_MODULES #define __MOD_INC_USE_COUNT(mod) \ (atomic_inc(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED|MOD_USED_ONCE) #define __MOD_DEC_USE_COUNT(mod) \ (atomic_dec(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED) +#else +#define __MOD_INC_USE_COUNT(mod) +#define __MOD_DEC_USE_COUNT(mod) +#endif #endif +#ifndef HAVE_INTER_MODULE +static inline void *inter_module_get(char *x) {return NULL;} +static inline void *inter_module_get_request(char *x, char *y) {return NULL;} +static inline void inter_module_put(const char *x) {} +static inline void inter_module_register(const char *x, struct module *y, const void *z) {} +static inline void inter_module_unregister(const char *x) {} +#endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) #define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL #define init_waitqueue_head init_waitqueue +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) + static inline int try_inc_mod_count(struct module *mod) { +#ifdef CONFIG_MODULES if (mod) __MOD_INC_USE_COUNT(mod); +#endif return 1; } #endif @@ -196,6 +360,123 @@ #include #endif -#endif /* __LINUX_MTD_COMPATMAC_H__ */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) +#define set_current_state(state_value) \ + do { current->state = (state_value); } while (0) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0) +static inline int invalidate_device(kdev_t dev, int do_sync) { + + if (do_sync) + fsync_dev(dev); + + invalidate_buffers(dev); + return 0; +} +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,4,5) +static inline int invalidate_device(kdev_t dev, int do_sync) { + struct super_block *sb = get_super(dev); + int res = 0; + + if (do_sync) + fsync_dev(dev); + + if (sb) + res = invalidate_inodes(sb); + + invalidate_buffers(dev); + return res; +} +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) +#undef min +#undef max +#undef min_t +#undef max_t +/* + * min()/max() macros that also do + * strict type-checking.. See the + * "unnecessary" pointer comparison. + */ +#define min(x,y) ({ \ + const typeof(x) _x = (x); \ + const typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x < _y ? _x : _y; }) + +#define max(x,y) ({ \ + const typeof(x) _x = (x); \ + const typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x > _y ? _x : _y; }) + +/* + * ..and if you can't take the strict + * types, you can specify one yourself. + * + * Or not use min/max at all, of course. + */ +#define min_t(type,x,y) \ + ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) +#define max_t(type,x,y) \ + ({ type __x = (x); type __y = (y); __x > __y ? __x: __y; }) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,7) +struct completion { + struct semaphore s; +}; + +#define complete(c) up(&(c)->s) +#define wait_for_completion(c) down(&(c)->s) +#define init_completion(c) init_MUTEX_LOCKED(&(c)->s); + +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,9) +/* This came later */ +#define complete_and_exit(c, r) do { complete(c); do_exit(r); } while(0) +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) +#include + +static inline void add_gendisk(struct gendisk *gp) +{ + gp->next = gendisk_head; + gendisk_head = gp; +} + +static inline void del_gendisk(struct gendisk *gp) +{ + struct gendisk *gd, **gdp; + + for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) + if (*gdp == gp) { + gd = *gdp; *gdp = gd->next; + break; + } +} +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) && defined(MODULE) + +#define module_init(func) \ +mod_init_t init_module(void) { \ + return func(); \ +} + +#define module_exit(func) \ +mod_exit_t cleanup_module(void) { \ + return func(); \ +} +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) +#define MODULE_LICENSE(x) /* */ +#endif + +#endif /* __LINUX_MTD_COMPATMAC_H__ */ diff -urN orig/include/linux/mtd/mtd.h linux/include/linux/mtd/mtd.h --- orig/include/linux/mtd/mtd.h Tue May 27 10:05:42 2003 +++ linux/include/linux/mtd/mtd.h Tue May 27 10:14:24 2003 @@ -1,5 +1,5 @@ -/* $Id: mtd.h,v 1.33 2001/06/09 00:08:59 dwmw2 Exp $ */ +/* $Id: mtd.h,v 1.34 2001/11/27 14:55:12 cdavies Exp $ */ #ifndef __MTD_MTD_H__ #define __MTD_MTD_H__ @@ -180,6 +180,18 @@ int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); + /* + * Methods to access the protection register area, present in some + * flash devices. The user data is one time programmable but the + * factory data is read only. + */ + int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); + + int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); + + /* This function is not yet implemented */ + int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); + /* iovec-based read/write methods. We need these especially for NAND flash, with its limited number of write cycles per erase. NB: The 'count' parameter is the number of _vectors_, each of diff -urN orig/include/linux/mtd/partitions.h linux/include/linux/mtd/partitions.h --- orig/include/linux/mtd/partitions.h Wed Jul 4 19:54:10 2001 +++ linux/include/linux/mtd/partitions.h Wed Jul 31 11:18:06 2002 @@ -5,7 +5,7 @@ * * This code is GPL * - * $Id: partitions.h,v 1.6 2001/03/17 17:10:21 dwmw2 Exp $ + * $Id: partitions.h,v 1.8 2002/03/08 16:34:36 rkaiser Exp $ */ #ifndef MTD_PARTITIONS_H @@ -26,14 +26,14 @@ * will extend to the end of the master MTD device. * offset: absolute starting position within the master MTD device; if * defined as MTDPART_OFS_APPEND, the partition will start where the - * previous one ended. + * previous one ended; if MTDPART_OFS_NXTBLK, at the next erase block. * mask_flags: contains flags that have to be masked (removed) from the * master MTD flag set for the corresponding MTD partition. * For example, to force a read-only partition, simply adding * MTD_WRITEABLE to the mask_flags will do the trick. * * Note: writeable partitions require their size and offset be - * erasesize aligned. + * erasesize aligned (e.g. use MTDPART_OFS_NEXTBLK). */ struct mtd_partition { @@ -41,8 +41,10 @@ u_int32_t size; /* partition size */ u_int32_t offset; /* offset within the master MTD space */ u_int32_t mask_flags; /* master MTD flags to mask out for this partition */ + struct mtd_info **mtdp; /* pointer to store the MTD object */ }; +#define MTDPART_OFS_NXTBLK (-2) #define MTDPART_OFS_APPEND (-1) #define MTDPART_SIZ_FULL (0) diff -urN orig/include/linux/pci.h linux/include/linux/pci.h --- orig/include/linux/pci.h Tue May 27 10:05:43 2003 +++ linux/include/linux/pci.h Tue May 27 10:22:10 2003 @@ -458,6 +458,8 @@ char name[48]; + unsigned short bridge_ctl; /* manage NO_ISA/FBB/et al behaviors */ + unsigned short pad2; struct device * dev; }; @@ -561,7 +563,6 @@ char *pci_class_name(u32 class); void pci_read_bridge_bases(struct pci_bus *child); struct resource *pci_find_parent_resource(const struct pci_dev *dev, struct resource *res); -int pci_setup_device(struct pci_dev *dev); int pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge); extern struct pci_dev *pci_get_dev(struct pci_dev *dev); extern void pci_put_dev(struct pci_dev *dev); @@ -789,26 +790,7 @@ { int rc = pci_register_driver (drv); - if (rc > 0) - return 0; - - /* iff CONFIG_HOTPLUG and built into kernel, we should - * leave the driver around for future hotplug events. - * For the module case, a hotplug daemon of some sort - * should load a module in response to an insert event. */ -#if defined(CONFIG_HOTPLUG) && !defined(MODULE) - if (rc == 0) - return 0; -#else - if (rc == 0) - rc = -ENODEV; -#endif - - /* if we get here, we need to clean up pci driver instance - * and return some sort of error */ - pci_unregister_driver (drv); - - return rc; + return rc < 0 ? : 0; } #endif /* !CONFIG_PCI */ diff -urN orig/include/linux/pld/pld_epxa.h linux/include/linux/pld/pld_epxa.h --- orig/include/linux/pld/pld_epxa.h Thu Jan 1 01:00:00 1970 +++ linux/include/linux/pld/pld_epxa.h Wed Feb 13 17:10:54 2002 @@ -0,0 +1,35 @@ +#ifndef __LINUX_EPXAPLD_H +#define __LINUX_EPXAPLD_H + +/* + * linux/drivers/char/pld/epxapld.h + * + * Pld driver for Altera EPXA Excalibur devices + * + * Copyright 2001 Altera Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: $ + * + */ +#define PLD_IOC_MAGIC 'p' +#if !defined(KERNEL) || defined(CONFIG_PLD_HOTSWAP) +#define PLD_IOC_ADD_PLD_DEV _IOW(PLD_IOC_MAGIC, 0xa0, struct pldhs_dev_desc) +#define PLD_IOC_REMOVE_PLD_DEVS _IO(PLD_IOC_MAGIC, 0xa1) +#define PLD_IOC_SET_INT_MODE _IOW(PLD_IOC_MAGIC, 0xa2, int) +#endif + +#endif diff -urN orig/include/linux/pld/pld_hotswap.h linux/include/linux/pld/pld_hotswap.h --- orig/include/linux/pld/pld_hotswap.h Thu Jan 1 01:00:00 1970 +++ linux/include/linux/pld/pld_hotswap.h Tue Feb 19 15:53:46 2002 @@ -0,0 +1,83 @@ +#ifndef __LINUX_PLD_HOTSWAP_H +#define __LINUX_PLD_HOTSWAP_H +/* + * linux/drivers/char/pld/pld_hotswap.h + * + * Pld driver for Altera EPXA Excalibur devices + * + * Copyright 2001 Altera Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id: $ + * + */ + +/* + * The pld_hotswap ops contain the basic operation required for adding + * and removing devices from the system. + */ + +struct pldhs_dev_info{ + unsigned int size; + unsigned short vendor_id; + unsigned short product_id; + unsigned char fsize; + unsigned char nsize; + unsigned char pssize; + unsigned char irq; + unsigned char cmdsize; + unsigned char res1; + unsigned char res2; + unsigned char res3; + unsigned int base_addr; + unsigned int reg_size; +}; + +struct pldhs_dev_desc{ + struct pldhs_dev_info* info; + char* name; + void* data; +}; + +#include + + +#ifdef __KERNEL__ +struct pld_hotswap_ops{ + struct list_head list; + char* name; + int (*add_device)(struct pldhs_dev_info *dev_info, void* dev_ps_data); + int (*remove_devices)(void); +}; + +/* + * These functions are called by the device drivers to register functions + * which should be called when devices are added and removed + */ +extern int pldhs_register_driver(struct pld_hotswap_ops *drv); +extern int pldhs_unregister_driver(char *driver); + +/* + * These functions are called by the pld loader to add and remove + * device instances from the system. The call the functions regsistered + * the the particular deriver for this purpose + */ +extern int pldhs_add_device(struct pldhs_dev_info* dev_info,char *drv_name, void* dev_ps_data); +extern int pldhs_remove_devices(void); + +#endif + +#endif diff -urN orig/include/linux/ptrace.h linux/include/linux/ptrace.h --- orig/include/linux/ptrace.h Tue May 27 10:05:43 2003 +++ linux/include/linux/ptrace.h Tue May 27 10:14:30 2003 @@ -65,6 +65,7 @@ #define PT_TRACE_EXIT 0x00000200 #define PT_TRACE_MASK 0x000003f4 +#define PT_SINGLESTEP 0x80000000 /* single stepping (used on ARM) */ #include /* For unlikely. */ #include /* For struct task_struct. */ diff -urN orig/include/linux/serial.h linux/include/linux/serial.h --- orig/include/linux/serial.h Wed Mar 5 19:51:58 2003 +++ linux/include/linux/serial.h Wed Mar 5 09:58:27 2003 @@ -82,17 +82,6 @@ #define SERIAL_IO_HUB6 1 #define SERIAL_IO_MEM 2 -struct serial_uart_config { - char *name; - int dfl_xmit_fifo_size; - int flags; -}; - -#define UART_CLEAR_FIFO 0x01 -#define UART_USE_FIFO 0x02 -#define UART_STARTECH 0x04 -#define UART_NATSEMI 0x08 - /* * Definitions for async_struct (and serial_struct) flags field */ diff -urN orig/include/linux/serial_core.h linux/include/linux/serial_core.h --- orig/include/linux/serial_core.h Mon May 5 17:40:12 2003 +++ linux/include/linux/serial_core.h Mon May 5 17:45:04 2003 @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - * $Id: serial_core.h,v 1.49 2002/07/20 18:06:32 rmk Exp $ + * $Id: serial_core.h,v 1.53 2002/08/02 12:55:08 rmk Exp $ */ /* @@ -67,6 +67,9 @@ #define PORT_PC9861 45 #define PORT_PC9801_101 46 +/* DZ */ +#define PORT_DZ 47 + #ifdef __KERNEL__ @@ -143,8 +146,6 @@ spinlock_t lock; /* port lock */ unsigned int iobase; /* in/out[bwl] */ char *membase; /* read/write[bwl] */ - unsigned int irq; /* irq number */ - unsigned int uartclk; /* base uart clock */ unsigned char fifosize; /* tx fifo size */ unsigned char x_char; /* xon/xoff char */ unsigned char regshift; /* reg offset shift */ @@ -156,6 +157,7 @@ unsigned int read_status_mask; /* driver specific */ unsigned int ignore_status_mask; /* driver specific */ + struct uart_info *info; /* pointer to parent info */ struct uart_icount icount; /* statistics */ @@ -196,7 +198,9 @@ unsigned int timeout; /* character-based timeout */ unsigned int type; /* port type */ struct uart_ops *ops; + unsigned int uartclk; /* base uart clock */ unsigned int custom_divisor; + unsigned int irq; /* irq number */ unsigned int line; /* port index */ unsigned long mapbase; /* for ioremap */ unsigned char hub6; /* this should be in the 8250 driver */ diff -urN orig/include/linux/serial_reg.h linux/include/linux/serial_reg.h --- orig/include/linux/serial_reg.h Fri May 25 20:06:30 2001 +++ linux/include/linux/serial_reg.h Fri Aug 23 15:55:30 2002 @@ -121,6 +121,7 @@ /* * These are the definitions for the Modem Control Register */ +#define UART_MCR_AFE 0x20 /* Enable auto-RTS/CTS (TI16C750) */ #define UART_MCR_LOOP 0x10 /* Enable loopback test mode */ #define UART_MCR_OUT2 0x08 /* Out2 complement */ #define UART_MCR_OUT1 0x04 /* Out1 complement */ diff -urN orig/include/linux/switches.h linux/include/linux/switches.h --- orig/include/linux/switches.h Thu Jan 1 01:00:00 1970 +++ linux/include/linux/switches.h Sun Mar 24 15:24:06 2002 @@ -0,0 +1,74 @@ +/* + * linux/include/linux/switches.h + * + * Copyright (C) 2000 John Dorsey + * + * 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. + * + * 23 October 2000 - created. + */ + +#if !defined(_LINUX_SWITCHES_H) +#define _LINUX_SWITCHES_H + +#define SWITCHES_MASK_SIZE (128) + +typedef unsigned long switches_bitfield; + +#define SWITCHES_BITS (sizeof(switches_bitfield) * 8) +#define SWITCHES_NUM_FIELDS (SWITCHES_MASK_SIZE / SWITCHES_BITS) +#define SWITCHES_FIELD_SELECT(i) ((i) / SWITCHES_BITS) +#define SWITCHES_FIELD_MASK(i) ((switches_bitfield)(1 << (i) % \ + SWITCHES_BITS)) + +typedef struct switches_mask_t { + unsigned int count; + switches_bitfield events[SWITCHES_NUM_FIELDS]; + switches_bitfield states[SWITCHES_NUM_FIELDS]; +} switches_mask_t; + +#define SWITCHES_ZERO(m) \ +do { \ + unsigned int sz_i; \ + (m)->count = 0; \ + for(sz_i = 0; sz_i < SWITCHES_NUM_FIELDS; ++sz_i) \ + (m)->events[sz_i] = (m)->states[sz_i] = 0; \ +} while (0) + +/* `s' is the state of the switch, either 0 or non-zero: */ +#define SWITCHES_SET(m, i, s) \ +do { \ + ((m)->events[SWITCHES_FIELD_SELECT((i))] |= \ + SWITCHES_FIELD_MASK((i))); \ + if(s) \ + ((m)->states[SWITCHES_FIELD_SELECT((i))] |= \ + SWITCHES_FIELD_MASK((i))); \ + else \ + ((m)->states[SWITCHES_FIELD_SELECT((i))] &= \ + ~SWITCHES_FIELD_MASK((i))); \ + ++((m)->count); \ +} while (0) + +/* Should only use to clear an event set by SWITCHES_SET(): */ +#define SWITCHES_CLEAR(m, i) \ +do { \ + ((m)->events[SWITCHES_FIELD_SELECT((i))] &= \ + ~SWITCHES_FIELD_MASK((i))); \ + ((m)->states[SWITCHES_FIELD_SELECT((i))] &= \ + ~SWITCHES_FIELD_MASK((i))); \ + --((m)->count); \ +} + +#define SWITCHES_COUNT(m) ((m)->count) + +/* Returns 0 or non-zero: */ +#define SWITCHES_EVENT(m, i) \ +((m)->events[SWITCHES_FIELD_SELECT((i))] & SWITCHES_FIELD_MASK((i))) + +/* Returns 0 or non-zero: */ +#define SWITCHES_STATE(m, i) \ +((m)->states[SWITCHES_FIELD_SELECT((i))] & SWITCHES_FIELD_MASK((i))) + +#endif /* !defined(_LINUX_SWITCHES_H) */ diff -urN orig/include/pcmcia/cs.h linux/include/pcmcia/cs.h --- orig/include/pcmcia/cs.h Sat Apr 12 14:47:13 2003 +++ linux/include/pcmcia/cs.h Wed Jun 11 21:46:06 2003 @@ -316,7 +316,7 @@ /* Special stuff for binding drivers to sockets */ typedef struct bind_req_t { - socket_t Socket; + struct pcmcia_socket *Socket; u_char Function; dev_info_t *dev_info; } bind_req_t; @@ -325,7 +325,7 @@ #define BIND_FN_ALL 0xff typedef struct mtd_bind_t { - socket_t Socket; + struct pcmcia_socket *Socket; u_int Attributes; u_int CardOffset; dev_info_t *dev_info; diff -urN orig/include/pcmcia/ds.h linux/include/pcmcia/ds.h --- orig/include/pcmcia/ds.h Mon May 5 17:40:13 2003 +++ linux/include/pcmcia/ds.h Sat May 17 22:53:23 2003 @@ -156,12 +156,6 @@ int pcmcia_register_driver(struct pcmcia_driver *driver); void pcmcia_unregister_driver(struct pcmcia_driver *driver); -/* legacy driver registration interface. don't use in new code */ -int register_pccard_driver(dev_info_t *dev_info, - dev_link_t *(*attach)(void), - void (*detach)(dev_link_t *)); -int unregister_pccard_driver(dev_info_t *dev_info); - /* error reporting */ void cs_error(client_handle_t handle, int func, int ret); diff -urN orig/include/pcmcia/ss.h linux/include/pcmcia/ss.h --- orig/include/pcmcia/ss.h Mon May 5 17:40:13 2003 +++ linux/include/pcmcia/ss.h Thu Jun 12 12:44:44 2003 @@ -31,6 +31,8 @@ #define _LINUX_SS_H #include +#include +#include #include /* Definitions for card status flags for GetStatus */ @@ -111,6 +113,7 @@ u_char map; u_char flags; u_short speed; + struct resource *res; u_long sys_start, sys_stop; u_int card_start; } pccard_mem_map; @@ -124,37 +127,145 @@ /* * Socket operations. */ +struct pcmcia_socket; + struct pccard_operations { struct module *owner; - int (*init)(unsigned int sock); - int (*suspend)(unsigned int sock); - int (*register_callback)(unsigned int sock, void (*handler)(void *, unsigned int), void * info); - int (*inquire_socket)(unsigned int sock, socket_cap_t *cap); - int (*get_status)(unsigned int sock, u_int *value); - int (*get_socket)(unsigned int sock, socket_state_t *state); - int (*set_socket)(unsigned int sock, socket_state_t *state); - int (*set_io_map)(unsigned int sock, struct pccard_io_map *io); - int (*set_mem_map)(unsigned int sock, struct pccard_mem_map *mem); - void (*proc_setup)(unsigned int sock, struct proc_dir_entry *base); + int (*init)(struct pcmcia_socket *sock); + int (*suspend)(struct pcmcia_socket *sock); + int (*register_callback)(struct pcmcia_socket *sock, void (*handler)(void *, unsigned int), void * info); + int (*inquire_socket)(struct pcmcia_socket *sock, socket_cap_t *cap); + int (*get_status)(struct pcmcia_socket *sock, u_int *value); + int (*get_socket)(struct pcmcia_socket *sock, socket_state_t *state); + int (*set_socket)(struct pcmcia_socket *sock, socket_state_t *state); + int (*set_io_map)(struct pcmcia_socket *sock, struct pccard_io_map *io); + int (*set_mem_map)(struct pcmcia_socket *sock, struct pccard_mem_map *mem); + void (*proc_setup)(struct pcmcia_socket *sock, struct proc_dir_entry *base); }; /* * Calls to set up low-level "Socket Services" drivers */ +struct pcmcia_socket; struct pcmcia_socket_class_data { unsigned int nsock; /* number of sockets */ unsigned int sock_offset; /* socket # (which is * returned to driver) = sock_offset + (0, 1, .. , (nsock-1) */ struct pccard_operations *ops; /* see above */ - void *s_info; /* socket_info_t */ + struct pcmcia_socket *s_info; struct class_device class_dev; /* generic class structure */ }; +typedef struct erase_busy_t { + eraseq_entry_t *erase; + client_handle_t client; + struct timer_list timeout; + struct erase_busy_t *prev, *next; +} erase_busy_t; + +struct io_resource { + struct list_head node; + unsigned long mask; + struct resource *res; +}; + +typedef struct io_window_t { + u_int Attributes; + ioaddr_t BasePort, NumPorts; + ioaddr_t InUse, Config; + struct list_head reslist; +} io_window_t; + +#define WINDOW_MAGIC 0xB35C +typedef struct window_t { + u_short magic; + u_short index; + client_handle_t handle; + struct pcmcia_socket *sock; + pccard_mem_map ctl; +} window_t; + +/* Maximum number of IO windows per socket */ +#define MAX_IO_WIN 2 + +/* Maximum number of memory windows per socket */ +#define MAX_WIN 4 + +struct config_t; +struct region_t; +struct map_operations; + +struct pcmcia_socket { + spinlock_t lock; + struct pccard_operations * ss_entry; + struct map_operations *map_ops; + socket_state_t socket; + socket_cap_t cap; + u_int state; + u_short functions; + u_short lock_count; + client_handle_t clients; + u_int real_clients; + pccard_mem_map cis_mem; + u_char *cis_virt; + struct config_t *config; + struct { + u_int AssignedIRQ; + u_int Config; + } irq; + io_window_t io[MAX_IO_WIN]; + window_t win[MAX_WIN]; + struct region_t *c_region, *a_region; + erase_busy_t erase_busy; + struct list_head cis_cache; + u_int fake_cis_len; + char *fake_cis; + + struct list_head socket_list; + + /* deprecated */ + unsigned int sock; /* socket number */ + +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *proc; +#endif + + /* state thread */ + struct semaphore skt_sem; /* protects socket h/w state */ + + struct task_struct *thread; + struct completion thread_done; + wait_queue_head_t thread_wait; + spinlock_t thread_lock; /* protects thread_events */ + unsigned int thread_events; + + /* pcmcia (16-bit) */ + struct pcmcia_bus_socket *pcmcia; + + /* cardbus (32-bit) */ +#ifdef CONFIG_CARDBUS + struct resource * cb_cis_res; + u_char *cb_cis_virt; +#endif + + /* socket device */ + struct class_device dev; + void *driver_data; /* data internal to the socket driver */ + +}; + +struct pcmcia_socket * pcmcia_get_socket_by_nr(unsigned int nr); + + + +extern int pcmcia_register_socket(struct pcmcia_socket *socket); +extern void pcmcia_unregister_socket(struct pcmcia_socket *socket); + extern struct class pcmcia_socket_class; /* socket drivers are expected to use these callbacks in their .drv struct */ -extern int pcmcia_socket_dev_suspend(struct pcmcia_socket_class_data *cls_d, u32 state, u32 level); -extern int pcmcia_socket_dev_resume(struct pcmcia_socket_class_data *cls_d, u32 level); +extern int pcmcia_socket_dev_suspend(struct device *dev, u32 state, u32 level); +extern int pcmcia_socket_dev_resume(struct device *dev, u32 level); #endif /* _LINUX_SS_H */ diff -urN orig/init/do_mounts.c linux/init/do_mounts.c --- orig/init/do_mounts.c Tue May 27 10:05:45 2003 +++ linux/init/do_mounts.c Tue May 27 10:14:38 2003 @@ -363,7 +363,7 @@ root_device_name += 5; } - is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR; + is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR || MAJOR(ROOT_DEV) == 31; /* This has to be before mounting root, because even readonly mount of reiserfs would replay log corrupting stuff */ diff -urN orig/kernel/fork.c linux/kernel/fork.c --- orig/kernel/fork.c Tue May 27 10:05:45 2003 +++ linux/kernel/fork.c Tue May 27 10:14:39 2003 @@ -199,7 +199,11 @@ * value: the thread structures can take up at most half * of memory. */ +#if THREAD_SIZE > PAGE_SIZE max_threads = mempages / (THREAD_SIZE/PAGE_SIZE) / 8; +#else + max_threads = (mempages * PAGE_SIZE) / (8 * THREAD_SIZE); +#endif /* * we need to allow at least 20 threads to boot a system */ @@ -327,6 +331,7 @@ out: flush_tlb_mm(current->mm); + memc_update_mm(mm); up_write(&oldmm->mmap_sem); return retval; fail_nomem: diff -urN orig/kernel/printk.c linux/kernel/printk.c --- orig/kernel/printk.c Mon May 5 17:40:15 2003 +++ linux/kernel/printk.c Mon May 5 17:45:11 2003 @@ -678,3 +678,25 @@ tty->driver->write(tty, 0, msg, strlen(msg)); return; } + +#include + +static void +show_msg_info(int key, struct pt_regs *regs, struct tty_struct *tty) +{ + call_console_drivers(log_end - logged_chars, log_end); +} + +static struct sysrq_key_op msg_info_op = { + .handler = show_msg_info, + .help_msg = "Dumpmsgs", + .action_msg = "Kernel Messages", +}; + +static int __init dbg_init(void) +{ + register_sysrq_key('d', &msg_info_op); + return 0; +} + +__initcall(dbg_init); diff -urN orig/kernel/timer.c linux/kernel/timer.c --- orig/kernel/timer.c Tue May 27 10:05:48 2003 +++ linux/kernel/timer.c Tue May 27 10:14:41 2003 @@ -438,8 +438,8 @@ /* * Timekeeping variables */ -unsigned long tick_usec = TICK_USEC; /* ACTHZ period (usec) */ -unsigned long tick_nsec = TICK_NSEC(TICK_USEC); /* USER_HZ period (nsec) */ +unsigned long tick_usec; /* ACTHZ period (usec) */ +unsigned long tick_nsec; /* USER_HZ period (nsec) */ /* * The current time @@ -1216,6 +1216,9 @@ void __init init_timers(void) { + tick_usec = TICK_USEC; + tick_nsec = TICK_NSEC(TICK_USEC); + timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE, (void *)(long)smp_processor_id()); register_cpu_notifier(&timers_nb); diff -urN orig/lib/inflate.c linux/lib/inflate.c --- orig/lib/inflate.c Tue May 27 10:05:48 2003 +++ linux/lib/inflate.c Tue May 27 10:20:17 2003 @@ -1013,8 +1013,8 @@ * **********************************************************************/ -static ulg crc_32_tab[256]; -static ulg crc; /* initialized in makecrc() so it'll reside in bss */ +STATIC ulg crc_32_tab[256]; +STATIC ulg crc; /* initialized in makecrc() so it'll reside in bss */ #define CRC_VALUE (crc ^ 0xffffffffUL) /* diff -urN orig/mm/swapfile.c linux/mm/swapfile.c --- orig/mm/swapfile.c Tue May 27 10:05:48 2003 +++ linux/mm/swapfile.c Tue May 27 10:14:45 2003 @@ -23,6 +23,7 @@ #include #include +#include #include spinlock_t swaplock = SPIN_LOCK_UNLOCKED; diff -urN orig/mm/vmalloc.c linux/mm/vmalloc.c --- orig/mm/vmalloc.c Tue May 27 10:05:48 2003 +++ linux/mm/vmalloc.c Tue May 27 10:14:45 2003 @@ -178,21 +178,11 @@ return err; } - -/** - * get_vm_area - reserve a contingous kernel virtual area - * - * @size: size of the area - * @flags: %VM_IOREMAP for I/O mappings or VM_ALLOC - * - * Search an area of @size in the kernel virtual mapping area, - * and reserved it for out purposes. Returns the area descriptor - * on success or %NULL on failure. - */ -struct vm_struct *get_vm_area(unsigned long size, unsigned long flags) +struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags, + unsigned long start, unsigned long end) { struct vm_struct **p, *tmp, *area; - unsigned long addr = VMALLOC_START; + unsigned long addr = start; area = kmalloc(sizeof(*area), GFP_KERNEL); if (unlikely(!area)) @@ -209,12 +199,14 @@ write_lock(&vmlist_lock); for (p = &vmlist; (tmp = *p) ;p = &tmp->next) { + if ((unsigned long)tmp->addr < addr) + continue; if ((size + addr) < addr) goto out; if (size + addr <= (unsigned long)tmp->addr) goto found; addr = tmp->size + (unsigned long)tmp->addr; - if (addr > VMALLOC_END-size) + if (addr > end - size) goto out; } @@ -239,6 +231,21 @@ } /** + * get_vm_area - reserve a contingous kernel virtual area + * + * @size: size of the area + * @flags: %VM_IOREMAP for I/O mappings or VM_ALLOC + * + * Search an area of @size in the kernel virtual mapping area, + * and reserved it for out purposes. Returns the area descriptor + * on success or %NULL on failure. + */ +struct vm_struct *get_vm_area(unsigned long size, unsigned long flags) +{ + return __get_vm_area(size, flags, VMALLOC_START, VMALLOC_END); +} + +/** * remove_vm_area - find and remove a contingous kernel virtual area * * @addr: base address diff -urN orig/net/irda/af_irda.c linux/net/irda/af_irda.c --- orig/net/irda/af_irda.c Tue May 27 10:05:55 2003 +++ linux/net/irda/af_irda.c Tue May 27 10:15:03 2003 @@ -59,6 +59,7 @@ #include #include +#include /*rmk*/ static int irda_create(struct socket *sock, int protocol); @@ -2513,6 +2514,10 @@ */ int __init irsock_init(void) { + BUG_ON(sizeof(struct test_frame) != 10); + BUG_ON(sizeof(struct snrm_frame) != 11); + BUG_ON(sizeof(struct ua_frame) != 10); + sock_register(&irda_family_ops); return 0; @@ -2527,6 +2532,4 @@ void __exit irsock_cleanup(void) { sock_unregister(PF_IRDA); - - return; } diff -urN orig/net/irda/iriap.c linux/net/irda/iriap.c --- orig/net/irda/iriap.c Tue May 27 10:05:58 2003 +++ linux/net/irda/iriap.c Tue May 27 10:15:04 2003 @@ -1013,18 +1013,20 @@ switch (attrib->value->type) { case IAS_INTEGER: - len += sprintf(buf+len, "%d\n", + len += sprintf(buf+len, "%d", attrib->value->t.integer); break; case IAS_STRING: - len += sprintf(buf+len, "\"%s\"\n", + len += sprintf(buf+len, "\"%s\"", attrib->value->t.string); break; case IAS_OCT_SEQ: - len += sprintf(buf+len, "octet sequence (%d bytes)\n", attrib->value->len); + len += sprintf(buf+len, + "octet sequence (%d bytes)", + attrib->value->len); break; case IAS_MISSING: - len += sprintf(buf+len, "missing\n"); + len += sprintf(buf+len, "missing"); break; default: IRDA_DEBUG(0, "%s(), Unknown value type!\n", @@ -1039,6 +1041,7 @@ spin_unlock(&obj->attribs->hb_spinlock); obj = (struct ias_object *) hashbin_get_next(irias_objects); + len += sprintf(buf+len, "\n"); } spin_unlock_irqrestore(&irias_objects->hb_spinlock, flags); diff -urN orig/net/irda/irlan/irlan_common.c linux/net/irda/irlan/irlan_common.c --- orig/net/irda/irlan/irlan_common.c Tue May 27 10:05:58 2003 +++ linux/net/irda/irlan/irlan_common.c Tue May 27 10:15:04 2003 @@ -318,6 +318,9 @@ del_timer(&self->watchdog_timer); + /* since irlan_do_*_event gobble the skb, we must get it once -- rmk */ + skb_get(skb); + /* If you want to pass the skb to *both* state machines, you will * need to skb_clone() it, so that you don't free it twice. * As the state machines don't need it, git rid of it here... diff -urN orig/net/irda/irlap_frame.c linux/net/irda/irlap_frame.c --- orig/net/irda/irlap_frame.c Tue May 27 10:05:58 2003 +++ linux/net/irda/irlap_frame.c Tue May 27 11:05:29 2003 @@ -34,6 +34,7 @@ #include #include +#include #include #include @@ -134,8 +135,8 @@ */ if (qos) { skb_put(tx_skb, 9); /* 21 left */ - frame->saddr = cpu_to_le32(self->saddr); - frame->daddr = cpu_to_le32(self->daddr); + put_unaligned(cpu_to_le32(self->saddr), &frame->saddr); + put_unaligned(cpu_to_le32(self->daddr), &frame->daddr); frame->ncaddr = self->caddr; @@ -173,8 +174,8 @@ } /* Copy peer device address */ - info->daddr = le32_to_cpu(frame->saddr); - info->saddr = le32_to_cpu(frame->daddr); + info->daddr = le32_to_cpu(get_unaligned(&frame->saddr)); + info->saddr = le32_to_cpu(get_unaligned(&frame->daddr)); /* Only accept if addressed directly to us */ if (info->saddr != self->saddr) { @@ -217,8 +218,8 @@ frame->caddr = self->caddr; frame->control = UA_RSP | PF_BIT; - frame->saddr = cpu_to_le32(self->saddr); - frame->daddr = cpu_to_le32(self->daddr); + put_unaligned(cpu_to_le32(self->saddr), &frame->saddr); + put_unaligned(cpu_to_le32(self->daddr), &frame->daddr); /* Should we send QoS negotiation parameters? */ if (qos) { @@ -302,7 +303,7 @@ { struct sk_buff *tx_skb = NULL; struct xid_frame *frame; - __u32 bcast = BROADCAST; + __u32 bcast = BROADCAST, daddr; __u8 *info; IRDA_DEBUG(4, "%s(), s=%d, S=%d, command=%d\n", __FUNCTION__, @@ -328,12 +329,13 @@ } frame->ident = XID_FORMAT; - frame->saddr = cpu_to_le32(self->saddr); - if (command) - frame->daddr = cpu_to_le32(bcast); + daddr = bcast; else - frame->daddr = cpu_to_le32(discovery->data.daddr); + daddr = discovery->data.daddr; + + put_unaligned(cpu_to_le32(self->saddr), &frame->saddr); + put_unaligned(cpu_to_le32(daddr), &frame->daddr); switch (S) { case 1: @@ -404,8 +406,8 @@ xid = (struct xid_frame *) skb->data; - info->daddr = le32_to_cpu(xid->saddr); - info->saddr = le32_to_cpu(xid->daddr); + info->daddr = le32_to_cpu(get_unaligned(&xid->saddr)); + info->saddr = le32_to_cpu(get_unaligned(&xid->daddr)); /* Make sure frame is addressed to us */ if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) { @@ -471,8 +473,8 @@ xid = (struct xid_frame *) skb->data; - info->daddr = le32_to_cpu(xid->saddr); - info->saddr = le32_to_cpu(xid->daddr); + info->daddr = le32_to_cpu(get_unaligned(&xid->saddr)); + info->saddr = le32_to_cpu(get_unaligned(&xid->daddr)); /* Make sure frame is addressed to us */ if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) { @@ -1245,8 +1247,8 @@ } /* Read and swap addresses */ - info->daddr = le32_to_cpu(frame->saddr); - info->saddr = le32_to_cpu(frame->daddr); + info->daddr = le32_to_cpu(get_unaligned(&frame->saddr)); + info->saddr = le32_to_cpu(get_unaligned(&frame->daddr)); /* Make sure frame is addressed to us */ if ((info->saddr != self->saddr) && diff -urN orig/scripts/modpost.c linux/scripts/modpost.c --- orig/scripts/modpost.c Mon May 5 17:40:29 2003 +++ linux/scripts/modpost.c Thu May 8 16:40:46 2003 @@ -296,13 +296,14 @@ /* ignore global offset table */ if (strcmp(symname, "_GLOBAL_OFFSET_TABLE_") == 0) break; +#if defined(__sparc__) || defined(__sparc_v9__) if (info->hdr->e_machine == EM_SPARC || info->hdr->e_machine == EM_SPARCV9) { /* Ignore register directives. */ if (ELF_ST_TYPE(sym->st_info) == STT_REGISTER) break; } - +#endif if (memcmp(symname, MODULE_SYMBOL_PREFIX, strlen(MODULE_SYMBOL_PREFIX)) == 0) { s = alloc_symbol(symname + diff -urN orig/sound/core/Makefile linux/sound/core/Makefile --- orig/sound/core/Makefile Sun Apr 20 16:32:44 2003 +++ linux/sound/core/Makefile Wed May 7 23:49:37 2003 @@ -100,6 +100,7 @@ obj-$(CONFIG_SND_SB16) += snd-hwdep.o obj-$(CONFIG_SND_SBAWE) += snd-hwdep.o endif +obj-$(CONFIG_SND_WAVEARTIST) += snd-pcm.o snd.o snd-hwdep.o obj-$(CONFIG_SND_USB_AUDIO) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-rawmidi.o obj-m := $(sort $(obj-m)) diff -urN orig/sound/core/oss/pcm_oss.c linux/sound/core/oss/pcm_oss.c --- orig/sound/core/oss/pcm_oss.c Sun Apr 20 16:32:44 2003 +++ linux/sound/core/oss/pcm_oss.c Fri Jun 6 19:27:53 2003 @@ -696,6 +696,7 @@ { snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_sframes_t frames, frames1; +//printk(KERN_DEBUG"w2: %p %d\n", buf, bytes);/*RMK*/ if (runtime->oss.plugin_first) { snd_pcm_plugin_channel_t *channels; size_t oss_frame_bytes = (runtime->oss.plugin_first->src_width * runtime->oss.plugin_first->src_format.channels) / 8; @@ -719,6 +720,7 @@ return frames1; bytes = frames_to_bytes(runtime, frames1); } +//printk(KERN_DEBUG "w2r: %d\n", bytes);/*RMK*/ return bytes; } @@ -1874,7 +1876,7 @@ snd_pcm_oss_file_t *pcm_oss_file; snd_pcm_substream_t *substream; long result; - +//printk("-- write(%p %d)\n", buf, count);/*RMK*/ pcm_oss_file = snd_magic_cast(snd_pcm_oss_file_t, file->private_data, return -ENXIO); substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK]; if (substream == NULL) @@ -1882,6 +1884,7 @@ up(&file->f_dentry->d_inode->i_sem); result = snd_pcm_oss_write1(substream, buf, count); down(&file->f_dentry->d_inode->i_sem); +//printk("** %d\n", result);/*RMK*/ return result; } diff -urN orig/sound/core/pcm_lib.c linux/sound/core/pcm_lib.c --- orig/sound/core/pcm_lib.c Sun Apr 20 16:32:44 2003 +++ linux/sound/core/pcm_lib.c Fri Jun 6 19:42:32 2003 @@ -152,6 +152,7 @@ delta = hw_ptr_interrupt - new_hw_ptr; if (delta > 0) { +printk("pos %x new_hw_ptr %x hw_ptr_interrupt %x\n", pos, new_hw_ptr, hw_ptr_interrupt); if ((snd_pcm_uframes_t)delta < runtime->buffer_size / 2) { snd_printd("Unexpected hw_pointer value (stream = %i, delta: -%ld, max jitter = %ld): wrong interrupt acknowledge?\n", substream->stream, (long) delta, runtime->buffer_size / 2); return 0; @@ -160,6 +161,7 @@ if (runtime->hw_ptr_base == runtime->boundary) runtime->hw_ptr_base = 0; new_hw_ptr = runtime->hw_ptr_base + pos; +printk("new_hw_ptr %x\n", new_hw_ptr); } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && @@ -2041,6 +2043,7 @@ } else { char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff); snd_assert(runtime->dma_area, return -EFAULT); +//printk(KERN_DEBUG"t: %p %d\n", buf, frames_to_bytes(runtime, frames));/*RMK*/ if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames))) return -EFAULT; } @@ -2059,7 +2062,7 @@ snd_pcm_uframes_t xfer = 0; snd_pcm_uframes_t offset = 0; int err = 0; - +//printk(KERN_DEBUG"w1: %p %d %x\n", data, size, runtime->control->appl_ptr);/*RMK*/ if (size == 0) return 0; if (size > runtime->xfer_align) @@ -2206,6 +2209,7 @@ _end_unlock: spin_unlock_irq(&runtime->lock); _end: +//printk(KERN_DEBUG"w1r: %d %d\n", xfer, err);/*RMK*/ return xfer > 0 ? (snd_pcm_sframes_t)xfer : err; } diff -urN orig/sound/core/seq/Makefile linux/sound/core/seq/Makefile --- orig/sound/core/seq/Makefile Tue Feb 11 16:11:07 2003 +++ linux/sound/core/seq/Makefile Thu May 8 00:06:16 2003 @@ -83,6 +83,7 @@ obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += $(RAWMIDI_OBJS) snd-seq-midi-emul.o snd-seq-instr.o obj-$(call sequencer,$(CONFIG_SND_YMFPCI)) += $(RAWMIDI_OBJS) $(OPL3_OBJS) obj-$(call sequencer,$(CONFIG_SND_USB_AUDIO)) += $(RAWMIDI_OBJS) +obj-$(call sequencer,$(CONFIG_SND_WAVEARTIST)) += $(OPL3_OBJS) obj-$(call sequencer,$(CONFIG_SND_HDSP)) += $(RAWMIDI_OBJS) obj-m := $(sort $(obj-m)) diff -urN orig/sound/core/seq/instr/Makefile linux/sound/core/seq/instr/Makefile --- orig/sound/core/seq/instr/Makefile Tue Feb 11 16:11:07 2003 +++ linux/sound/core/seq/instr/Makefile Thu May 8 00:08:51 2003 @@ -47,5 +47,6 @@ obj-$(call sequencer,$(CONFIG_SND_SONICVIBES)) += snd-ainstr-fm.o obj-$(call sequencer,$(CONFIG_SND_TRIDENT)) += snd-ainstr-simple.o obj-$(call sequencer,$(CONFIG_SND_YMFPCI)) += snd-ainstr-fm.o +obj-$(call sequencer,$(CONFIG_SND_WAVEARTIST)) += snd-ainstr-fm.o obj-m := $(sort $(obj-m)) diff -urN orig/sound/drivers/opl3/Makefile linux/sound/drivers/opl3/Makefile --- orig/sound/drivers/opl3/Makefile Mon Mar 24 23:47:34 2003 +++ linux/sound/drivers/opl3/Makefile Wed May 7 23:42:17 2003 @@ -42,5 +42,6 @@ obj-$(CONFIG_SND_FM801) += $(OPL3_OBJS) obj-$(CONFIG_SND_SONICVIBES) += $(OPL3_OBJS) obj-$(CONFIG_SND_YMFPCI) += $(OPL3_OBJS) +obj-$(CONFIG_SND_WAVEARTIST) += $(OPL3_OBJS) obj-m := $(sort $(obj-m)) diff -urN orig/sound/isa/Kconfig linux/sound/isa/Kconfig --- orig/sound/isa/Kconfig Sun Apr 20 16:32:45 2003 +++ linux/sound/isa/Kconfig Wed May 7 00:25:06 2003 @@ -191,5 +191,9 @@ help Say 'Y' or 'M' to include support for Aztech Sound Galaxy. +config SND_WAVEARTIST + tristate "Rockwell WaveArtist RWA-010" + depends on SND + endmenu diff -urN orig/sound/isa/Makefile linux/sound/isa/Makefile --- orig/sound/isa/Makefile Mon Dec 23 18:44:22 2002 +++ linux/sound/isa/Makefile Wed May 7 00:23:12 2003 @@ -21,4 +21,4 @@ obj-$(CONFIG_SND_SGALAXY) += snd-sgalaxy.o obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ gus/ opti9xx/ \ - sb/ wavefront/ + sb/ waveartist/ wavefront/ diff -urN orig/sound/isa/waveartist/Makefile linux/sound/isa/waveartist/Makefile --- orig/sound/isa/waveartist/Makefile Thu Jan 1 01:00:00 1970 +++ linux/sound/isa/waveartist/Makefile Wed May 7 16:08:03 2003 @@ -0,0 +1,9 @@ +# +# Makefile for waveartist driver +# + +obj-$(CONFIG_SND_WAVEARTIST) := waveartist2.o + +waveartist2-y := waveartist_core.o waveartist_pcm.o waveartist_mixer.o + +waveartist2-y += netwinder.o diff -urN orig/sound/isa/waveartist/netwinder.c linux/sound/isa/waveartist/netwinder.c --- orig/sound/isa/waveartist/netwinder.c Thu Jan 1 01:00:00 1970 +++ linux/sound/isa/waveartist/netwinder.c Thu May 8 00:31:13 2003 @@ -0,0 +1,83 @@ +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "waveartist.h" + +static LIST_HEAD(waveartists); + +static int __init nw_waveartist_init(void) +{ + snd_card_t *card; + snd_pcm_t *pcm; + opl3_t *opl3; + struct wa_chip *wa; + int ret; + + card = snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + wa = snd_waveartist_create(card, 0x250, 12, 3, 7); + if (IS_ERR(wa)) { + ret = PTR_ERR(wa); + goto free_card; + } + + ret = snd_waveartist_pcm(wa, 0, &pcm); + if (ret != 0) + goto free_card; + + ret = snd_opl3_create(card, 0x388, 0x38a, OPL3_HW_OPL3, 0, &opl3); + if (ret != 0) + goto free_card; + + ret = snd_opl3_hwdep_new(opl3, 0, 1, NULL); + if (ret != 0) + goto free_card; + + strncpy(card->driver, "waveartist", sizeof(card->driver)); + strncpy(card->shortname, "NetWinder", sizeof(card->shortname)); + snprintf(card->longname, sizeof(card->longname), + "%s at 0x%lx, irq %d, dma %d/%d", + pcm->name, wa->base, wa->irq, + wa->playback.dma, wa->capture.dma); + + ret = snd_waveartist_ctl(wa); + if (ret) + goto free_card; + + ret = snd_card_register(card); + if (ret == 0) { + list_add(&wa->node, &waveartists); + goto out; + } + + free_card: + snd_card_free(card); + out: + return ret; +} + +static void __exit nw_waveartist_exit(void) +{ + struct list_head *l, *n; + + list_for_each_safe(l, n, &waveartists) { + struct wa_chip *wa = list_entry(l, struct wa_chip, node); + + snd_card_free(wa->card); + } +} + +module_init(nw_waveartist_init); +module_exit(nw_waveartist_exit); + +MODULE_DESCRIPTION("NetWinder WaveArtist RWA-010 sound driver"); +MODULE_LICENSE("GPL"); diff -urN orig/sound/isa/waveartist/waveartist.h linux/sound/isa/waveartist/waveartist.h --- orig/sound/isa/waveartist/waveartist.h Thu Jan 1 01:00:00 1970 +++ linux/sound/isa/waveartist/waveartist.h Wed May 7 00:02:46 2003 @@ -0,0 +1,26 @@ +struct wa_channel { + snd_pcm_substream_t *substream; /* the substream associated with this channel */ + unsigned int dma; /* dma channel number */ + unsigned int dma_size; /* size of dma buffer */ + unsigned int size; /* number of samples for one channel */ + unsigned int running; /* are we active? */ +}; + +struct wa_chip { + struct list_head node; + snd_card_t *card; + snd_pcm_t *pcm; + unsigned long base; + unsigned int irq; + unsigned int ctl; + spinlock_t lock; + struct wa_channel playback; + struct wa_channel capture; +}; + +int snd_waveartist_pcm(struct wa_chip *wa, int device, snd_pcm_t **rpcm); + +struct wa_chip * +snd_waveartist_create(snd_card_t *card, unsigned long base, int irq, + int play_dma, int rec_dma); + diff -urN orig/sound/isa/waveartist/waveartist_core.c linux/sound/isa/waveartist/waveartist_core.c --- orig/sound/isa/waveartist/waveartist_core.c Thu Jan 1 01:00:00 1970 +++ linux/sound/isa/waveartist/waveartist_core.c Wed May 7 14:03:26 2003 @@ -0,0 +1,206 @@ +/* + * linux/sound/isa/waveartist_core.c + * + * Copyright (C) 2003 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. + */ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "waveartist_core.h" + +int snd_waveartist_cmd(struct wa_chip *wa, int csz, unsigned int *cdata, + int rsz, unsigned int *rdata) +{ + unsigned int i, timed_out = 0; + + if (inb(wa->base + STATR) & STATR_CMDRF) { + inw(wa->base + CMDR); + udelay(10); + } + + for (i = 0; !timed_out && i < csz; i++) { + int count; + + for (count = 5000; count; count--) + if (inb(wa->base + STATR) & STATR_CMDWE) + break; + + if (count == 0) + timed_out = 1; + else + outw(cdata[i], wa->base + CMDR); + } + + for (i = 0; !timed_out && i < rsz; i++) { + int count; + + for (count = 5000; count; count--) + if (inb(wa->base + STATR) & STATR_CMDRF) + break; + + if (count == 0) + timed_out = 1; + else + rdata[i] = inw(wa->base + CMDR); + } + + if (timed_out) { + printk(KERN_WARNING "waveartist: command "); + for (i = 0; i < csz; i++) + printk("%04x ", cdata[i]); + printk("timed out\n"); + } + return timed_out; +} + +static int wa_reset(struct wa_chip *wa) +{ + unsigned int timeout, res = (unsigned int)-1; + + outb(RESET, wa->base + CTLR); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(20*HZ/100); + outb(0, wa->base + CTLR); + + for (timeout = 500; timeout; timeout--) { + mdelay(2); + + if (inb(wa->base + STATR) & STATR_CMDRF) { + res = inw(wa->base + CMDR); + if (res == 0x55aa) + return 0; + } + } + + printk(KERN_WARNING "WaveArtist: reset timed out (res=%04x)\n", + res); + return -ENODEV; +} + +static irqreturn_t wa_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct wa_chip *wa = dev_id; + unsigned int irqstat, statr; + + spin_lock(&wa->lock); + + irqstat = inb(wa->base + IRQSTAT); + statr = inb(wa->base + STATR); + + if (!(statr & STATR_IRQREQ)) + goto no_irq; + + outb(wa->ctl | IRQ_ACK, wa->base + CTLR); + outb(wa->ctl, wa->base + CTLR); + + spin_unlock(&wa->lock); + + if (irqstat & 0x01) + snd_waveartist_pcm_interrupt(wa, statr); + if (irqstat & 0x02) + printk(KERN_WARNING "waveartist: unexpected SB interrupt\n"); + + return IRQ_HANDLED; + + no_irq: + spin_unlock(&wa->lock); + + return IRQ_NONE; +} + +static int snd_waveartist_free(struct wa_chip *wa) +{ + if (wa->capture.dma != -1) { + snd_dma_disable(wa->capture.dma); + free_dma(wa->capture.dma); + } + if (wa->playback.dma != -1) { + snd_dma_disable(wa->playback.dma); + free_dma(wa->playback.dma); + } + if (wa->irq != -1) + free_irq(wa->irq, wa); + if (wa->base != (unsigned long)-1) + release_region(wa->base, 15); + kfree(wa); + return 0; +} + +static int snd_waveartist_dev_free(snd_device_t *device) +{ + return snd_waveartist_free(device->device_data); +} + +static snd_device_ops_t snd_waveartist_ops = { + .dev_free = snd_waveartist_dev_free, +}; + +struct wa_chip * +snd_waveartist_create(snd_card_t *card, unsigned long base, int irq, + int play_dma, int rec_dma) +{ + struct wa_chip *wa; + int ret; + + wa = kmalloc(sizeof(struct wa_chip), GFP_KERNEL); + if (wa == NULL) + return ERR_PTR(-ENOMEM); + + memset(wa, 0, sizeof(struct wa_chip)); + + spin_lock_init(&wa->lock); + wa->card = card; + wa->base = (unsigned long)-1; + wa->irq = -1; + wa->playback.dma = -1; + wa->capture.dma = -1; + + if (!request_region(base, 15, "waveartist")) { + ret = -EBUSY; + goto busy; + } + wa->base = base; + + ret = request_irq(irq, wa_interrupt, SA_INTERRUPT, "waveartist", wa); + if (ret) + goto busy; + wa->irq = irq; + + ret = request_dma(play_dma, "waveartist"); + if (ret) + goto busy; + wa->playback.dma = play_dma; + + if (rec_dma != -1 && rec_dma != play_dma) { + ret = request_dma(rec_dma, "waveartist"); + if (ret) + goto busy; + wa->capture.dma = rec_dma; + } + + ret = wa_reset(wa); + if (ret) + goto busy; + + ret = snd_device_new(card, SNDRV_DEV_LOWLEVEL, wa, &snd_waveartist_ops); + if (ret == 0) + return wa; + + busy: + snd_waveartist_free(wa); + return ERR_PTR(ret); +} diff -urN orig/sound/isa/waveartist/waveartist_core.h linux/sound/isa/waveartist/waveartist_core.h --- orig/sound/isa/waveartist/waveartist_core.h Thu Jan 1 01:00:00 1970 +++ linux/sound/isa/waveartist/waveartist_core.h Wed May 7 19:59:43 2003 @@ -0,0 +1,123 @@ +#include "waveartist.h" + +//registers +#define CMDR 0 +#define DATR 2 +#define CTLR 4 +#define STATR 5 +#define IRQSTAT 12 + +//bit defs +//reg STATR +#define STATR_CMDWE 0x80 +#define STATR_CMDRF 0x40 +#define STATR_DATWE 0x20 +#define STATR_DATRF 0x10 + +#define STATR_IRQREQ 0x08 +#define STATR_DMA1 0x04 +#define STATR_DMA0 0x02 + +//bit defs +//reg CTLR +#define CMD_WEIE 0x80 +#define CMD_RFIE 0x40 +#define DAT_WEIE 0x20 +#define DAT_RFIE 0x10 + +#define RESET 0x08 +#define DMA1_IE 0x04 +#define DMA0_IE 0x02 +#define IRQ_ACK 0x01 + +//commands + +#define WACMD_SYSTEMID 0x00 +#define WACMD_GETREV 0x00 +#define WACMD_INPUTFORMAT 0x10 //0-8S, 1-16S, 2-8U +#define WACMD_INPUTCHANNELS 0x11 //1-Mono, 2-Stereo +#define WACMD_INPUTSPEED 0x12 //sampling rate +#define WACMD_INPUTDMA 0x13 //0-8bit, 1-16bit, 2-PIO +#define WACMD_INPUTSIZE 0x14 //samples to interrupt +#define WACMD_INPUTSTART 0x15 //start ADC +#define WACMD_INPUTPAUSE 0x16 //pause ADC +#define WACMD_INPUTSTOP 0x17 //stop ADC +#define WACMD_INPUTRESUME 0x18 //resume ADC +#define WACMD_INPUTPIO 0x19 //PIO ADC + +#define WACMD_OUTPUTFORMAT 0x20 //0-8S, 1-16S, 2-8U +#define WACMD_OUTPUTCHANNELS 0x21 //1-Mono, 2-Stereo +#define WACMD_OUTPUTSPEED 0x22 //sampling rate +#define WACMD_OUTPUTDMA 0x23 //0-8bit, 1-16bit, 2-PIO +#define WACMD_OUTPUTSIZE 0x24 //samples to interrupt +#define WACMD_OUTPUTSTART 0x25 //start ADC +#define WACMD_OUTPUTPAUSE 0x26 //pause ADC +#define WACMD_OUTPUTSTOP 0x27 //stop ADC +#define WACMD_OUTPUTRESUME 0x28 //resume ADC +#define WACMD_OUTPUTPIO 0x29 //PIO ADC + +#define WACMD_GET_LEVEL 0x30 +#define WACMD_SET_LEVEL 0x31 +#define WACMD_SET_MIXER 0x32 +#define WACMD_RST_MIXER 0x33 +#define WACMD_SET_MONO 0x34 + +/* + * Definitions for left/right recording input mux + */ +#define ADC_MUX_NONE 0 +#define ADC_MUX_MIXER 1 +#define ADC_MUX_LINE 2 +#define ADC_MUX_AUX2 3 +#define ADC_MUX_AUX1 4 +#define ADC_MUX_MIC 5 + +/* + * Definitions for mixer gain settings + */ +#define MIX_GAIN_LINE 0 /* line in */ +#define MIX_GAIN_AUX1 1 /* aux1 */ +#define MIX_GAIN_AUX2 2 /* aux2 */ +#define MIX_GAIN_XMIC 3 /* crossover mic */ +#define MIX_GAIN_MIC 4 /* normal mic */ +#define MIX_GAIN_PREMIC 5 /* preamp mic */ +#define MIX_GAIN_OUT 6 /* output */ +#define MIX_GAIN_MONO 7 /* mono in */ + +extern void snd_waveartist_pcm_interrupt(struct wa_chip *wa, unsigned int statr); + +extern int snd_waveartist_cmd(struct wa_chip *wa, int csz, unsigned int *cdata, + int rsz, unsigned int *rdata); + +static inline int wa_cmd1(struct wa_chip *wa, unsigned int cmd) +{ + return snd_waveartist_cmd(wa, 1, &cmd, 0, NULL); +} + +static inline unsigned int wa_cmd1_r(struct wa_chip *wa, unsigned int cmd) +{ + unsigned int ret; + snd_waveartist_cmd(wa, 1, &cmd, 1, &ret); + return ret; +} + +static inline void +wa_cmd2(struct wa_chip *wa, unsigned int cmd, unsigned int arg) +{ + unsigned int vals[2]; + + vals[0] = cmd; + vals[1] = arg; + snd_waveartist_cmd(wa, 2, vals, 1, vals); +} + +static inline void +wa_cmd3(struct wa_chip *wa, unsigned int cmd, unsigned int arg1, unsigned int arg2) +{ + unsigned int vals[3]; + + vals[0] = cmd; + vals[1] = arg1; + vals[2] = arg2; + snd_waveartist_cmd(wa, 3, vals, 0, NULL); +} diff -urN orig/sound/isa/waveartist/waveartist_mixer.c linux/sound/isa/waveartist/waveartist_mixer.c --- orig/sound/isa/waveartist/waveartist_mixer.c Thu Jan 1 01:00:00 1970 +++ linux/sound/isa/waveartist/waveartist_mixer.c Wed May 7 23:10:12 2003 @@ -0,0 +1,488 @@ +/* + * linux/sound/isa/waveartist_mixer.c + * + * Copyright (C) 2003 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. + */ +#include +#include + +#include +#include +#include +#include + +#include "waveartist_core.h" + +struct wa_mixer { + const char *name; + snd_kcontrol_new_t *ctl; + unsigned char reg_l; + unsigned char reg_r; + unsigned char shift; + unsigned short max; +}; + +static int snd_wa_single_info(snd_kcontrol_t *kc, snd_ctl_elem_info_t *uc) +{ + struct wa_mixer *m = (struct wa_mixer *)kc->private_value; + + uc->type = m->max > 1 ? SNDRV_CTL_ELEM_TYPE_INTEGER : + SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uc->count = 1; + uc->value.integer.min = 0; + uc->value.integer.max = m->max; + + return 0; +} + +static int snd_wa_single_get(snd_kcontrol_t *kc, snd_ctl_elem_value_t *uc) +{ + struct wa_chip *wa = kc->private_data; + struct wa_mixer *m = (struct wa_mixer *)kc->private_value; + unsigned int val; + unsigned long flags; + + spin_lock_irqsave(&wa->lock, flags); + val = wa_cmd1_r(wa, WACMD_GET_LEVEL | m->reg_l << 8); + spin_unlock_irqrestore(&wa->lock, flags); + + if (m->shift) + val >>= m->shift; + + uc->value.integer.value[0] = val & m->max; + + return 0; +} + +static int snd_wa_single_put(snd_kcontrol_t *kc, snd_ctl_elem_value_t *uc) +{ + struct wa_chip *wa = kc->private_data; + struct wa_mixer *m = (struct wa_mixer *)kc->private_value; + unsigned int val, mask, newval; + unsigned long flags; + + mask = m->max; + newval = uc->value.integer.value[0] & m->max; + + if (m->shift) { + mask <<= m->shift; + newval <<= m->shift; + } + + spin_lock_irqsave(&wa->lock, flags); + val = wa_cmd1_r(wa, WACMD_GET_LEVEL | m->reg_l << 8); + + val = (val & ~mask) | newval; + + wa_cmd3(wa, WACMD_SET_MIXER, val, val); + spin_unlock_irqrestore(&wa->lock, flags); +printk("set_mixer: %04x %04x\n", val, val); + + return 1; +} + +static snd_kcontrol_new_t mix_single = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_wa_single_info, + .get = snd_wa_single_get, + .put = snd_wa_single_put, +}; + +static int snd_wa_double_info(snd_kcontrol_t *kc, snd_ctl_elem_info_t *uc) +{ + struct wa_mixer *m = (struct wa_mixer *)kc->private_value; + + uc->type = m->max > 1 ? SNDRV_CTL_ELEM_TYPE_INTEGER : + SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uc->count = 2; + uc->value.integer.min = 0; + uc->value.integer.max = m->max; + + return 0; +} + +static int snd_wa_double_get(snd_kcontrol_t *kc, snd_ctl_elem_value_t *uc) +{ + struct wa_chip *wa = kc->private_data; + struct wa_mixer *m = (struct wa_mixer *)kc->private_value; + unsigned int left, right; + unsigned long flags; + + spin_lock_irqsave(&wa->lock, flags); + left = wa_cmd1_r(wa, WACMD_GET_LEVEL | m->reg_l << 8); + right = wa_cmd1_r(wa, WACMD_GET_LEVEL | m->reg_r << 8); + spin_unlock_irqrestore(&wa->lock, flags); + + if (m->shift) { + left >>= m->shift; + right >>= m->shift; + } + + uc->value.integer.value[0] = left & m->max; + uc->value.integer.value[1] = right & m->max; + + return 0; +} + +static int snd_wa_mix_double_put(snd_kcontrol_t *kc, snd_ctl_elem_value_t *uc) +{ + struct wa_chip *wa = kc->private_data; + struct wa_mixer *m = (struct wa_mixer *)kc->private_value; + unsigned int left, right, mask, newleft, newright; + unsigned long flags; + + mask = m->max; + newleft = uc->value.integer.value[0] & m->max; + newright = uc->value.integer.value[1] & m->max; + + if (m->shift) { + mask <<= m->shift; + newleft <<= m->shift; + newright <<= m->shift; + } + + spin_lock_irqsave(&wa->lock, flags); + left = wa_cmd1_r(wa, WACMD_GET_LEVEL | m->reg_l << 8); + right = wa_cmd1_r(wa, WACMD_GET_LEVEL | m->reg_r << 8); + + left = (left & ~mask) | newleft; + right = (right & ~mask) | newright; + + wa_cmd3(wa, WACMD_SET_MIXER, left, right); + spin_unlock_irqrestore(&wa->lock, flags); +printk("set_mixer: %04x %04x\n", left, right); + + return 1; +} + +static snd_kcontrol_new_t mix_double = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_wa_double_info, + .get = snd_wa_double_get, + .put = snd_wa_mix_double_put, +}; + +static int snd_wa_lev_double_get(snd_kcontrol_t *kc, snd_ctl_elem_value_t *uc) +{ + struct wa_chip *wa = kc->private_data; + struct wa_mixer *m = (struct wa_mixer *)kc->private_value; + unsigned int left, right; + unsigned long flags; + + spin_lock_irqsave(&wa->lock, flags); + left = wa_cmd1_r(wa, WACMD_GET_LEVEL | (10 + m->reg_l * 2) << 8); + right = wa_cmd1_r(wa, WACMD_GET_LEVEL | (11 + m->reg_l * 2) << 8); + spin_unlock_irqrestore(&wa->lock, flags); + + uc->value.integer.value[0] = left & m->max; + uc->value.integer.value[1] = right & m->max; + + return 0; +} + +static int snd_wa_lev_double_put(snd_kcontrol_t *kc, snd_ctl_elem_value_t *uc) +{ + struct wa_chip *wa = kc->private_data; + struct wa_mixer *m = (struct wa_mixer *)kc->private_value; + unsigned int left, right, reg; + unsigned long flags; + + reg = m->reg_l * 256; + left = uc->value.integer.value[0] & m->max; + right = uc->value.integer.value[1] & m->max; + + spin_lock_irqsave(&wa->lock, flags); + wa_cmd3(wa, WACMD_SET_LEVEL | reg, left, right); + spin_unlock_irqrestore(&wa->lock, flags); + + return 1; +} + +static snd_kcontrol_new_t lev_double = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_wa_double_info, + .get = snd_wa_lev_double_get, + .put = snd_wa_lev_double_put, +}; + +static const char *sources[] = { + "None", + "Master", + "Line", + "Aux2", + "Aux1", + "Mic", + "In6", + "In7", +}; + +static int snd_wa_cap_src_info(snd_kcontrol_t *kc, snd_ctl_elem_info_t *uc) +{ + struct wa_mixer *m = (struct wa_mixer *)kc->private_value; + + uc->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uc->count = 2; + uc->value.enumerated.items = ARRAY_SIZE(sources); + if (uc->value.enumerated.item >= uc->value.enumerated.items) + uc->value.enumerated.item = uc->value.enumerated.items - 1; + strcpy(uc->value.enumerated.name, sources[uc->value.enumerated.item]); + + return 0; +} + +static int snd_wa_cap_src_get(snd_kcontrol_t *kc, snd_ctl_elem_value_t *uc) +{ + struct wa_chip *wa = kc->private_data; + struct wa_mixer *m = (struct wa_mixer *)kc->private_value; + unsigned long flags; + unsigned int val; + + spin_lock_irqsave(&wa->lock, flags); + val = wa_cmd1_r(wa, WACMD_GET_LEVEL | 8 << 8); + spin_unlock_irqrestore(&wa->lock, flags); + + uc->value.enumerated.item[0] = val & 7; + uc->value.enumerated.item[1] = (val >> 3) & 7; + + return 0; +} + +static int snd_wa_cap_src_put(snd_kcontrol_t *kc, snd_ctl_elem_value_t *uc) +{ + struct wa_chip *wa = kc->private_data; + struct wa_mixer *m = (struct wa_mixer *)kc->private_value; + unsigned long flags; + unsigned int val, newval; + + newval = uc->value.enumerated.item[0] | + uc->value.enumerated.item[1] << 3; + + spin_lock_irqsave(&wa->lock, flags); + val = wa_cmd1_r(wa, WACMD_GET_LEVEL | 8 << 8); + + newval = (val & 0xffc0) | newval; + + wa_cmd3(wa, WACMD_SET_MIXER, newval, newval); + spin_unlock_irqrestore(&wa->lock, flags); + + return val != newval; +} + +static snd_kcontrol_new_t capture_source = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_wa_cap_src_info, + .get = snd_wa_cap_src_get, + .put = snd_wa_cap_src_put, +}; + +static struct wa_mixer mixers[] = { + { + .name = "Master Playback Volume", + .ctl = &mix_double, + .reg_l = 2, + .reg_r = 6, + .shift = 1, + .max = 7, + }, + { + .name = "Master Playback Switch", + .ctl = &mix_double, + .reg_l = 0, + .reg_r = 4, + .shift = 0, + .max = 1, + }, + { + .name = "Mono Output Playback Switch", + .ctl = &mix_single, + .reg_l = 1, + .shift = 0, + .max = 1 + }, + { + .name = "PCM Playback Volume", + .ctl = &lev_double, + .reg_l = 0, + .shift = 0, + .max = 32767, + }, + { + .name = "PCM Playback Switch", + .ctl = &mix_double, + .reg_l = 3, + .reg_r = 7, + .shift = 10, + .max = 1, + }, + { + .name = "FM Playback Volume", + .ctl = &lev_double, + .reg_l = 1, + .shift = 0, + .max = 32767, + }, + { + .name = "Wave Playback Volume", + .ctl = &lev_double, + .reg_l = 2, + .shift = 0, + .max = 32767, + }, + { + .name = "Line Playback Volume", + .ctl = &mix_double, + .reg_l = 0, + .reg_r = 4, + .shift = 6, + .max = 31, + }, + { + .name = "Line Playback Switch", + .ctl = &mix_double, + .reg_l = 3, + .reg_r = 7, + .shift = 4, + .max = 1, + }, + { + .name = "Aux1 Playback Volume", + .ctl = &mix_double, + .reg_l = 0, + .reg_r = 4, + .shift = 1, + .max = 31, + }, + { + .name = "Aux1 Playback Switch", + .ctl = &mix_double, + .reg_l = 3, + .reg_r = 7, + .shift = 6, + .max = 1, + }, + { + .name = "Aux2 Playback Volume", + .ctl = &mix_double, + .reg_l = 1, + .reg_r = 5, + .shift = 6, + .max = 31, + }, + { + .name = "Mic Playback Volume", + .ctl = &mix_double, + .reg_l = 2, + .reg_r = 6, + .shift = 6, + .max = 31, + }, + { + .name = "Mic Playback Switch", + .ctl = &mix_double, + .reg_l = 3, + .reg_r = 7, + .shift = 7, + .max = 1, + }, + { + .name = "Micx Playback Volume", + .ctl = &mix_double, + .reg_l = 1, + .reg_r = 5, + .shift = 1, + .max = 31, + }, + { + .name = "Micx Playback Switch", + .ctl = &mix_double, + .reg_l = 3, + .reg_r = 7, + .shift = 8, + .max = 1, + }, + { + .name = "Mono Playback Volume", + .ctl = &mix_single, + .reg_l = 8, + .shift = 6, + .max = 31, + }, + { + .name = "Capture Volume", + .ctl = &mix_double, + .reg_l = 3, + .reg_r = 7, + .shift = 0, + .max = 15, + }, + { + .name = "Mic Boost", + .ctl = &mix_double, + .reg_l = 2, + .reg_r = 6, + .shift = 4, + .max = 3, + }, + { + .name = "Capture Source", + .ctl = &capture_source, + }, + { + .name = "2 Playback Switch", + .ctl = &mix_double, + .reg_l = 3, + .reg_r = 7, + .shift = 5, + .max = 1, + }, + { + .name = "6 Playback Switch", + .ctl = &mix_double, + .reg_l = 3, + .reg_r = 7, + .shift = 9, + .max = 1, + }, +}; + +int snd_wa_ctl_add(struct wa_chip *wa, struct wa_mixer *m) +{ + snd_kcontrol_t *ctl; + int ret; + + ctl = snd_ctl_new1(m->ctl, wa); + if (!ctl) + return -ENOMEM; + + strncpy(ctl->id.name, m->name, sizeof(ctl->id.name) - 1); + ctl->private_value = (unsigned long)m; + + ret = snd_ctl_add(wa->card, ctl); + if (ret < 0) + snd_ctl_free_one(ctl); + return ret; +} + +int snd_waveartist_ctl(struct wa_chip *wa) +{ + int i, ret; + + strncpy(wa->card->mixername, wa->pcm->name, + sizeof(wa->card->mixername) - 1); + + for (i = 0; i < ARRAY_SIZE(mixers); i++) { + ret = snd_wa_ctl_add(wa, &mixers[i]); + if (ret) + break; + } + + return ret; +} diff -urN orig/sound/isa/waveartist/waveartist_pcm.c linux/sound/isa/waveartist/waveartist_pcm.c --- orig/sound/isa/waveartist/waveartist_pcm.c Thu Jan 1 01:00:00 1970 +++ linux/sound/isa/waveartist/waveartist_pcm.c Wed May 7 22:57:37 2003 @@ -0,0 +1,438 @@ +#include +#include +#include +#include + +#include +#include + +#include "waveartist_core.h" + +static inline unsigned int wa_speed(unsigned int rate) +{ + unsigned int speed; + + if (rate == 8000) + speed = 0x2e71; + else if (rate == 11025) + speed = 0x4000; + else if (rate == 22050) + speed = 0x8000; + else if (rate == 44100) + speed = 0x0000; + else { + speed = rate << 16; + speed = (speed / 44100) & 65535; + } + + return speed; +} + +static inline unsigned int wa_sample_format(snd_pcm_format_t format) +{ + unsigned int res; + + if (format == SNDRV_PCM_FORMAT_S16_LE) + res = 1; + else if (format == SNDRV_PCM_FORMAT_S8) + res = 0; + else + res = 2; /* default: SNDRV_PCM_FMTBIT_U8 */ + return res; +} + +void snd_waveartist_pcm_interrupt(struct wa_chip *wa, unsigned int statr) +{ + if (statr & STATR_DMA0 && wa->playback.substream) + snd_pcm_period_elapsed(wa->playback.substream); + + if (statr & STATR_DMA1 && wa->capture.substream) + snd_pcm_period_elapsed(wa->capture.substream); +} + +static snd_pcm_hardware_t snd_wa_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_START, + .formats = SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 5000, + .rate_max = 44100, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 128*1024, + .period_bytes_min = 64, + .period_bytes_max = 128*1024, + .periods_min = 1, + .periods_max = 1024, + .fifo_size = 0, +}; + +/* ============================= Playback ================================= */ + +static int snd_wa_playback_open(snd_pcm_substream_t *substream) +{ + struct wa_chip *wa = substream->private_data; + snd_pcm_runtime_t *runtime = substream->runtime; + + wa->playback.substream = substream; + + spin_lock_irq(&wa->lock); + wa->ctl |= DMA0_IE; + outb(wa->ctl, wa->base + CTLR); + spin_unlock_irq(&wa->lock); + + memcpy(&runtime->hw, &snd_wa_hardware, sizeof(snd_wa_hardware)); + snd_pcm_limit_isa_dma_size(wa->playback.dma, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(wa->playback.dma, &runtime->hw.period_bytes_max); + + snd_pcm_set_sync(substream); + + return 0; +} + +static int snd_wa_playback_close(snd_pcm_substream_t *substream) +{ + struct wa_chip *wa = substream->private_data; + + spin_lock_irq(&wa->lock); + wa->playback.running = 0; + wa_cmd1(wa, WACMD_OUTPUTSTOP); + + wa->ctl &= ~DMA0_IE; + outb(wa->ctl, wa->base + CTLR); + + snd_dma_disable(wa->playback.dma); + spin_unlock_irq(&wa->lock); + + wa->playback.substream = NULL; + return 0; +} + +static int +snd_wa_playback_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params) +{ + struct wa_chip *wa = substream->private_data; + unsigned int format, speed, channels, err; + unsigned long flags; + + if (wa->playback.running) { + printk(KERN_ERR "waveartist: %s called while playback " + "running\n", __FUNCTION__); + return -EBUSY; + } + + err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + if (err < 0) + return err; + + speed = wa_speed(params_rate(params)); + format = wa_sample_format(params_format(params)); + channels = params_channels(params); + + spin_lock_irqsave(&wa->lock, flags); + + wa_cmd2(wa, WACMD_OUTPUTFORMAT, format); + wa_cmd2(wa, WACMD_OUTPUTCHANNELS, channels); + wa_cmd2(wa, WACMD_OUTPUTSPEED, speed); + wa_cmd2(wa, WACMD_OUTPUTDMA, wa->playback.dma < 4 ? 0 : 1); + wa_cmd2(wa, WACMD_OUTPUTFORMAT, format); + + spin_unlock_irqrestore(&wa->lock, flags); + + return 0; +} + +static int snd_wa_playback_prepare(snd_pcm_substream_t *substream) +{ + struct wa_chip *wa = substream->private_data; + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + unsigned int count, size; + + count = snd_pcm_lib_period_bytes(substream); + if (runtime->format == SNDRV_PCM_FORMAT_S16_LE) + count >>= 1; + if (runtime->channels == 2) + count >>= 1; + + size = snd_pcm_lib_buffer_bytes(substream); + + spin_lock_irqsave(&wa->lock, flags); + wa->playback.dma_size = size; + snd_dma_program(wa->playback.dma, runtime->dma_addr, size, + DMA_MODE_WRITE | DMA_AUTOINIT); + + if (wa->playback.size != count) { + wa->playback.size = count; + wa_cmd2(wa, WACMD_OUTPUTSIZE, count - 1); + } + spin_unlock_irqrestore(&wa->lock, flags); + + return 0; +} + +static int snd_wa_playback_trigger(snd_pcm_substream_t *substream, int cmd) +{ + struct wa_chip *wa = substream->private_data; + unsigned long flags; + int ret = -EINVAL; + + spin_lock_irqsave(&wa->lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + wa->playback.running = 1; + wa_cmd1(wa, WACMD_OUTPUTSTART); + ret = 0; + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + wa->playback.running = 0; + wa_cmd1(wa, WACMD_OUTPUTSTOP); + ret = 0; + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + wa_cmd1(wa, WACMD_OUTPUTPAUSE); + ret = 0; + break; + + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + wa_cmd1(wa, WACMD_OUTPUTRESUME); + ret = 0; + break; + } + spin_unlock_irqrestore(&wa->lock, flags); + + return ret; +} + +static snd_pcm_uframes_t snd_wa_playback_pointer(snd_pcm_substream_t *substream) +{ + struct wa_chip *wa = substream->private_data; + size_t ptr; + + if (!wa->playback.running) + return 0; + ptr = snd_dma_pointer(wa->playback.dma, wa->playback.dma_size); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_ops_t snd_waveartist_playback_ops = { + .open = snd_wa_playback_open, + .close = snd_wa_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_wa_playback_hw_params, + .hw_free = snd_pcm_lib_free_pages, + .prepare = snd_wa_playback_prepare, + .trigger = snd_wa_playback_trigger, + .pointer = snd_wa_playback_pointer, +}; + +/* ============================= Capture ================================== */ + +static int snd_wa_capture_open(snd_pcm_substream_t *substream) +{ + struct wa_chip *wa = substream->private_data; + snd_pcm_runtime_t *runtime = substream->runtime; + + wa->capture.substream = substream; + + spin_lock_irq(&wa->lock); + wa->ctl |= DMA1_IE; + outb(wa->ctl, wa->base + CTLR); + spin_unlock_irq(&wa->lock); + + memcpy(&runtime->hw, &snd_wa_hardware, sizeof(snd_wa_hardware)); + snd_pcm_limit_isa_dma_size(wa->capture.dma, &runtime->hw.buffer_bytes_max); + snd_pcm_limit_isa_dma_size(wa->capture.dma, &runtime->hw.period_bytes_max); + + snd_pcm_set_sync(substream); + + return 0; +} + +static int snd_wa_capture_close(snd_pcm_substream_t *substream) +{ + struct wa_chip *wa = substream->private_data; + + spin_lock_irq(&wa->lock); + wa->capture.running = 0; + wa_cmd1(wa, WACMD_INPUTSTOP); + + wa->ctl &= ~DMA1_IE; + outb(wa->ctl, wa->base + CTLR); + + snd_dma_disable(wa->playback.dma); + spin_unlock_irq(&wa->lock); + + wa->capture.substream = NULL; + return 0; +} + +static int +snd_wa_capture_hw_params(snd_pcm_substream_t *substream, snd_pcm_hw_params_t *params) +{ + struct wa_chip *wa = substream->private_data; + unsigned int bits, speed, channels, err; + unsigned long flags; + + if (wa->capture.running) { + printk(KERN_ERR "waveartist: %s called while capture " + "running\n", __FUNCTION__); + return -EBUSY; + } + + err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + if (err < 0) + return err; + + speed = wa_speed(params_rate(params)); + bits = wa_sample_format(params_format(params)); + channels = params_channels(params); + + spin_lock_irqsave(&wa->lock, flags); + + wa_cmd2(wa, WACMD_INPUTFORMAT, bits); + wa_cmd2(wa, WACMD_INPUTCHANNELS, channels); + wa_cmd2(wa, WACMD_INPUTSPEED, speed); + wa_cmd2(wa, WACMD_INPUTDMA, wa->capture.dma < 4 ? 0 : 1); + wa_cmd2(wa, WACMD_INPUTFORMAT, bits); + + spin_unlock_irqrestore(&wa->lock, flags); + + return 0; +} + +static int snd_wa_capture_prepare(snd_pcm_substream_t *substream) +{ + struct wa_chip *wa = substream->private_data; + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned long flags; + unsigned int count, size; + + count = snd_pcm_lib_period_bytes(substream); + if (runtime->format == SNDRV_PCM_FMTBIT_S16_LE) + count >>= 1; + if (runtime->channels == 2) + count >>= 1; + + size = snd_pcm_lib_buffer_bytes(substream); + + spin_lock_irqsave(&wa->lock, flags); + wa->capture.dma_size = size; + snd_dma_program(wa->capture.dma, runtime->dma_addr, size, + DMA_MODE_READ | DMA_AUTOINIT); + + if (wa->capture.size != count) { + wa->capture.size = count; + wa_cmd2(wa, WACMD_INPUTSIZE, count - 1); + } + spin_unlock_irqrestore(&wa->lock, flags); + + return 0; +} + +static int snd_wa_capture_trigger(snd_pcm_substream_t *substream, int cmd) +{ + struct wa_chip *wa = substream->private_data; + unsigned long flags; + int ret = -EINVAL; + + spin_lock_irqsave(&wa->lock, flags); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + wa->capture.running = 1; + wa_cmd1(wa, WACMD_INPUTSTART); + ret = 0; + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + wa->capture.running = 0; + wa_cmd1(wa, WACMD_INPUTSTOP); + ret = 0; + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + wa_cmd1(wa, WACMD_INPUTPAUSE); + ret = 0; + break; + + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + wa_cmd1(wa, WACMD_INPUTRESUME); + ret = 0; + break; + } + spin_unlock_irqrestore(&wa->lock, flags); + + return ret; +} + +static snd_pcm_uframes_t snd_wa_capture_pointer(snd_pcm_substream_t *substream) +{ + struct wa_chip *wa = substream->private_data; + size_t ptr; + + if (!wa->capture.running) + return 0; + ptr = snd_dma_pointer(wa->capture.dma, wa->capture.dma_size); + return bytes_to_frames(substream->runtime, ptr); +} + +static snd_pcm_ops_t snd_waveartist_capture_ops = { + .open = snd_wa_capture_open, + .close = snd_wa_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_wa_capture_hw_params, + .hw_free = snd_pcm_lib_free_pages, + .prepare = snd_wa_capture_prepare, + .trigger = snd_wa_capture_trigger, + .pointer = snd_wa_capture_pointer, +}; + +static void snd_waveartist_pcm_free(snd_pcm_t *pcm) +{ + struct wa_chip *wa = pcm->private_data; + + wa->pcm = NULL; + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +int snd_waveartist_pcm(struct wa_chip *wa, int device, snd_pcm_t **rpcm) +{ + snd_pcm_t *pcm; + int err; + + err = snd_pcm_new(wa->card, "WaveArtist", device, 1, 1, &pcm); + if (err < 0) + return err; + + spin_lock_init(&wa->lock); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_waveartist_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_waveartist_capture_ops); + + pcm->private_data = wa; + pcm->private_free = snd_waveartist_pcm_free; + pcm->info_flags = 0; + + strncpy(pcm->name, "Rockwell WaveArtist RWA-010", sizeof(pcm->name)); + + snd_pcm_lib_preallocate_isa_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream, + 64*1024, wa->playback.dma < 4 ? 64*1024 : 128*1024); + snd_pcm_lib_preallocate_isa_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, + 64*1024, wa->capture.dma < 4 ? 64*1024 : 128*1024); + + wa->pcm = pcm; + if (rpcm) + *rpcm = pcm; + return 0; +} diff -urN orig/sound/oss/Kconfig linux/sound/oss/Kconfig --- orig/sound/oss/Kconfig Tue May 27 10:06:03 2003 +++ linux/sound/oss/Kconfig Tue May 27 10:15:20 2003 @@ -554,6 +554,61 @@ to external MIDI hardware, and is not required for software playback of MIDI files. +config SOUND_SA1100 + tristate "StrongARM-11x0 Sound Drivers" + depends on SOUND && ARCH_SA1100 + +config SOUND_UDA1341 + tristate "UDA1341 Stereo Codec" + depends on L3 && SOUND + +config SOUND_ASSABET_UDA1341 + tristate "Assabet audio support" + depends on SOUND_UDA1341 && SOUND_SA1100 && SA1100_ASSABET + help + Say Y or M if you have an Intel Assabet evaluation board and want to + use the Philips UDA 1341 audio chip (the one that drives the stereo + audio output) on the SA1100 SSP port. + +config SOUND_H3600_UDA1341 + tristate "Compaq iPAQ audio support" + depends on SOUND_UDA1341 && SOUND_SA1100 && SA1100_H3600 + help + Say Y or M if you have a Compaq iPAQ handheld computer and want to + use its Philips UDA 1341 audio chip. + +config SOUND_PANGOLIN_UDA1341 + tristate "Pangolin audio support" + depends on SOUND_UDA1341 && SOUND_SA1100 && SA1100_PANGOLIN + +config SOUND_SA1111_UDA1341 + tristate "SA1111 audio support" + depends on SOUND_UDA1341 && SOUND_SA1100 && SA1111 + help + Say Y or M if you have an SA11x0 system with a Philips UDA 1341 + connected to the SA11x1. An example of such a system is the Intel + Assabet evaluation board connected to a Neponset expansion board. + +config SOUND_STORK_UDA1341 + tristate "Stork UDA1341 audio support" + depends on SOUND_UDA1341 && SOUND_SA1100 && SA1100_STORK + help + Say Y or M if you have a Stork SA1110 board and want to + use the Philips UDA 1341 audio chip. + +config SOUND_SA1100SSP + tristate "Generic DAC on the SA11x0 SSP port" + depends on SOUND_SA1100 + help + Say Y or M if you have an SA-11x0 system with a DAC on the SSP port. + The LART has an Burr-Brown PCM 1710 digital to analog convertor on + the SSP port, so you want to say Y or M for the LART. It might work + on other SA-1100 platforms, too, but this is not tested. + +config SOUND_STORK_AC97 + tristate "Stork AC97 audio support" + depends on SOUND_SA1100 && SA1100_STORK + config SOUND_OSS tristate "OSS sound modules" depends on SOUND_PRIME!=n && SOUND diff -urN orig/sound/oss/Makefile linux/sound/oss/Makefile --- orig/sound/oss/Makefile Tue Feb 11 16:11:11 2003 +++ linux/sound/oss/Makefile Mon Feb 10 23:54:20 2003 @@ -59,6 +59,14 @@ obj-$(CONFIG_SOUND_MAESTRO) += maestro.o obj-$(CONFIG_SOUND_MAESTRO3) += maestro3.o ac97_codec.o obj-$(CONFIG_SOUND_TRIDENT) += trident.o ac97_codec.o +obj-$(CONFIG_SOUND_SA1100) += sa1100-audio.o +obj-$(CONFIG_SOUND_UDA1341) += uda1341.o +obj-$(CONFIG_SOUND_ASSABET_UDA1341) += assabet-uda1341.o +obj-$(CONFIG_SOUND_PANGOLIN_UDA1341) += pangolin-uda1341.o +obj-$(CONFIG_SOUND_H3600_UDA1341) += h3600-uda1341.o +obj-$(CONFIG_SOUND_STORK_UDA1341) += stork-uda1341.o +obj-$(CONFIG_SOUND_SA1111_UDA1341) += sa1111-uda1341.o +obj-$(CONFIG_SOUND_SA1100SSP) += sa1100ssp.o obj-$(CONFIG_SOUND_EMU10K1) += ac97_codec.o obj-$(CONFIG_SOUND_RME96XX) += rme96xx.o obj-$(CONFIG_SOUND_BT878) += btaudio.o diff -urN orig/sound/oss/assabet-uda1341.c linux/sound/oss/assabet-uda1341.c --- orig/sound/oss/assabet-uda1341.c Thu Jan 1 01:00:00 1970 +++ linux/sound/oss/assabet-uda1341.c Wed Jun 11 19:12:11 2003 @@ -0,0 +1,443 @@ +/* + * Glue audio driver for the SA1110 Assabet board & Philips UDA1341 codec. + * + * Copyright (c) 2000 Nicolas Pitre + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + * This is the machine specific part of the Assabet/UDA1341 support. + * This driver makes use of the UDA1341 and the sa1100-audio modules. + * + * History: + * + * 2000-05-21 Nicolas Pitre Initial release. + * + * 2001-06-03 Nicolas Pitre Made this file a separate module, based on + * the former sa1100-uda1341.c driver. + * + * 2001-07-17 Nicolas Pitre Supports 44100Hz and 22050Hz samplerate now. + * + * 2001-08-03 Russell King Fix left/right channel swap. + * Attempt to reduce power consumption when idle. + * + * 2001-09-23 Russell King Remove old L3 bus driver. + * + * Please note that fiddling too much with MDREFR results in oopses, so we don't + * touch MDREFR unnecessarily, which means we don't touch it on close. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "sa1100-audio.h" + +/* + * Define this to fix the power drain on early Assabets + */ +#define FIX_POWER_DRAIN + +/* + * Debugging? + */ +#undef DEBUG + + +#ifdef DEBUG +#define DPRINTK( x... ) printk( ##x ) +#else +#define DPRINTK( x... ) +#endif + + +#define AUDIO_RATE_DEFAULT 44100 + +/* + * Mixer (UDA1341) interface + */ + +static struct l3_client uda1341; + +static int +mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) +{ + /* + * We only accept mixer (type 'M') ioctls. + */ + if (_IOC_TYPE(cmd) != 'M') + return -EINVAL; + + return l3_command(&uda1341, cmd, (void *)arg); +} + +static struct file_operations assabet_mixer_fops = { + .ioctl = mixer_ioctl, + .owner = THIS_MODULE +}; + + +/* + * Audio interface + */ +static long audio_samplerate = AUDIO_RATE_DEFAULT; + +/* + * FIXME: what about SFRM going high when SSP is disabled? + */ +static void assabet_set_samplerate(long val) +{ + struct uda1341_cfg cfg; + u_int clk_ref, clk_div; + + /* We don't want to mess with clocks when frames are in flight */ + Ser4SSCR0 &= ~SSCR0_SSE; + /* wait for any frame to complete */ + udelay(125); + + /* + * Our clock source is derived from the CPLD on which we don't have + * much control unfortunately. This was intended for a fixed 48000 Hz + * samplerate assuming a core clock of 221.2 MHz. The CPLD appears + * to divide the memory clock so there is a ratio of 4608 between + * the core clock and the resulting samplerate (obtained by + * measurements, the CPLD equations should confirm that). + * + * Still we can play with the SA1110's clock divisor for the SSP port + * to get half the samplerate as well. + * + * Apparently the clock sent to the SA1110 for the SSP port is further + * more divided from the clock sent to the UDA1341 (some people tried + * to be too clever in their design, or simply failed to read the + * SA1110 manual). If it was the same clock we would have been able + * to support a third samplerate with the UDA1341's 384FS mode. + * + * At least it would have been a minimum acceptable solution to be + * able to set the CPLD divisor by software. The iPAQ design is + * certainly a better example to follow for a new design. + */ + clk_ref = cpufreq_get(0) * 1000 / 4608; + if (val > clk_ref*4/7) { + audio_samplerate = clk_ref; + cfg.fs = 256; + clk_div = SSCR0_SerClkDiv(2); + } else { + audio_samplerate = clk_ref/2; + cfg.fs = 512; + clk_div = SSCR0_SerClkDiv(4); + } + + cfg.format = FMT_LSB16; + l3_command(&uda1341, L3_UDA1341_CONFIGURE, &cfg); + + Ser4SSCR0 = (Ser4SSCR0 & ~0xff00) + clk_div + SSCR0_SSE; +} + +/* + * Initialise the Assabet audio driver. + * + * Note that we have to be careful with the order that we do things here; + * there is a D-type flip flop which is clocked from the SFRM line which + * indicates whether the same is for the left or right channel to the + * UDA1341. + * + * When you disable the SSP (by clearing SSCR0_SSE) it appears that the + * SFRM signal can float high. When you re-enable the SSP, you clock the + * flip flop once, and end up swapping the left and right channels. + * + * The ASSABET_BCR_CODEC_RST line will force this flip flop into a known + * state, but this line resets other devices as well! + * + * In addition to the above, it appears that powering down the UDA1341 on + * early Assabets leaves the UDA_WS actively driving a logic '1' into the + * chip, wasting power! (you can tell this by D11 being half-on). We + * attempt to correct this by kicking the flip flop on init/open/close. + * We should probably do this on PM resume as well. + * + * (Note the ordering of ASSABET_BCR_AUDIO_ON, SFRM and ASSABET_BCR_CODEC_RST + * is important). + */ +static void assabet_audio_init(void *dummy) +{ + unsigned long flags; + unsigned int mdrefr; + + local_irq_save(flags); + + /* + * Enable the power for the UDA1341 before driving any signals. + * We leave the audio amp (LM4880) disabled for now. + */ + ASSABET_BCR_set(ASSABET_BCR_AUDIO_ON); + +#ifdef FIX_POWER_DRAIN + GPSR = GPIO_SSP_SFRM; + GPCR = GPIO_SSP_SFRM; +#endif + + ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); + ASSABET_BCR_clear(ASSABET_BCR_STEREO_LB); + + /* + * Setup the SSP uart. + */ + PPAR |= PPAR_SPR; + Ser4SSCR0 = SSCR0_DataSize(16) + SSCR0_TI + SSCR0_SerClkDiv(2); + Ser4SSCR1 = SSCR1_SClkIactL + SSCR1_SClk1P + SSCR1_ExtClk; + GAFR |= GPIO_SSP_TXD | GPIO_SSP_RXD | GPIO_SSP_SCLK | GPIO_SSP_CLK; + GPDR |= GPIO_SSP_TXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM; + GPDR &= ~(GPIO_SSP_RXD | GPIO_SSP_CLK); + Ser4SSCR0 |= SSCR0_SSE; + + /* + * Only give SFRM to the SSP after it has been enabled. + */ + GAFR |= GPIO_SSP_SFRM; + + /* + * The assabet board uses the SDRAM clock as the source clock for + * audio. This is supplied to the SA11x0 from the CPLD on pin 19. + * At 206MHz we need to run the audio clock (SDRAM bank 2) + * at half speed. This clock will scale with core frequency so + * the audio sample rate will also scale. The CPLD on Assabet + * will need to be programmed to match the core frequency. + */ + mdrefr = MDREFR; + if ((mdrefr & (MDREFR_K2DB2 | MDREFR_K2RUN | MDREFR_EAPD | + MDREFR_KAPD)) != (MDREFR_K2DB2 | MDREFR_K2RUN)) { + mdrefr |= MDREFR_K2DB2 | MDREFR_K2RUN; + mdrefr &= ~(MDREFR_EAPD | MDREFR_KAPD); + MDREFR = mdrefr; + (void) MDREFR; + } + local_irq_restore(flags); + + /* Wait for the UDA1341 to wake up */ + mdelay(1); + + l3_open(&uda1341); + + assabet_set_samplerate(audio_samplerate); + + /* Enable the audio power */ + ASSABET_BCR_clear(ASSABET_BCR_QMUTE | ASSABET_BCR_SPK_OFF); +} + +/* + * Shutdown the Assabet audio driver. + * + * We have to be careful about the SFRM line here for the same reasons + * described in the initialisation comments above. This basically means + * that we must hand the SSP pins back to the GPIO module before disabling + * the SSP. + * + * In addition, to reduce power drain, we toggle the SFRM line once so + * that the UDA_WS line is at logic 0. + * + * We can't clear ASSABET_BCR_CODEC_RST without knowing if the UCB1300 or + * ADV7171 driver is still active. If it is, then we still need to play + * games, so we might as well leave ASSABET_BCR_CODEC_RST set. + */ +static void assabet_audio_shutdown(void *dummy) +{ + ASSABET_BCR_set(ASSABET_BCR_STEREO_LB | ASSABET_BCR_QMUTE | + ASSABET_BCR_SPK_OFF); + + l3_close(&uda1341); + + GAFR &= ~(GPIO_SSP_TXD | GPIO_SSP_RXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM); + Ser4SSCR0 = 0; + +#ifdef FIX_POWER_DRAIN + GPSR = GPIO_SSP_SFRM; + GPCR = GPIO_SSP_SFRM; +#endif + + /* disable the audio power */ + ASSABET_BCR_clear(ASSABET_BCR_AUDIO_ON); +} + +static int assabet_audio_ioctl( struct inode *inode, struct file *file, + uint cmd, ulong arg) +{ + long val; + int ret = 0; + + /* + * These are platform dependent ioctls which are not handled by the + * generic sa1100-audio module. + */ + switch (cmd) { + case SNDCTL_DSP_STEREO: + ret = get_user(val, (int *) arg); + if (ret) + return ret; + /* the UDA1341 is stereo only */ + ret = (val == 0) ? -EINVAL : 1; + return put_user(ret, (int *) arg); + + case SNDCTL_DSP_CHANNELS: + case SOUND_PCM_READ_CHANNELS: + /* the UDA1341 is stereo only */ + return put_user(2, (long *) arg); + + case SNDCTL_DSP_SPEED: + ret = get_user(val, (long *) arg); + if (ret) break; + assabet_set_samplerate(val); + /* fall through */ + + case SOUND_PCM_READ_RATE: + return put_user(audio_samplerate, (long *) arg); + + case SNDCTL_DSP_SETFMT: + case SNDCTL_DSP_GETFMTS: + /* we can do signed 16-bit only */ + return put_user(AFMT_S16_LE, (long *) arg); + + default: + /* Maybe this is meant for the mixer (As per OSS Docs) */ + return mixer_ioctl(inode, file, cmd, arg); + } + + return ret; +} + +static struct sys_device assabet_audio_device; + +static audio_stream_t output_stream = { + .id = "UDA1341 out", + .dev = &assabet_audio_device.dev, + .dma_dev = DMA_Ser4SSPWr, +}; + +static audio_stream_t input_stream = { + .id = "UDA1341 in", + .dev = &assabet_audio_device.dev, + .dma_dev = DMA_Ser4SSPRd, +}; + +static audio_state_t audio_state = { + .output_stream = &output_stream, + .input_stream = &input_stream, + .need_tx_for_rx = 1, + .hw_init = assabet_audio_init, + .hw_shutdown = assabet_audio_shutdown, + .client_ioctl = assabet_audio_ioctl, + .sem = __MUTEX_INITIALIZER(audio_state.sem), +}; + +static int assabet_audio_open(struct inode *inode, struct file *file) +{ + return sa1100_audio_attach(inode, file, &audio_state); +} + +/* + * Missing fields of this structure will be patched with the call + * to sa1100_audio_attach(). + */ +static struct file_operations assabet_audio_fops = { + open: assabet_audio_open, + owner: THIS_MODULE +}; + + +static int audio_dev_id, mixer_dev_id; + +#ifdef CONFIG_PM +static int assabet_audio_suspend(struct device *_dev, u32 state, u32 level) +{ + return sa1100_audio_suspend(&audio_state, state, level); +} + +static int assabet_audio_resume(struct device *_dev, u32 level) +{ + return sa1100_audio_resume(&audio_state, level); +} +#else +#define assabet_audio_suspend NULL +#define assabet_audio_resume NULL +#endif + +static int assabet_audio_probe(struct device *_dev) +{ + int ret; + + ret = l3_attach_client(&uda1341, "l3-bit-sa1100-gpio", "uda1341"); + if (ret) + return ret; + + /* register devices */ + audio_dev_id = register_sound_dsp(&assabet_audio_fops, -1); + mixer_dev_id = register_sound_mixer(&assabet_mixer_fops, -1); + +#ifdef FIX_POWER_DRAIN + { + unsigned long flags; + local_irq_save(flags); + ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); + GPSR = GPIO_SSP_SFRM; + GPDR |= GPIO_SSP_SFRM; + GPCR = GPIO_SSP_SFRM; + local_irq_restore(flags); + } +#endif + + printk(KERN_INFO "Sound: Assabet UDA1341: dsp id %d mixer id %d\n", + audio_dev_id, mixer_dev_id); + return 0; +} + +static int assabet_audio_remove(struct device *_dev) +{ + unregister_sound_dsp(audio_dev_id); + unregister_sound_mixer(mixer_dev_id); + l3_detach_client(&uda1341); + return 0; +} + +static struct device_driver assabet_audio_driver = { + .name = "sa11x0-ssp", + .bus = &platform_bus_type, + .probe = assabet_audio_probe, + .remove = assabet_audio_remove, + .suspend = assabet_audio_suspend, + .resume = assabet_audio_resume, +}; + +static int __init assabet_uda1341_init(void) +{ + int ret = -ENODEV; + + if (machine_is_assabet()) + ret = driver_register(&assabet_audio_driver); + + return ret; +} + +static void __exit assabet_uda1341_exit(void) +{ + driver_unregister(&assabet_audio_driver); +} + +module_init(assabet_uda1341_init); +module_exit(assabet_uda1341_exit); + +MODULE_AUTHOR("Nicolas Pitre"); +MODULE_DESCRIPTION("Glue audio driver for the SA1110 Assabet board & Philips UDA1341 codec."); diff -urN orig/sound/oss/h3600-uda1341.c linux/sound/oss/h3600-uda1341.c --- orig/sound/oss/h3600-uda1341.c Thu Jan 1 01:00:00 1970 +++ linux/sound/oss/h3600-uda1341.c Wed Jun 11 19:15:50 2003 @@ -0,0 +1,390 @@ +/* + * Glue audio driver for the Compaq iPAQ H3600 & Philips UDA1341 codec. + * + * Copyright (c) 2000 Nicolas Pitre + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + * This is the machine specific part of the Compaq iPAQ (aka Bitsy) support. + * This driver makes use of the UDA1341 and the sa1100-audio modules. + * + * History: + * + * 2000-05-21 Nicolas Pitre Initial UDA1341 driver release. + * + * 2000-07-?? George France Bitsy support. + * + * 2000-12-13 Deborah Wallach Fixed power handling for iPAQ/h3600 + * + * 2001-06-03 Nicolas Pitre Made this file a separate module, based on + * the former sa1100-uda1341.c driver. + * + * 2001-07-13 Nicolas Pitre Fixes for all supported samplerates. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "sa1100-audio.h" + + +#undef DEBUG +#ifdef DEBUG +#define DPRINTK( x... ) printk( ##x ) +#else +#define DPRINTK( x... ) +#endif + + +#define AUDIO_NAME "Bitsy_UDA1341" + +#define AUDIO_RATE_DEFAULT 44100 + + +static struct l3_client uda1341; + +static int +mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) +{ + /* + * We only accept mixer (type 'M') ioctls. + */ + if (_IOC_TYPE(cmd) != 'M') + return -EINVAL; + + return l3_command(&uda1341, cmd, (void *)arg); +} + +static struct file_operations h3600_mixer_fops = { + ioctl: mixer_ioctl, + owner: THIS_MODULE +}; + + +/* + * Audio interface + */ + +static long audio_samplerate = AUDIO_RATE_DEFAULT; + +/* + * Stop-gap solution until rest of hh.org HAL stuff is merged. + */ +#define GPIO_H3600_CLK_SET0 GPIO_GPIO (12) +#define GPIO_H3600_CLK_SET1 GPIO_GPIO (13) +static void h3600_set_audio_clock(long val) +{ + switch (val) { + case 24000: case 32000: case 48000: /* 00: 12.288 MHz */ + GPCR = GPIO_H3600_CLK_SET0 | GPIO_H3600_CLK_SET1; + break; + + case 22050: case 29400: case 44100: /* 01: 11.2896 MHz */ + GPSR = GPIO_H3600_CLK_SET0; + GPCR = GPIO_H3600_CLK_SET1; + break; + + case 8000: case 10666: case 16000: /* 10: 4.096 MHz */ + GPCR = GPIO_H3600_CLK_SET0; + GPSR = GPIO_H3600_CLK_SET1; + break; + + case 10985: case 14647: case 21970: /* 11: 5.6245 MHz */ + GPSR = GPIO_H3600_CLK_SET0 | GPIO_H3600_CLK_SET1; + break; + } +} + +static void h3600_set_samplerate(long val) +{ + struct uda1341_cfg cfg; + int clk_div = 0; + + /* We don't want to mess with clocks when frames are in flight */ + Ser4SSCR0 &= ~SSCR0_SSE; + /* wait for any frame to complete */ + udelay(125); + + /* + * We have the following clock sources: + * 4.096 MHz, 5.6245 MHz, 11.2896 MHz, 12.288 MHz + * Those can be divided either by 256, 384 or 512. + * This makes up 12 combinations for the following samplerates... + */ + if (val >= 48000) + val = 48000; + else if (val >= 44100) + val = 44100; + else if (val >= 32000) + val = 32000; + else if (val >= 29400) + val = 29400; + else if (val >= 24000) + val = 24000; + else if (val >= 22050) + val = 22050; + else if (val >= 21970) + val = 21970; + else if (val >= 16000) + val = 16000; + else if (val >= 14647) + val = 14647; + else if (val >= 10985) + val = 10985; + else if (val >= 10666) + val = 10666; + else + val = 8000; + + /* Set the external clock generator */ + h3600_set_audio_clock(val); + + /* Select the clock divisor */ + switch (val) { + case 8000: + case 10985: + case 22050: + case 24000: + cfg.fs = 512; + clk_div = SSCR0_SerClkDiv(16); + break; + case 16000: + case 21970: + case 44100: + case 48000: + cfg.fs = 256; + clk_div = SSCR0_SerClkDiv(8); + break; + case 10666: + case 14647: + case 29400: + case 32000: + cfg.fs = 384; + clk_div = SSCR0_SerClkDiv(12); + break; + } + + cfg.format = FMT_LSB16; + l3_command(&uda1341, L3_UDA1341_CONFIGURE, &cfg); + Ser4SSCR0 = (Ser4SSCR0 & ~0xff00) + clk_div + SSCR0_SSE; + audio_samplerate = val; +} + +static void h3600_audio_init(void *dummy) +{ + unsigned long flags; + + /* Setup the uarts */ + local_irq_save(flags); + GAFR |= (GPIO_SSP_CLK); + GPDR &= ~(GPIO_SSP_CLK); + Ser4SSCR0 = 0; + Ser4SSCR0 = SSCR0_DataSize(16) + SSCR0_TI + SSCR0_SerClkDiv(8); + Ser4SSCR1 = SSCR1_SClkIactL + SSCR1_SClk1P + SSCR1_ExtClk; + Ser4SSCR0 |= SSCR0_SSE; + + /* Enable the audio power */ + + clr_h3600_egpio(IPAQ_EGPIO_CODEC_NRESET); + set_h3600_egpio(IPAQ_EGPIO_AUDIO_ON); + set_h3600_egpio(IPAQ_EGPIO_QMUTE); + local_irq_restore(flags); + + /* external clock configuration */ + h3600_set_samplerate(audio_samplerate); + + /* Wait for the UDA1341 to wake up */ + set_h3600_egpio(IPAQ_EGPIO_CODEC_NRESET); + mdelay(1); + + /* make the left and right channels unswapped (flip the WS latch ) */ + Ser4SSDR = 0; + + /* Initialize the UDA1341 internal state */ + l3_open(&uda1341); + + clr_h3600_egpio(IPAQ_EGPIO_QMUTE); +} + +static void h3600_audio_shutdown(void *dummy) +{ + /* disable the audio power and all signals leading to the audio chip */ + l3_close(&uda1341); + Ser4SSCR0 = 0; + clr_h3600_egpio(IPAQ_EGPIO_CODEC_NRESET); + clr_h3600_egpio(IPAQ_EGPIO_AUDIO_ON); + clr_h3600_egpio(IPAQ_EGPIO_QMUTE); +} + +static int h3600_audio_ioctl(struct inode *inode, struct file *file, + uint cmd, ulong arg) +{ + long val; + int ret = 0; + + /* + * These are platform dependent ioctls which are not handled by the + * generic sa1100-audio module. + */ + switch (cmd) { + case SNDCTL_DSP_STEREO: + ret = get_user(val, (int *) arg); + if (ret) + return ret; + /* the UDA1341 is stereo only */ + ret = (val == 0) ? -EINVAL : 1; + return put_user(ret, (int *) arg); + + case SNDCTL_DSP_CHANNELS: + case SOUND_PCM_READ_CHANNELS: + /* the UDA1341 is stereo only */ + return put_user(2, (long *) arg); + + case SNDCTL_DSP_SPEED: + ret = get_user(val, (long *) arg); + if (ret) break; + h3600_set_samplerate(val); + /* fall through */ + + case SOUND_PCM_READ_RATE: + return put_user(audio_samplerate, (long *) arg); + + case SNDCTL_DSP_SETFMT: + case SNDCTL_DSP_GETFMTS: + /* we can do 16-bit only */ + return put_user(AFMT_S16_LE, (long *) arg); + + default: + /* Maybe this is meant for the mixer (As per OSS Docs) */ + return mixer_ioctl(inode, file, cmd, arg); + } + + return ret; +} + +static audio_stream_t output_stream = { + id: "UDA1341 out", + dma_dev: DMA_Ser4SSPWr, +}; + +static audio_stream_t input_stream = { + id: "UDA1341 in", + dma_dev: DMA_Ser4SSPRd, +}; + +static audio_state_t audio_state = { + output_stream: &output_stream, + input_stream: &input_stream, + need_tx_for_rx: 1, + hw_init: h3600_audio_init, + hw_shutdown: h3600_audio_shutdown, + client_ioctl: h3600_audio_ioctl, + sem: __MUTEX_INITIALIZER(audio_state.sem), +}; + +static int h3600_audio_open(struct inode *inode, struct file *file) +{ + return sa1100_audio_attach(inode, file, &audio_state); +} + +/* + * Missing fields of this structure will be patched with the call + * to sa1100_audio_attach(). + */ +static struct file_operations h3600_audio_fops = { + open: h3600_audio_open, + owner: THIS_MODULE +}; + + +static int audio_dev_id, mixer_dev_id; + +#ifdef CONFIG_PM +static int h3x00_audio_suspend(struct device *_dev, u32 state, u32 level) +{ + return sa1100_audio_suspend(&audio_state, state, level); +} + +static int h3x00_audio_resume(struct device *_dev, u32 level) +{ + return sa1100_audio_resume(&audio_state, level); +} +#else +#define h3x00_audio_suspend NULL +#define h3x00_audio_resume NULL +#endif + +static int h3x00_audio_probe(struct device *_dev) +{ + int ret; + + ret = l3_attach_client(&uda1341, "l3-bit-sa1100-gpio", "uda1341"); + if (ret) + goto out; + + /* register devices */ + audio_dev_id = register_sound_dsp(&h3600_audio_fops, -1); + mixer_dev_id = register_sound_mixer(&h3600_mixer_fops, -1); + + printk( KERN_INFO "iPAQ audio support initialized\n" ); + + out: + return ret; +} + +static int h3x00_audio_remove(struct device *_dev) +{ + unregister_sound_dsp(audio_dev_id); + unregister_sound_mixer(mixer_dev_id); + l3_detach_client(&uda1341); + + return 0; +} + +static struct device_driver h3x00_audio_driver = { + .name = "sa11x0-ssp", + .bus = &platform_bus_type, + .probe = h3x00_audio_probe, + .remove = h3x00_audio_remove, + .suspend = h3x00_audio_suspend, + .resume = h3x00_audio_resume, +}; + +static int __init h3600_uda1341_init(void) +{ + int ret = -ENODEV; + + if (machine_is_h3xxx()) + ret = driver_register(&h3x00_audio_driver); + + return ret; +} + +static void __exit h3600_uda1341_exit(void) +{ + driver_unregister(&h3x00_audio_driver); +} + +module_init(h3600_uda1341_init); +module_exit(h3600_uda1341_exit); + +MODULE_AUTHOR("Nicolas Pitre, George France"); +MODULE_DESCRIPTION("Glue audio driver for the Compaq iPAQ H3600 & Philips UDA1341 codec."); diff -urN orig/sound/oss/pangolin-uda1341.c linux/sound/oss/pangolin-uda1341.c --- orig/sound/oss/pangolin-uda1341.c Thu Jan 1 01:00:00 1970 +++ linux/sound/oss/pangolin-uda1341.c Wed Jun 11 19:17:39 2003 @@ -0,0 +1,360 @@ +/* + * Glue audio driver for the SA1110 Pangolin board & Philips UDA1341 codec. + * + * Copyright (c) 2000 Nicolas Pitre + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + * This is the machine specific part of the Pangolin/UDA1341 support. + * This driver makes use of the UDA1341 and the sa1100-audio modules. + * + * History: + * + * 2000-05-21 Nicolas Pitre Initial release. + * + * 2001-06-03 Nicolas Pitre Made this file a separate module, based on + * the former sa1100-uda1341.c driver. + * + * 2001-07-17 Nicolas Pitre Supports 44100Hz and 22050Hz samplerate now. + * + * 2001-08-06 Richard Fan Pangolin Support + * + * 2001-09-23 Russell King Update inline with Assabet driver + * Remove old L3 bus driver + * + * Note: this should probably be merged with the Assabet audio driver, + * and become the "SDRAM-clock driven" SA1100 audio driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "sa1100-audio.h" + +/* + * Debugging? + */ +#undef DEBUG + + +#ifdef DEBUG +#define DPRINTK( x... ) printk( ##x ) +#else +#define DPRINTK( x... ) +#endif + + +#define AUDIO_RATE_DEFAULT 44100 + +#define QmutePin GPIO_GPIO(4) +#define SpeakerOffPin GPIO_GPIO(5) + +/* + * Mixer (UDA1341) interface + */ + +static struct l3_client uda1341; + +static int +mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) +{ + /* + * We only accept mixer (type 'M') ioctls. + */ + if (_IOC_TYPE(cmd) != 'M') + return -EINVAL; + + return l3_command(&uda1341, cmd, (void *)arg); +} + +static struct file_operations pangolin_mixer_fops = { + ioctl: mixer_ioctl, + owner: THIS_MODULE +}; + + +/* + * Audio interface + */ +static long audio_samplerate = AUDIO_RATE_DEFAULT; + +static void pangolin_set_samplerate(long val) +{ + struct uda1341_cfg cfg; + int clk_div; + + /* We don't want to mess with clocks when frames are in flight */ + Ser4SSCR0 &= ~SSCR0_SSE; + /* wait for any frame to complete */ + udelay(125); + + /* + * Our clock source is derived from the CPLD on which we don't have + * much control unfortunately. This was intended for a fixed 44100Hz + * samplerate assuming a core clock of 206 MHz. Still we can play + * with the SA1110's clock divisor for the SSP port to get a 22050Hz + * samplerate. + * + * Apparently the clock sent to the SA1110 for the SSP port is + * divided from the clock sent to the UDA1341 (some people tried to + * be too clever in their design, or simply failed to read the SA1110 + * manual). If it was the same source we would have been able to + * support a third samplerate. + * + * At least it would have been a minimum acceptable solution to be + * able to set the CPLD divisor by software. The iPAQ design is + * certainly a better example to follow for a new design. + */ + if (val >= 44100) { + audio_samplerate = 44100; + cfg.fs = 256; + clk_div = SSCR0_SerClkDiv(2); + } else { + audio_samplerate = 22050; + cfg.fs = 512; + clk_div = SSCR0_SerClkDiv(4); + } + + cfg.format = FMT_LSB16; + l3_command(&uda1341, L3_UDA1341_CONFIGURE, &cfg); + + Ser4SSCR0 = (Ser4SSCR0 & ~0xff00) + clk_div + SSCR0_SSE; +} + +static void pangolin_audio_init(void *dummy) +{ + unsigned long flags; + unsigned int mdrefr; + + local_irq_save(flags); + + /* + * Setup the SSP uart. + */ + PPAR |= PPAR_SPR; + Ser4SSCR0 = SSCR0_DataSize(16) + SSCR0_TI + SSCR0_SerClkDiv(2); + Ser4SSCR1 = SSCR1_SClkIactL + SSCR1_SClk1P + SSCR1_ExtClk; + GAFR |= GPIO_SSP_TXD | GPIO_SSP_RXD | GPIO_SSP_SCLK | GPIO_SSP_CLK | + GPIO_SSP_SFRM; + GPDR |= GPIO_SSP_TXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM; + GPDR &= ~(GPIO_SSP_RXD | GPIO_SSP_CLK); + Ser4SSCR0 |= SSCR0_SSE; + + GAFR &= ~(SpeakerOffPin | QmutePin); + GPDR |= (SpeakerOffPin | QmutePin); + GPCR = SpeakerOffPin; + + /* + * The assabet board uses the SDRAM clock as the source clock for + * audio. This is supplied to the SA11x0 from the CPLD on pin 19. + * At 206MHz we need to run the audio clock (SDRAM bank 2) + * at half speed. This clock will scale with core frequency so + * the audio sample rate will also scale. The CPLD on Assabet + * will need to be programmed to match the core frequency. + */ + mdrefr = MDREFR; + if ((mdrefr & (MDREFR_K2DB2 | MDREFR_K2RUN | MDREFR_EAPD | + MDREFR_KAPD)) != (MDREFR_K2DB2 | MDREFR_K2RUN)) { + mdrefr |= MDREFR_K2DB2 | MDREFR_K2RUN; + mdrefr &= ~(MDREFR_EAPD | MDREFR_KAPD); + MDREFR = mdrefr; + (void) MDREFR; + } + local_irq_restore(flags); + + /* Wait for the UDA1341 to wake up */ + mdelay(100); + + l3_open(&uda1341); + + pangolin_set_samplerate(audio_samplerate); + + GPCR = QmutePin; +} + +static void pangolin_audio_shutdown(void *dummy) +{ + GPSR = QmutePin; + + l3_close(&uda1341); + + Ser4SSCR0 = 0; + MDREFR &= ~(MDREFR_K2DB2 | MDREFR_K2RUN); +} + +static int pangolin_audio_ioctl( struct inode *inode, struct file *file, + uint cmd, ulong arg) +{ + long val; + int ret = 0; + + /* + * These are platform dependent ioctls which are not handled by the + * generic sa1100-audio module. + */ + switch (cmd) { + case SNDCTL_DSP_STEREO: + ret = get_user(val, (int *) arg); + if (ret) + return ret; + /* the UDA1341 is stereo only */ + ret = (val == 0) ? -EINVAL : 1; + return put_user(ret, (int *) arg); + + case SNDCTL_DSP_CHANNELS: + case SOUND_PCM_READ_CHANNELS: + /* the UDA1341 is stereo only */ + return put_user(2, (long *) arg); + + case SNDCTL_DSP_SPEED: + ret = get_user(val, (long *) arg); + if (ret) break; + pangolin_set_samplerate(val); + /* fall through */ + + case SOUND_PCM_READ_RATE: + return put_user(audio_samplerate, (long *) arg); + + case SNDCTL_DSP_SETFMT: + case SNDCTL_DSP_GETFMTS: + /* we can do signed 16-bit only */ + return put_user(AFMT_S16_LE, (long *) arg); + + default: + /* Maybe this is meant for the mixer (As per OSS Docs) */ + return mixer_ioctl(inode, file, cmd, arg); + } + + return ret; +} + +static audio_stream_t output_stream = { + id: "UDA1341 out", + dma_dev: DMA_Ser4SSPWr, +}; + +static audio_stream_t input_stream = { + id: "UDA1341 in", + dma_dev: DMA_Ser4SSPRd, +}; + +static audio_state_t audio_state = { + output_stream: &output_stream, + input_stream: &input_stream, + need_tx_for_rx: 1, + hw_init: pangolin_audio_init, + hw_shutdown: pangolin_audio_shutdown, + client_ioctl: pangolin_audio_ioctl, + sem: __MUTEX_INITIALIZER(audio_state.sem), +}; + +static int pangolin_audio_open(struct inode *inode, struct file *file) +{ + return sa1100_audio_attach(inode, file, &audio_state); +} + +/* + * Missing fields of this structure will be patched with the call + * to sa1100_audio_attach(). + */ +static struct file_operations pangolin_audio_fops = { + open: pangolin_audio_open, + owner: THIS_MODULE +}; + + +static int audio_dev_id, mixer_dev_id; + +#ifdef CONFIG_PM +static int pangolin_audio_suspend(struct device *_dev, u32 state, u32 level) +{ + return sa1100_audio_suspend(&audio_state, state, level); +} + +static int pangolin_audio_resume(struct device *_dev, u32 level) +{ + return sa1100_audio_resume(&audio_state, level); +} +#else +#define pangolin_audio_suspend NULL +#define pangolin_audio_resume NULL +#endif + +static int pangolin_audio_probe(struct device *_dev) +{ + unsigned long flags; + int ret; + + ret = l3_attach_client(&uda1341, "l3-bit-sa1100-gpio", "uda1341"); + if (ret) + goto out; + + /* register devices */ + audio_dev_id = register_sound_dsp(&pangolin_audio_fops, -1); + mixer_dev_id = register_sound_mixer(&pangolin_mixer_fops, -1); + + local_irq_save(flags); + GAFR &= ~(SpeakerOffPin | QmutePin); + GPDR |= (SpeakerOffPin | QmutePin); + local_irq_restore(flags); + + printk(KERN_INFO "Pangolin UDA1341 audio driver initialized\n"); + + out: + return 0; +} + +static int pangolin_audio_remove(struct device *_dev) +{ + unregister_sound_dsp(audio_dev_id); + unregister_sound_mixer(mixer_dev_id); + l3_detach_client(&uda1341); + + return 0; +} + +static struct device_driver pangolin_audio_driver = { + .name = "sa11x0-ssp", + .bus = &platform_bus_type, + .probe = pangolin_audio_probe, + .remove = pangolin_audio_remove, + .suspend = pangolin_audio_suspend, + .resume = pangolin_audio_resume, +}; + +static int __init pangolin_uda1341_init(void) +{ + int ret = -ENODEV; + + if (machine_is_pangolin()) + ret = driver_register(&pangolin_audio_driver); + + return ret; +} + +static void __exit pangolin_uda1341_exit(void) +{ + sys_device_unregister(&pangolin_audio_device); +} + +module_init(pangolin_uda1341_init); +module_exit(pangolin_uda1341_exit); + +MODULE_AUTHOR("Nicolas Pitre"); +MODULE_DESCRIPTION("Glue audio driver for the SA1110 Pangolin board & Philips UDA1341 codec."); diff -urN orig/sound/oss/sa1100-audio.c linux/sound/oss/sa1100-audio.c --- orig/sound/oss/sa1100-audio.c Thu Jan 1 01:00:00 1970 +++ linux/sound/oss/sa1100-audio.c Tue May 27 20:22:42 2003 @@ -0,0 +1,1141 @@ +/* + * Common audio handling for the SA11x0 processor + * + * Copyright (C) 2000, 2001 Nicolas Pitre + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + * + * This module handles the generic buffering/DMA/mmap audio interface for + * codecs connected to the SA1100 chip. All features depending on specific + * hardware implementations like supported audio formats or samplerates are + * relegated to separate specific modules. + * + * + * History: + * + * 2000-05-21 Nicolas Pitre Initial release. + * + * 2000-06-10 Erik Bunce Add initial poll support. + * + * 2000-08-22 Nicolas Pitre Removed all DMA stuff. Now using the + * generic SA1100 DMA interface. + * + * 2000-11-30 Nicolas Pitre - Validation of opened instances; + * - Power handling at open/release time instead + * of driver load/unload; + * + * 2001-06-03 Nicolas Pitre Made this file a separate module, based on + * the former sa1100-uda1341.c driver. + * + * 2001-07-22 Nicolas Pitre - added mmap() and realtime support + * - corrected many details to better comply + * with the OSS API + * + * 2001-10-19 Nicolas Pitre - brought DMA registration processing + * into this module for better ressource + * management. This also fixes a bug + * with the suspend/resume logic. + * + * 2001-12-20 Nicolas Pitre - moved DMA buffer handling out of the + * generic code (since only this module + * used it anyway) and reimplemented it + * locally for much better efficiency. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "sa1100-audio.h" + + +#undef DEBUG +#ifdef DEBUG +#define DPRINTK( x... ) printk( ##x ) +#else +#define DPRINTK( x... ) +#endif + + +#define AUDIO_NAME "sa1100-audio" +#define AUDIO_NBFRAGS_DEFAULT 8 +#define AUDIO_FRAGSIZE_DEFAULT 8192 + + +#define AUDIO_ACTIVE(state) ((state)->rd_ref || (state)->wr_ref) + +#define SPIN_ADDR (dma_addr_t)FLUSH_BASE_PHYS +#define SPIN_SIZE 2048 + + +/* + * DMA processing + */ + +#ifndef CONFIG_SA1111 + +#define DMA_REQUEST(s, cb) sa1100_request_dma(s->dma_dev, s->id, cb, s, &s->dma_regs) +#define DMA_FREE(s) {sa1100_free_dma(s->dma_regs); s->dma_regs = 0;} +#define DMA_START(s, d, l) sa1100_start_dma(s->dma_regs, d, l) +#define DMA_POS(s) sa1100_get_dma_pos(s->dma_regs) +#define DMA_STOP(s) sa1100_stop_dma(s->dma_regs) +#define DMA_CLEAR(s) sa1100_clear_dma(s->dma_regs) +#define DMA_RESET(s) sa1100_reset_dma(s->dma_regs) + +#else + +#include "sa1111-sac.h" + +static int DMA_REQUEST(audio_stream_t *s, dma_callback_t cb) +{ + if (s->sa1111_dma) { + sa1111_dma_t *dma = (sa1111_dma_t *)s->dma_regs; + dma->callback = cb; + dma->data = s; + return 0; + } else { + return sa1100_request_dma(s->dma_dev, s->id, cb, s, &s->dma_regs); + } +} + +static void DMA_FREE(audio_stream_t *s) +{ + if (s->sa1111_dma) { + sa1111_dma_t *dma = (sa1111_dma_t *)s->dma_regs; + dma->reset(dma); + return; + } else { + sa1100_free_dma(s->dma_regs); + s->dma_regs = NULL; + } +} + +static int DMA_START(audio_stream_t *s, dma_addr_t data, int len) +{ + if (s->sa1111_dma) { + sa1111_dma_t *dma = (sa1111_dma_t *)s->dma_regs; + return dma->start(dma, data, len); + } else { + return sa1100_start_dma(s->dma_regs, data, len); + } +} + +static dma_addr_t DMA_POS(audio_stream_t *s) +{ + if (s->sa1111_dma) { + /* the real thing is not implemented so... */ + return s->buffers[s->dma_tail].dma_addr; + } else { + return sa1100_get_dma_pos(s->dma_regs); + } +} + +static void DMA_STOP(audio_stream_t *s) +{ + if (s->sa1111_dma) { + sa1111_dma_t *dma = (sa1111_dma_t *)s->dma_regs; + dma->reset(dma); + return; + } else { + sa1100_stop_dma(s->dma_regs); + } +} + +static void DMA_CLEAR(audio_stream_t *s) +{ + if (s->sa1111_dma) { + sa1111_dma_t *dma = (sa1111_dma_t *)s->dma_regs; + dma->reset(dma); + return; + } else { + sa1100_clear_dma(s->dma_regs); + } +} + +static void DMA_RESET(audio_stream_t *s) +{ + if (s->sa1111_dma) { + sa1111_dma_t *dma = (sa1111_dma_t *)s->dma_regs; + dma->reset(dma); + return; + } else { + sa1100_reset_dma(s->dma_regs); + } +} + +#endif + + +static u_int audio_get_dma_pos(audio_stream_t *s) +{ + audio_buf_t *b = &s->buffers[s->dma_tail]; + u_int offset; + + if (b->dma_ref) { + offset = DMA_POS(s) - b->dma_addr; + if (offset >= s->fragsize) + offset = s->fragsize - 4; + } else if (s->pending_frags) { + offset = b->offset; + } else { + offset = 0; + } + return offset; +} + + +static void audio_stop_dma(audio_stream_t *s) +{ + u_int pos; + unsigned long flags; + audio_buf_t *b; + + if (s->dma_spinref > 0 || !s->buffers) + return; + + local_irq_save(flags); + s->stopped = 1; + DMA_STOP(s); + pos = audio_get_dma_pos(s); + DMA_CLEAR(s); + if (s->spin_idle) { + DMA_START(s, SPIN_ADDR, SPIN_SIZE); + DMA_START(s, SPIN_ADDR, SPIN_SIZE); + s->dma_spinref = 2; + } else + s->dma_spinref = 0; + local_irq_restore(flags); + + /* back up pointers to be ready to restart from the same spot */ + while (s->dma_head != s->dma_tail) { + b = &s->buffers[s->dma_head]; + if (b->dma_ref) { + b->dma_ref = 0; + b->offset = 0; + } + s->pending_frags++; + if (s->dma_head == 0) + s->dma_head = s->nbfrags; + s->dma_head--; + } + b = &s->buffers[s->dma_head]; + if (b->dma_ref) { + b->offset = pos; + b->dma_ref = 0; + } +} + + +static void audio_reset(audio_stream_t *s) +{ + if (s->buffers) { + audio_stop_dma(s); + s->buffers[s->dma_head].offset = 0; + s->buffers[s->usr_head].offset = 0; + s->usr_head = s->dma_head; + s->pending_frags = 0; + sema_init(&s->sem, s->nbfrags); + } + s->active = 0; + s->stopped = 0; +} + + +static void audio_process_dma(audio_stream_t *s) +{ + int ret; + + if (s->stopped) + goto spin; + + if (s->dma_spinref > 0 && s->pending_frags) { + s->dma_spinref = 0; + DMA_CLEAR(s); + } + + while (s->pending_frags) { + audio_buf_t *b = &s->buffers[s->dma_head]; + u_int dma_size = s->fragsize - b->offset; + if (dma_size > MAX_DMA_SIZE) + dma_size = CUT_DMA_SIZE; + ret = DMA_START(s, b->dma_addr + b->offset, dma_size); + if (ret) + return; + b->dma_ref++; + b->offset += dma_size; + if (b->offset >= s->fragsize) { + s->pending_frags--; + if (++s->dma_head >= s->nbfrags) + s->dma_head = 0; + } + } + +spin: + if (s->spin_idle) { + int spincnt = 0; + while (DMA_START(s, SPIN_ADDR, SPIN_SIZE) == 0) + spincnt++; + /* + * Note: if there is still a data buffer being + * processed then the ref count is negative. This + * allows for the DMA termination to be accounted in + * the proper order. Of course dma_spinref can't be + * greater than 0 if dma_ref is not 0 since we kill + * the spinning above as soon as there is real data to process. + */ + if (s->buffers && s->buffers[s->dma_tail].dma_ref) + spincnt = -spincnt; + s->dma_spinref += spincnt; + } +} + + +static void audio_dma_callback(void *data) +{ + audio_stream_t *s = data; + audio_buf_t *b = &s->buffers[s->dma_tail]; + + if (s->dma_spinref > 0) { + s->dma_spinref--; + } else if (!s->buffers) { + printk(KERN_CRIT "sa1100_audio: received DMA IRQ for non existent buffers!\n"); + return; + } else if (b->dma_ref && --b->dma_ref == 0 && b->offset >= s->fragsize) { + /* This fragment is done */ + b->offset = 0; + s->bytecount += s->fragsize; + s->fragcount++; + s->dma_spinref = -s->dma_spinref; + if (++s->dma_tail >= s->nbfrags) + s->dma_tail = 0; + if (!s->mapped) + up(&s->sem); + else + s->pending_frags++; + wake_up(&s->wq); + } + + audio_process_dma(s); +} + + +/* + * Buffer creation/destruction + */ + +static void audio_discard_buf(audio_stream_t * s) +{ + DPRINTK("audio_discard_buf\n"); + + /* ensure DMA isn't using those buffers */ + audio_reset(s); + + if (s->buffers) { + int frag; + for (frag = 0; frag < s->nbfrags; frag++) { + if (!s->buffers[frag].master) + continue; + dma_free_coherent(s->dev, + s->buffers[frag].master, + s->buffers[frag].data, + s->buffers[frag].dma_addr); + } + kfree(s->buffers); + s->buffers = NULL; + } +} + + +static int audio_setup_buf(audio_stream_t * s) +{ + int frag; + int dmasize = 0; + char *dmabuf = NULL; + dma_addr_t dmaphys = 0; + + if (s->buffers) + return -EBUSY; + + s->buffers = kmalloc(sizeof(audio_buf_t) * s->nbfrags, GFP_KERNEL); + if (!s->buffers) + goto err; + memset(s->buffers, 0, sizeof(audio_buf_t) * s->nbfrags); + + for (frag = 0; frag < s->nbfrags; frag++) { + audio_buf_t *b = &s->buffers[frag]; + + /* + * Let's allocate non-cached memory for DMA buffers. + * We try to allocate all memory at once. + * If this fails (a common reason is memory fragmentation), + * then we allocate more smaller buffers. + */ + if (!dmasize) { + dmasize = (s->nbfrags - frag) * s->fragsize; + do { + dmabuf = dma_alloc_coherent(s->dev, dmasize, &dmaphys, GFP_KERNEL); + if (!dmabuf) + dmasize -= s->fragsize; + } while (!dmabuf && dmasize); + if (!dmabuf) + goto err; + b->master = dmasize; + memzero(dmabuf, dmasize); + } + + b->data = dmabuf; + b->dma_addr = dmaphys; + DPRINTK("buf %d: start %p dma %#08x\n", frag, b->data, + b->dma_addr); + + dmabuf += s->fragsize; + dmaphys += s->fragsize; + dmasize -= s->fragsize; + } + + s->usr_head = s->dma_head = s->dma_tail = 0; + s->bytecount = 0; + s->fragcount = 0; + sema_init(&s->sem, s->nbfrags); + + return 0; + +err: + printk(AUDIO_NAME ": unable to allocate audio memory\n "); + audio_discard_buf(s); + return -ENOMEM; +} + + +/* + * Driver interface functions + */ + +static int audio_write(struct file *file, const char *buffer, + size_t count, loff_t * ppos) +{ + const char *buffer0 = buffer; + audio_state_t *state = file->private_data; + audio_stream_t *s = state->output_stream; + int chunksize, ret = 0; + unsigned long flags; + + DPRINTK("audio_write: count=%d\n", count); + + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->mapped) + return -ENXIO; + if (!s->buffers && audio_setup_buf(s)) + return -ENOMEM; + + while (count > 0) { + audio_buf_t *b = &s->buffers[s->usr_head]; + + /* Wait for a buffer to become free */ + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + if (down_trylock(&s->sem)) + break; + } else { + ret = -ERESTARTSYS; + if (down_interruptible(&s->sem)) + break; + } + + /* Feed the current buffer */ + chunksize = s->fragsize - b->offset; + if (chunksize > count) + chunksize = count; + DPRINTK("write %d to %d\n", chunksize, s->usr_head); + if (copy_from_user(b->data + b->offset, buffer, chunksize)) { + up(&s->sem); + return -EFAULT; + } + buffer += chunksize; + count -= chunksize; + b->offset += chunksize; + if (b->offset < s->fragsize) { + up(&s->sem); + break; + } + + /* Update pointers and send current fragment to DMA */ + b->offset = 0; + if (++s->usr_head >= s->nbfrags) + s->usr_head = 0; + local_irq_save(flags); + s->pending_frags++; + s->active = 1; + audio_process_dma(s); + local_irq_restore(flags); + } + + if ((buffer - buffer0)) + ret = buffer - buffer0; + DPRINTK("audio_write: return=%d\n", ret); + return ret; +} + + +static void audio_prime_rx(audio_state_t *state) +{ + audio_stream_t *is = state->input_stream; + unsigned long flags; + + local_irq_save(flags); + if (state->need_tx_for_rx) { + /* + * With some codecs like the Philips UDA1341 we must ensure + * there is an output stream at any time while recording since + * this is how the UDA1341 gets its clock from the SA1100. + * So while there is no playback data to send, the output DMA + * will spin with all zeroes. We use the cache flush special + * area for that. + */ + state->output_stream->spin_idle = 1; + audio_process_dma(state->output_stream); + } + is->pending_frags = is->nbfrags; + sema_init(&is->sem, 0); + is->active = 1; + audio_process_dma(is); + local_irq_restore(flags); +} + + +static int audio_read(struct file *file, char *buffer, + size_t count, loff_t * ppos) +{ + char *buffer0 = buffer; + audio_state_t *state = file->private_data; + audio_stream_t *s = state->input_stream; + int chunksize, ret = 0; + unsigned long flags; + + DPRINTK("audio_read: count=%d\n", count); + + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->mapped) + return -ENXIO; + + if (!s->active) { + if (!s->buffers && audio_setup_buf(s)) + return -ENOMEM; + audio_prime_rx(state); + } + + while (count > 0) { + audio_buf_t *b = &s->buffers[s->usr_head]; + + /* Wait for a buffer to become full */ + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + if (down_trylock(&s->sem)) + break; + } else { + ret = -ERESTARTSYS; + if (down_interruptible(&s->sem)) + break; + } + + /* Grab data from the current buffer */ + chunksize = s->fragsize - b->offset; + if (chunksize > count) + chunksize = count; + DPRINTK("read %d from %d\n", chunksize, s->usr_head); + if (copy_to_user(buffer, b->data+ b->offset, chunksize)) { + up(&s->sem); + return -EFAULT; + } + buffer += chunksize; + count -= chunksize; + b->offset += chunksize; + if (b->offset < s->fragsize) { + up(&s->sem); + break; + } + + /* Update pointers and return current fragment to DMA */ + b->offset = 0; + if (++s->usr_head >= s->nbfrags) + s->usr_head = 0; + local_irq_save(flags); + s->pending_frags++; + audio_process_dma(s); + local_irq_restore(flags); + } + + if ((buffer - buffer0)) + ret = buffer - buffer0; + DPRINTK("audio_read: return=%d\n", ret); + return ret; +} + +static int audio_sync(struct file *file) +{ + audio_state_t *state = file->private_data; + audio_stream_t *s = state->output_stream; + audio_buf_t *b; + u_int shiftval = 0; + unsigned long flags; + DECLARE_WAITQUEUE(wait, current); + + DPRINTK("audio_sync\n"); + + if (!(file->f_mode & FMODE_WRITE) || !s->buffers || s->mapped) + return 0; + + /* + * Send current buffer if it contains data. Be sure to send + * a full sample count. + */ + b = &s->buffers[s->usr_head]; + if (b->offset &= ~3) { + down(&s->sem); + /* + * HACK ALERT ! + * To avoid increased complexity in the rest of the code + * where full fragment sizes are assumed, we cheat a little + * with the start pointer here and don't forget to restore + * it later. + */ + shiftval = s->fragsize - b->offset; + b->offset = shiftval; + b->dma_addr -= shiftval; + s->bytecount -= shiftval; + if (++s->usr_head >= s->nbfrags) + s->usr_head = 0; + local_irq_save(flags); + s->pending_frags++; + audio_process_dma(s); + local_irq_restore(flags); + } + + /* Let's wait for all buffers to complete */ + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&s->wq, &wait); + while (s->pending_frags && + s->dma_tail != s->usr_head && + !signal_pending(current)) { + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&s->wq, &wait); + + /* undo the pointer hack above */ + if (shiftval) { + local_irq_save(flags); + b->dma_addr += shiftval; + /* ensure sane DMA code behavior if not yet processed */ + if (b->offset != 0) + b->offset = s->fragsize; + local_irq_restore(flags); + } + + return 0; +} + +static int audio_mmap(struct file *file, struct vm_area_struct *vma) +{ + audio_state_t *state = file->private_data; + audio_stream_t *s; + unsigned long size, vma_addr; + int i, ret; + + if (vma->vm_pgoff != 0) + return -EINVAL; + + if (vma->vm_flags & VM_WRITE) { + if (!state->wr_ref) + return -EINVAL;; + s = state->output_stream; + } else if (vma->vm_flags & VM_READ) { + if (!state->rd_ref) + return -EINVAL; + s = state->input_stream; + } else return -EINVAL; + + if (s->mapped) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size != s->fragsize * s->nbfrags) + return -EINVAL; + if (!s->buffers && audio_setup_buf(s)) + return -ENOMEM; + vma_addr = vma->vm_start; + for (i = 0; i < s->nbfrags; i++) { + audio_buf_t *buf = &s->buffers[i]; + if (!buf->master) + continue; + ret = remap_page_range(vma, vma_addr, buf->dma_addr, buf->master, vma->vm_page_prot); + if (ret) + return ret; + vma_addr += buf->master; + } + s->mapped = 1; + + return 0; +} + +static unsigned int audio_poll(struct file *file, + struct poll_table_struct *wait) +{ + audio_state_t *state = file->private_data; + audio_stream_t *is = state->input_stream; + audio_stream_t *os = state->output_stream; + unsigned int mask = 0; + + DPRINTK("audio_poll(): mode=%s%s\n", + (file->f_mode & FMODE_READ) ? "r" : "", + (file->f_mode & FMODE_WRITE) ? "w" : ""); + + if (file->f_mode & FMODE_READ) { + /* Start audio input if not already active */ + if (!is->active) { + if (!is->buffers && audio_setup_buf(is)) + return -ENOMEM; + audio_prime_rx(state); + } + poll_wait(file, &is->wq, wait); + } + + if (file->f_mode & FMODE_WRITE) { + if (!os->buffers && audio_setup_buf(os)) + return -ENOMEM; + poll_wait(file, &os->wq, wait); + } + + if (file->f_mode & FMODE_READ) + if (( is->mapped && is->bytecount > 0) || + (!is->mapped && sema_count(&is->sem) > 0)) + mask |= POLLIN | POLLRDNORM; + + if (file->f_mode & FMODE_WRITE) + if (( os->mapped && os->bytecount > 0) || + (!os->mapped && sema_count(&os->sem) > 0)) + mask |= POLLOUT | POLLWRNORM; + + DPRINTK("audio_poll() returned mask of %s%s\n", + (mask & POLLIN) ? "r" : "", + (mask & POLLOUT) ? "w" : ""); + + return mask; +} + +static loff_t audio_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +static int audio_set_fragments(audio_stream_t *s, int val) +{ + if (s->active) + return -EBUSY; + if (s->buffers) + audio_discard_buf(s); + s->nbfrags = (val >> 16) & 0x7FFF; + val &= 0xffff; + if (val < 4) + val = 4; + if (s->sa1111_dma && val < 11) + val = 11; + if (val > 15) + val = 15; + s->fragsize = 1 << val; + if (s->nbfrags < 2) + s->nbfrags = 2; + if (s->nbfrags * s->fragsize > 128 * 1024) + s->nbfrags = 128 * 1024 / s->fragsize; + if (audio_setup_buf(s)) + return -ENOMEM; + return val|(s->nbfrags << 16); +} + +static int audio_ioctl(struct inode *inode, struct file *file, + uint cmd, ulong arg) +{ + audio_state_t *state = file->private_data; + audio_stream_t *os = state->output_stream; + audio_stream_t *is = state->input_stream; + long val; + + /* dispatch based on command */ + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) + return put_user(os->fragsize, (int *)arg); + else + return put_user(is->fragsize, (int *)arg); + + case SNDCTL_DSP_GETCAPS: + val = DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP; + if (is && os) + val |= DSP_CAP_DUPLEX; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (long *) arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + int ret = audio_set_fragments(is, val); + if (ret < 0) + return ret; + ret = put_user(ret, (int *)arg); + if (ret) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + int ret = audio_set_fragments(os, val); + if (ret < 0) + return ret; + ret = put_user(ret, (int *)arg); + if (ret) + return ret; + } + return 0; + + case SNDCTL_DSP_SYNC: + return audio_sync(file); + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && is->active && !is->stopped) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && os->active && !os->stopped) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + unsigned long flags; + if (!is->active) { + if (!is->buffers && audio_setup_buf(is)) + return -ENOMEM; + audio_prime_rx(state); + } + local_irq_save(flags); + is->stopped = 0; + audio_process_dma(is); + local_irq_restore(flags); + } else { + audio_stop_dma(is); + } + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + unsigned long flags; + if (!os->buffers && audio_setup_buf(os)) + return -ENOMEM; + local_irq_save(flags); + if (os->mapped && !os->pending_frags) { + os->pending_frags = os->nbfrags; + sema_init(&os->sem, 0); + os->active = 1; + } + os->stopped = 0; + audio_process_dma(os); + local_irq_restore(flags); + } else { + audio_stop_dma(os); + } + } + return 0; + + case SNDCTL_DSP_GETOPTR: + case SNDCTL_DSP_GETIPTR: + { + count_info inf = { 0, }; + audio_stream_t *s = (cmd == SNDCTL_DSP_GETOPTR) ? os : is; + int bytecount, offset; + unsigned long flags; + + if ((s == is && !(file->f_mode & FMODE_READ)) || + (s == os && !(file->f_mode & FMODE_WRITE))) + return -EINVAL; + if (s->active) { + local_irq_save(flags); + offset = audio_get_dma_pos(s); + inf.ptr = s->dma_tail * s->fragsize + offset; + bytecount = s->bytecount + offset; + s->bytecount = -offset; + inf.blocks = s->fragcount; + s->fragcount = 0; + local_irq_restore(flags); + if (bytecount < 0) + bytecount = 0; + inf.bytes = bytecount; + } + return copy_to_user((void *)arg, &inf, sizeof(inf)); + } + + case SNDCTL_DSP_GETOSPACE: + case SNDCTL_DSP_GETISPACE: + { + audio_buf_info inf = { 0, }; + audio_stream_t *s = (cmd == SNDCTL_DSP_GETOSPACE) ? os : is; + + if ((s == is && !(file->f_mode & FMODE_READ)) || + (s == os && !(file->f_mode & FMODE_WRITE))) + return -EINVAL; + if (!s->buffers && audio_setup_buf(s)) + return -ENOMEM; + inf.bytes = sema_count(&s->sem) * s->fragsize; + inf.bytes -= s->buffers[s->usr_head].offset; + inf.fragments = inf.bytes / s->fragsize; + inf.fragsize = s->fragsize; + inf.fragstotal = s->nbfrags; + return copy_to_user((void *)arg, &inf, sizeof(inf)); + } + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_READ) { + audio_reset(is); + if (state->need_tx_for_rx) { + unsigned long flags; + local_irq_save(flags); + os->spin_idle = 0; + local_irq_restore(flags); + } + } + if (file->f_mode & FMODE_WRITE) { + audio_reset(os); + } + return 0; + + default: + /* + * Let the client of this module handle the + * non generic ioctls + */ + return state->client_ioctl(inode, file, cmd, arg); + } + + return 0; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + audio_state_t *state = file->private_data; + audio_stream_t *os = state->output_stream; + audio_stream_t *is = state->input_stream; + + DPRINTK("audio_release\n"); + + down(&state->sem); + + if (file->f_mode & FMODE_READ) { + audio_discard_buf(is); + DMA_FREE(is); + is->dma_spinref = 0; + if (state->need_tx_for_rx) { + os->spin_idle = 0; + if (!state->wr_ref) { + DMA_FREE(os); + os->dma_spinref = 0; + } + } + state->rd_ref = 0; + } + + if (file->f_mode & FMODE_WRITE) { + audio_sync(file); + audio_discard_buf(os); + if (!state->need_tx_for_rx || !state->rd_ref) { + DMA_FREE(os); + os->dma_spinref = 0; + } + state->wr_ref = 0; + } + + if (!AUDIO_ACTIVE(state)) { + if (state->hw_shutdown) + state->hw_shutdown(state->data); + } + + up(&state->sem); + return 0; +} + + +#ifdef CONFIG_PM + +int sa1100_audio_suspend(audio_state_t *s, u32 state, u32 level) +{ + if (level == SUSPEND_DISABLE || level == SUSPEND_POWER_DOWN) { + audio_stream_t *is = s->input_stream; + audio_stream_t *os = s->output_stream; + int stopstate; + + if (is && is->dma_regs) { + stopstate = is->stopped; + audio_stop_dma(is); + DMA_CLEAR(is); + is->dma_spinref = 0; + is->stopped = stopstate; + } + if (os && os->dma_regs) { + stopstate = os->stopped; + audio_stop_dma(os); + DMA_CLEAR(os); + os->dma_spinref = 0; + os->stopped = stopstate; + } + if (AUDIO_ACTIVE(s) && s->hw_shutdown) + s->hw_shutdown(s->data); + } + return 0; +} + +int sa1100_audio_resume(audio_state_t *s, u32 level) +{ + if (level == RESUME_ENABLE) { + audio_stream_t *is = s->input_stream; + audio_stream_t *os = s->output_stream; + + if (AUDIO_ACTIVE(s) && s->hw_init) + s->hw_init(s->data); + if (os && os->dma_regs) { + DMA_RESET(os); + audio_process_dma(os); + } + if (is && is->dma_regs) { + DMA_RESET(is); + audio_process_dma(is); + } + } + return 0; +} + +EXPORT_SYMBOL(sa1100_audio_suspend); +EXPORT_SYMBOL(sa1100_audio_resume); + +#endif + + +int sa1100_audio_attach(struct inode *inode, struct file *file, + audio_state_t *state) +{ + audio_stream_t *os = state->output_stream; + audio_stream_t *is = state->input_stream; + int err, need_tx_dma; + + DPRINTK("audio_open\n"); + + down(&state->sem); + + /* access control */ + err = -ENODEV; + if ((file->f_mode & FMODE_WRITE) && !os) + goto out; + if ((file->f_mode & FMODE_READ) && !is) + goto out; + err = -EBUSY; + if ((file->f_mode & FMODE_WRITE) && state->wr_ref) + goto out; + if ((file->f_mode & FMODE_READ) && state->rd_ref) + goto out; + err = -EINVAL; + if ((file->f_mode & FMODE_READ) && state->need_tx_for_rx && !os) + goto out; + + /* request DMA channels */ + need_tx_dma = ((file->f_mode & FMODE_WRITE) || + ((file->f_mode & FMODE_READ) && state->need_tx_for_rx)); + if (state->wr_ref || (state->rd_ref && state->need_tx_for_rx)) + need_tx_dma = 0; + if (need_tx_dma) { + err = DMA_REQUEST(os, audio_dma_callback); + if (err) + goto out; + } + if (file->f_mode & FMODE_READ) { + err = DMA_REQUEST(is, audio_dma_callback); + if (err) { + if (need_tx_dma) + DMA_FREE(os); + goto out; + } + } + + /* now complete initialisation */ + if (!AUDIO_ACTIVE(state)) { + if (state->hw_init) + state->hw_init(state->data); + } + + if ((file->f_mode & FMODE_WRITE)) { + state->wr_ref = 1; + audio_reset(os); + os->fragsize = AUDIO_FRAGSIZE_DEFAULT; + os->nbfrags = AUDIO_NBFRAGS_DEFAULT; + os->mapped = 0; + init_waitqueue_head(&os->wq); + } + if (file->f_mode & FMODE_READ) { + state->rd_ref = 1; + audio_reset(is); + is->fragsize = AUDIO_FRAGSIZE_DEFAULT; + is->nbfrags = AUDIO_NBFRAGS_DEFAULT; + is->mapped = 0; + init_waitqueue_head(&is->wq); + } + + file->private_data = state; + file->f_op->release = audio_release; + file->f_op->write = audio_write; + file->f_op->read = audio_read; + file->f_op->mmap = audio_mmap; + file->f_op->poll = audio_poll; + file->f_op->ioctl = audio_ioctl; + file->f_op->llseek = audio_llseek; + err = 0; + +out: + up(&state->sem); + return err; +} + +EXPORT_SYMBOL(sa1100_audio_attach); + +MODULE_AUTHOR("Nicolas Pitre"); +MODULE_DESCRIPTION("Common audio handling for the SA11x0 processor"); +MODULE_LICENSE("GPL"); diff -urN orig/sound/oss/sa1100-audio.h linux/sound/oss/sa1100-audio.h --- orig/sound/oss/sa1100-audio.h Thu Jan 1 01:00:00 1970 +++ linux/sound/oss/sa1100-audio.h Tue May 27 20:22:13 2003 @@ -0,0 +1,70 @@ +/* + * Common audio handling for the SA11x0 + * + * Copyright (c) 2000 Nicolas Pitre + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + */ + +/* + * Buffer Management + */ + +typedef struct { + int offset; /* current offset */ + char *data; /* points to actual buffer */ + dma_addr_t dma_addr; /* physical buffer address */ + int dma_ref; /* DMA refcount */ + int master; /* owner for buffer allocation, contain size when true */ +} audio_buf_t; + +typedef struct { + char *id; /* identification string */ + struct device *dev; /* device */ + audio_buf_t *buffers; /* pointer to audio buffer structures */ + u_int usr_head; /* user fragment index */ + u_int dma_head; /* DMA fragment index to go */ + u_int dma_tail; /* DMA fragment index to complete */ + u_int fragsize; /* fragment i.e. buffer size */ + u_int nbfrags; /* nbr of fragments i.e. buffers */ + u_int pending_frags; /* Fragments sent to DMA */ + dma_device_t dma_dev; /* device identifier for DMA */ + dma_regs_t *dma_regs; /* points to our DMA registers */ + int bytecount; /* nbr of processed bytes */ + int fragcount; /* nbr of fragment transitions */ + struct semaphore sem; /* account for fragment usage */ + wait_queue_head_t wq; /* for poll */ + int dma_spinref; /* DMA is spinning */ + int mapped:1; /* mmap()'ed buffers */ + int active:1; /* actually in progress */ + int stopped:1; /* might be active but stopped */ + int spin_idle:1; /* have DMA spin on zeros when idle */ + int sa1111_dma:1; /* DMA handled by SA1111 */ +} audio_stream_t; + +/* + * State structure for one instance + */ + +typedef struct { + audio_stream_t *output_stream; + audio_stream_t *input_stream; + int rd_ref:1; /* open reference for recording */ + int wr_ref:1; /* open reference for playback */ + int need_tx_for_rx:1; /* if data must be sent while receiving */ + void *data; + void (*hw_init)(void *); + void (*hw_shutdown)(void *); + int (*client_ioctl)(struct inode *, struct file *, uint, ulong); + struct semaphore sem; /* to protect against races in attach() */ +} audio_state_t; + +/* + * Functions exported by this module + */ +extern int sa1100_audio_attach( struct inode *inode, struct file *file, + audio_state_t *state); +int sa1100_audio_suspend(audio_state_t *s, u32 state, u32 level); +int sa1100_audio_resume(audio_state_t *s, u32 level); + diff -urN orig/sound/oss/sa1100ssp.c linux/sound/oss/sa1100ssp.c --- orig/sound/oss/sa1100ssp.c Thu Jan 1 01:00:00 1970 +++ linux/sound/oss/sa1100ssp.c Wed Jun 11 19:08:11 2003 @@ -0,0 +1,213 @@ +/* + * Glue audio driver for a simple DAC on the SA1100's SSP port + * + * Copyright (c) 2001 Nicolas Pitre + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + * History: + * + * 2001-06-04 Nicolas Pitre Initial release. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "sa1100-audio.h" + + +#undef DEBUG +#ifdef DEBUG +#define DPRINTK( x... ) printk( ##x ) +#else +#define DPRINTK( x... ) +#endif + + +#define AUDIO_NAME "SA1100 SSP audio" + +#define AUDIO_FMT AFMT_S16_LE +#define AUDIO_CHANNELS 2 + +static int sample_rate = 44100; + + +static void ssp_audio_init(void *data) +{ + if (machine_is_lart()) { + unsigned long flags; + local_irq_save(flags); + + /* LART has the SSP port rewired to GPIO 10-13, 19 */ + /* alternate functions for the GPIOs */ + GAFR |= ( GPIO_SSP_TXD | GPIO_SSP_RXD | GPIO_SSP_SCLK | + GPIO_SSP_SFRM | GPIO_SSP_CLK ); + + /* Set the direction: 10, 12, 13 output; 11, 19 input */ + GPDR |= ( GPIO_SSP_TXD | GPIO_SSP_SCLK | GPIO_SSP_SFRM ); + GPDR &= ~( GPIO_SSP_RXD | GPIO_SSP_CLK ); + + /* enable SSP pin swap */ + PPAR |= PPAR_SPR; + + local_irq_restore(flags); + } + + /* turn on the SSP */ + Ser4SSCR0 = 0; + Ser4SSCR0 = (SSCR0_DataSize(16) | SSCR0_TI | SSCR0_SerClkDiv(2) | + SSCR0_SSE); + Ser4SSCR1 = (SSCR1_SClkIactL | SSCR1_SClk1P | SSCR1_ExtClk); +} + +static void ssp_audio_shutdown(void *data) +{ + Ser4SSCR0 = 0; +} + +static int ssp_audio_ioctl( struct inode *inode, struct file *file, + uint cmd, ulong arg) +{ + long val; + int ret = 0; + + /* + * These are platform dependent ioctls which are not handled by the + * generic sa1100-audio module. + */ + switch (cmd) { + case SNDCTL_DSP_STEREO: + ret = get_user(val, (int *) arg); + if (ret) + return ret; + /* Simple standard DACs are stereo only */ + ret = (val == 0) ? -EINVAL : 1; + return put_user(ret, (int *) arg); + + case SNDCTL_DSP_CHANNELS: + case SOUND_PCM_READ_CHANNELS: + /* Simple standard DACs are stereo only */ + return put_user(AUDIO_CHANNELS, (long *) arg); + + case SNDCTL_DSP_SPEED: + case SOUND_PCM_READ_RATE: + /* We assume the clock doesn't change */ + return put_user(sample_rate, (long *) arg); + + case SNDCTL_DSP_SETFMT: + case SNDCTL_DSP_GETFMTS: + /* Simple standard DACs are 16-bit only */ + return put_user(AUDIO_FMT, (long *) arg); + + default: + return -EINVAL; + } + + return ret; +} + +static audio_stream_t output_stream = { + id: "Generic SSP sound", + dma_dev: DMA_Ser4SSPWr, +}; + +static audio_state_t audio_state = { + output_stream: &output_stream, + hw_init: ssp_audio_init, + hw_shutdown: ssp_audio_shutdown, + client_ioctl: ssp_audio_ioctl, + sem: __MUTEX_INITIALIZER(audio_state.sem), +}; + +static int ssp_audio_open(struct inode *inode, struct file *file) +{ + return sa1100_audio_attach(inode, file, &audio_state); +} + +/* + * Missing fields of this structure will be patched with the call + * to sa1100_audio_attach(). + */ +static struct file_operations ssp_audio_fops = { + open: ssp_audio_open, + owner: THIS_MODULE +}; + +static int audio_dev_id; + +#ifdef CONFIG_PM +static int sa1100ssp_audio_suspend(struct device *_dev, u32 state, u32 level) +{ + return sa1100_audio_suspend(&audio_state, state, level); +} + +static int sa1100ssp_audio_resume(struct device *_dev, u32 level) +{ + return sa1100_audio_resume(&audio_state, level); +} +#else +#define sa1100ssp_audio_suspend NULL +#define sa1100ssp_audio_resume NULL +#endif + +static int sa1100ssp_audio_probe(struct device *_dev) +{ + audio_dev_id = register_sound_dsp(&ssp_audio_fops, -1); + + printk(KERN_INFO AUDIO_NAME " initialized\n"); + return 0; +} + +static int sa1100ssp_audio_remove(struct device *_dev) +{ + unregister_sound_dsp(audio_dev_id); + return 0; +} + +static struct device_driver sa1100ssp_audio_driver = { + .name = "sa11x0-ssp", + .bus = &platform_bus_type, + .probe = sa1100ssp_audio_probe, + .remove = sa1100ssp_audio_remove, + .suspend = sa1100ssp_audio_suspend, + .resume = sa1100ssp_audio_resume, +}; + +static int __init sa1100ssp_audio_init(void) +{ + int ret = -ENODEV; + + if (machine_is_lart()) + ret = driver_register(&sa1100ssp_audio_driver); + + return ret; +} + +static void __exit sa1100ssp_audio_exit(void) +{ + driver_unregister(&sa1100ssp_audio_driver); +} + +module_init(sa1100ssp_audio_init); +module_exit(sa1100ssp_audio_exit); + +MODULE_AUTHOR("Nicolas Pitre"); +MODULE_DESCRIPTION("Glue audio driver for a simple DAC on the SA1100's SSP port"); + +MODULE_PARM(sample_rate, "i"); +MODULE_PARM_DESC(sample_rate, "Sample rate of the audio DAC, default is 44100"); diff -urN orig/sound/oss/sa1111-sac.h linux/sound/oss/sa1111-sac.h --- orig/sound/oss/sa1111-sac.h Thu Jan 1 01:00:00 1970 +++ linux/sound/oss/sa1111-sac.h Fri Dec 21 17:07:48 2001 @@ -0,0 +1,21 @@ +/* + * SA1111 SAC DMA support + */ + +typedef struct { + volatile u_long SAD_CS; + volatile dma_addr_t SAD_SA; + volatile u_long SAD_CA; + volatile dma_addr_t SAD_SB; + volatile u_long SAD_CB; +} sac_regs_t; + +typedef struct sa1111_dma { + int (*start)(struct sa1111_dma *, dma_addr_t, size_t); + void (*reset)(struct sa1111_dma *); + dma_callback_t callback; + void *data; + sac_regs_t *regs; + int dma_a, dma_b, last_dma; +} sa1111_dma_t; + diff -urN orig/sound/oss/sa1111-uda1341.c linux/sound/oss/sa1111-uda1341.c --- orig/sound/oss/sa1111-uda1341.c Thu Jan 1 01:00:00 1970 +++ linux/sound/oss/sa1111-uda1341.c Tue May 27 20:55:49 2003 @@ -0,0 +1,595 @@ +/* + * Glue audio driver for the SA1111 compagnon chip & Philips UDA1341 codec. + * + * Copyright (c) 2000 John Dorsey + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + * History: + * + * 2000-09-04 John Dorsey SA-1111 Serial Audio Controller support + * was initially added to the sa1100-uda1341.c + * driver. + * + * 2001-06-03 Nicolas Pitre Made this file a separate module, based on + * the former sa1100-uda1341.c driver. + * + * 2001-09-23 Russell King Remove old L3 bus driver. + * + * 2001-12-19 Nicolas Pitre Moved SA1111 SAC support to this file where + * it actually belongs (formerly in dma-sa1111.c + * from John Dorsey). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sa1100-audio.h" +#include "sa1111-sac.h" + +#undef DEBUG +#ifdef DEBUG +#define DPRINTK( x... ) printk( ##x ) +#else +#define DPRINTK( x... ) +#endif + +static struct sa1111_driver sac_driver; + +/* + * L3 bus driver + */ +static inline unsigned char l3_sa1111_recv_byte(unsigned char addr) +{ + unsigned char dat; + + L3_CAR = addr; + while ((SASR0 & SASR0_L3RD) == 0) + mdelay(1); + dat = L3_CDR; + SASCR = SASCR_RDD; + return dat; +} + +static void l3_sa1111_recv_msg(struct l3_msg *msg) +{ + int len = msg->len; + char *p = msg->buf; + + if (len > 1) { + SACR1 |= SACR1_L3MB; + while ((len--) > 1) + *p++ = l3_sa1111_recv_byte(msg->addr); + } + SACR1 &= ~SACR1_L3MB; + *p = l3_sa1111_recv_byte(msg->addr); +} + +static inline void l3_sa1111_send_byte(unsigned char addr, unsigned char dat) +{ + L3_CAR = addr; + L3_CDR = dat; + while ((SASR0 & SASR0_L3WD) == 0) + mdelay(1); + SASCR = SASCR_DTS; +} + +static void l3_sa1111_send_msg(struct l3_msg *msg) +{ + int len = msg->len; + char *p = msg->buf; + + if (len > 1) { + SACR1 |= SACR1_L3MB; + while ((len--) > 1) + l3_sa1111_send_byte(msg->addr, *p++); + } + SACR1 &= ~SACR1_L3MB; + l3_sa1111_send_byte(msg->addr, *p); +} + +static int l3_sa1111_xfer(struct l3_adapter *adap, struct l3_msg msgs[], int num) +{ + int i; + + for (i = 0; i < num; i++) { + struct l3_msg *pmsg = &msgs[i]; + + if (pmsg->flags & L3_M_RD) + l3_sa1111_recv_msg(pmsg); + else + l3_sa1111_send_msg(pmsg); + } + + return num; +} + +static struct l3_algorithm l3_sa1111_algo = { + name: "L3 SA1111 algorithm", + xfer: l3_sa1111_xfer, +}; + +static DECLARE_MUTEX(sa1111_lock); + +static struct l3_adapter l3_sa1111_adapter = { + owner: THIS_MODULE, + name: "l3-sa1111", + algo: &l3_sa1111_algo, + lock: &sa1111_lock, +}; + +/* + * Definitions + */ +#define AUDIO_RATE_DEFAULT 22050 + +/* + * Mixer interface + */ +static struct l3_client uda1341; + +static int +mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) +{ + /* + * We only accept mixer (type 'M') ioctls. + */ + if (_IOC_TYPE(cmd) != 'M') + return -EINVAL; + + return l3_command(&uda1341, cmd, (void *)arg); +} + +static struct file_operations uda1341_mixer_fops = { + ioctl: mixer_ioctl, + owner: THIS_MODULE +}; + + +/* + * SA1111 SAC DMA support + */ + +static void reset_sac_dma(sa1111_dma_t *dma) +{ + dma->regs->SAD_CS = 0; + mdelay(1); + dma->dma_a = dma->dma_b = 0; +} + +static int start_sac_dma(sa1111_dma_t *dma, dma_addr_t dma_ptr, size_t size) +{ + sac_regs_t *sac_regs = dma->regs; + + DPRINTK(" SAC DMA %cCS %02x at %08x (%d)\n", + (sac_regs==&SADTCS)?'T':'R', sac_regs->SAD_CS, dma_ptr, size); + + /* The minimum transfer length requirement has not yet been + * verified: + */ + if( size < SA1111_SAC_DMA_MIN_XFER ) + printk(KERN_WARNING "Warning: SAC xfers below %u bytes may be buggy!" + " (%u bytes)\n", SA1111_SAC_DMA_MIN_XFER, size); + + if( dma->dma_a && dma->dma_b ){ + DPRINTK(" neither engine available! (A %d, B %d)\n", + dma->dma_a, dma->dma_b); + return -1; + } + + if( sa1111_check_dma_bug(dma_ptr) ) + printk(KERN_WARNING "Warning: DMA address %08x is buggy!\n", + dma_ptr); + + if( (dma->last_dma || dma->dma_b) && dma->dma_a == 0 ){ + if( sac_regs->SAD_CS & SAD_CS_DBDB ){ + DPRINTK(" awaiting \"done B\" interrupt, not starting\n"); + return -1; + } + sac_regs->SAD_SA = SA1111_DMA_ADDR((u_int)dma_ptr); + sac_regs->SAD_CA = size; + sac_regs->SAD_CS = SAD_CS_DSTA | SAD_CS_DEN; + ++dma->dma_a; + DPRINTK(" with A [%02lx %08lx %04lx]\n", sac_regs->SAD_CS, + sac_regs->SAD_SA, sac_regs->SAD_CA); + } else { + if( sac_regs->SAD_CS & SAD_CS_DBDA ){ + DPRINTK(" awaiting \"done A\" interrupt, not starting\n"); + return -1; + } + sac_regs->SAD_SB = SA1111_DMA_ADDR((u_int)dma_ptr); + sac_regs->SAD_CB = size; + sac_regs->SAD_CS = SAD_CS_DSTB | SAD_CS_DEN; + ++dma->dma_b; + DPRINTK(" with B [%02lx %08lx %04lx]\n", sac_regs->SAD_CS, + sac_regs->SAD_SB, sac_regs->SAD_CB); + } + + /* Additional delay to avoid DMA engine lockup during record: */ + if (sac_regs == (sac_regs_t*)&SADRCS) + mdelay(1); /* NP : wouuuh! ugly... */ + + return 0; +} + +static irqreturn_t sac_dma_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + sa1111_dma_t *dma = dev_id; + + DPRINTK("irq %d, last DMA serviced was %c, CS %02x\n", irq, + dma->last_dma?'B':'A', dma->regs->SAD_CS); + + /* Occasionally, one of the DMA engines (A or B) will + * lock up. We try to deal with this by quietly kicking + * the control register for the afflicted transfer + * direction. + * + * Note for the debugging-inclined: we currently aren't + * properly flushing the DMA engines during channel + * shutdown. A slight hiccup at the beginning of playback + * after a channel has been stopped can be heard as + * evidence of this. Programmatically, this shows up + * as either a locked engine, or a spurious interrupt. -jd + */ + + if(irq==AUDXMTDMADONEA || irq==AUDRCVDMADONEA){ + + if(dma->last_dma == 0){ + DPRINTK("DMA B has locked up!\n"); + dma->regs->SAD_CS = 0; + mdelay(1); + dma->dma_a = dma->dma_b = 0; + } else { + if(dma->dma_a == 0) + DPRINTK("spurious SAC IRQ %d\n", irq); + else { + --dma->dma_a; + + /* Servicing the SAC DMA engines too quickly + * after they issue a DONE interrupt causes + * them to lock up. + */ + if(irq==AUDRCVDMADONEA || irq==AUDRCVDMADONEB) + mdelay(1); + } + } + + dma->regs->SAD_CS = SAD_CS_DBDA | SAD_CS_DEN; /* w1c */ + dma->last_dma = 0; + + } else { + + if(dma->last_dma == 1){ + DPRINTK("DMA A has locked up!\n"); + dma->regs->SAD_CS = 0; + mdelay(1); + dma->dma_a = dma->dma_b = 0; + } else { + if(dma->dma_b == 0) + DPRINTK("spurious SAC IRQ %d\n", irq); + else { + --dma->dma_b; + + /* See lock-up note above. */ + if(irq==AUDRCVDMADONEA || irq==AUDRCVDMADONEB) + mdelay(1); + } + } + + dma->regs->SAD_CS = SAD_CS_DBDB | SAD_CS_DEN; /* w1c */ + dma->last_dma = 1; + + } + + /* NP: maybe this shouldn't be called in all cases? */ + dma->callback(dma->data); + + return IRQ_HANDLED; +} + +static sa1111_dma_t sac_tx = { + reset: reset_sac_dma, + start: start_sac_dma, + regs: (sac_regs_t *) &SADTCS, +}; + +static sa1111_dma_t sac_rx = { + reset: reset_sac_dma, + start: start_sac_dma, + regs: (sac_regs_t *) &SADRCS, +}; + + +/* + * Audio interface + */ +static void sa1111_audio_init(void *dummy) +{ + struct sa1111_dev *sadev = dummy; + +#ifdef CONFIG_SA1100_JORNADA720 + if (machine_is_jornada720()) { + /* LDD4 is speaker, LDD3 is microphone */ + PPSR &= ~(PPC_LDD3 | PPC_LDD4); + PPDR |= PPC_LDD3 | PPC_LDD4; + PPSR |= PPC_LDD4; /* enable speaker */ + PPSR |= PPC_LDD3; /* enable microphone */ + } +#endif +#ifdef CONFIG_ASSABET_NEPONSET + if (machine_is_assabet()) { + /* Select I2S audio (instead of AC-Link) */ + AUD_CTL = AUD_SEL_1341; + } +#endif + + /* Select i2s mode */ + sa1111_select_audio_mode(sadev, SA1111_AUDIO_I2S); + + /* Enable the I2S clock and L3 bus clock: */ + sa1111_enable_device(sadev); + + /* Activate and reset the Serial Audio Controller */ + SACR0 |= (SACR0_ENB | SACR0_RST); + mdelay(5); + SACR0 &= ~SACR0_RST; + + /* For I2S, BIT_CLK is supplied internally. The "SA-1111 + * Specification Update" mentions that the BCKD bit should + * be interpreted as "0 = output". Default clock divider + * is 22.05kHz. + * + * Select I2S, L3 bus. "Recording" and "Replaying" + * (receive and transmit) are enabled. + */ + SACR1 = SACR1_L3EN; + + /* Initialize the UDA1341 internal state */ + l3_open(&uda1341); +} + +static void sa1111_audio_shutdown(void *dummy) +{ + struct sa1111_dev *sadev = dummy; + + l3_close(&uda1341); + SACR0 &= ~SACR0_ENB; + + /* Disable the I2S clock and L3 bus clock */ + sa1111_disable_device(sadev); +} + +static int +sa1111_audio_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) +{ + audio_state_t *state = file->private_data; + struct sa1111_dev *sadev = state->data; + long val; + int ret = 0; + + switch (cmd) { + case SNDCTL_DSP_STEREO: + ret = get_user(val, (int *) arg); + if (ret) + break; + /* the UDA1341 is stereo only */ + ret = (val == 0) ? -EINVAL : 1; + ret = put_user(ret, (int *) arg); + break; + + case SNDCTL_DSP_CHANNELS: + case SOUND_PCM_READ_CHANNELS: + /* the UDA1341 is stereo only */ + ret = put_user(2, (long *) arg); + break; + + case SNDCTL_DSP_SPEED: + ret = get_user(val, (long *) arg); + if (ret) + break; + sa1111_set_audio_rate(sadev, val); + /* fall through */ + + case SOUND_PCM_READ_RATE: + ret = put_user(sa1111_get_audio_rate(sadev), (long *) arg); + break; + + case SNDCTL_DSP_SETFMT: + case SNDCTL_DSP_GETFMTS: + /* we can do 16-bit only */ + ret = put_user(AFMT_S16_LE, (long *) arg); + break; + + default: + /* Maybe this is meant for the mixer (as per OSS Docs) */ + ret = mixer_ioctl(inode, file, cmd, arg); + break; + } + + return ret; +} + +static audio_stream_t output_stream = { + sa1111_dma: 1, + dma_regs: (dma_regs_t *) &sac_tx, +}; + +static audio_stream_t input_stream = { + sa1111_dma: 1, + dma_regs: (dma_regs_t *) &sac_rx, +}; + +static audio_state_t audio_state = { + output_stream: &output_stream, + input_stream: &input_stream, + hw_init: sa1111_audio_init, + hw_shutdown: sa1111_audio_shutdown, + client_ioctl: sa1111_audio_ioctl, + sem: __MUTEX_INITIALIZER(audio_state.sem), +}; + +static int sa1111_audio_open(struct inode *inode, struct file *file) +{ + return sa1100_audio_attach(inode, file, &audio_state); +} + +/* + * Missing fields of this structure will be patched with the call + * to sa1100_audio_attach(). + */ +static struct file_operations sa1111_audio_fops = { + open: sa1111_audio_open, + owner: THIS_MODULE +}; + +static int audio_dev_id, mixer_dev_id; + +static int sac_probe(struct device *dev) +{ + struct sa1111_dev *sadev = SA1111_DEV(dev); + struct uda1341_cfg cfg; + int ret; + + if (!request_mem_region(sadev->res.start, 512, SA1111_DRIVER_NAME(sadev))) + return -EBUSY; + + audio_state.data = sadev; + input_stream.dev = dev; + output_stream.dev = dev; + l3_sa1111_adapter.data = sadev; + + ret = l3_add_adapter(&l3_sa1111_adapter); + if (ret) + goto out; + + ret = l3_attach_client(&uda1341, "l3-sa1111", "uda1341"); + if (ret) + goto remove_l3; + + /* + * Acquire SAC interrupts + */ + ret = request_irq(sadev->irq[0], sac_dma_irq, SA_INTERRUPT, + SA1111_DRIVER_NAME(sadev), &sac_tx); + if (ret) + goto release_l3; + ret = request_irq(sadev->irq[1], sac_dma_irq, SA_INTERRUPT, + SA1111_DRIVER_NAME(sadev), &sac_tx); + if (ret) + goto release_ITXA; + ret = request_irq(sadev->irq[2], sac_dma_irq, SA_INTERRUPT, + SA1111_DRIVER_NAME(sadev), &sac_rx); + if (ret) + goto release_ITXB; + ret = request_irq(sadev->irq[3], sac_dma_irq, SA_INTERRUPT, + SA1111_DRIVER_NAME(sadev), &sac_rx); + if (ret) + goto release_IRXA; + + cfg.fs = 256; + cfg.format = FMT_I2S; + l3_command(&uda1341, L3_UDA1341_CONFIGURE, &cfg); + + sa1111_set_audio_rate(sadev, AUDIO_RATE_DEFAULT); + + /* register devices */ + audio_dev_id = register_sound_dsp(&sa1111_audio_fops, -1); + mixer_dev_id = register_sound_mixer(&uda1341_mixer_fops, -1); + + printk(KERN_INFO "Sound: SA1111 UDA1341: dsp id %d mixer id %d\n", + audio_dev_id, mixer_dev_id); + return 0; + +release_IRXA: + free_irq(AUDRCVDMADONEA, &sac_rx); +release_ITXB: + free_irq(AUDXMTDMADONEB, &sac_tx); +release_ITXA: + free_irq(AUDXMTDMADONEA, &sac_tx); +release_l3: + l3_detach_client(&uda1341); +remove_l3: + l3_del_adapter(&l3_sa1111_adapter); +out: + release_mem_region(sadev->res.start, 512); + return ret; +} + +static int sac_remove(struct device *dev) +{ + struct sa1111_dev *sadev = SA1111_DEV(dev); + + unregister_sound_dsp(audio_dev_id); + unregister_sound_mixer(mixer_dev_id); + free_irq(AUDRCVDMADONEB, &sac_rx); + free_irq(AUDRCVDMADONEA, &sac_rx); + free_irq(AUDXMTDMADONEB, &sac_tx); + free_irq(AUDXMTDMADONEA, &sac_tx); + l3_detach_client(&uda1341); + l3_del_adapter(&l3_sa1111_adapter); + + release_mem_region(sadev->res.start, 512); + + return 0; +} + +static int sac_suspend(struct device *dev, u32 state, u32 level) +{ + return sa1100_audio_suspend(&audio_state, state, level); +} + +static int sac_resume(struct device *dev, u32 level) +{ + return sa1100_audio_resume(&audio_state, level); +} + +static struct sa1111_driver sac_driver = { + .drv = { + .name = "sa1111-uda1341", + .bus = &sa1111_bus_type, + .probe = sac_probe, + .remove = sac_remove, + .suspend = sac_suspend, + .resume = sac_resume, + }, + .devid = SA1111_DEVID_SAC, +}; + +static int sa1111_uda1341_init(void) +{ + return driver_register(&sac_driver.drv); +} + +static void sa1111_uda1341_exit(void) +{ + driver_unregister(&sac_driver.drv); +} + +module_init(sa1111_uda1341_init); +module_exit(sa1111_uda1341_exit); + +MODULE_AUTHOR("John Dorsey, Nicolas Pitre"); +MODULE_DESCRIPTION("Glue audio driver for the SA1111 companion chip & Philips UDA1341 codec."); diff -urN orig/sound/oss/stork-uda1341.c linux/sound/oss/stork-uda1341.c --- orig/sound/oss/stork-uda1341.c Thu Jan 1 01:00:00 1970 +++ linux/sound/oss/stork-uda1341.c Wed Jun 11 19:19:18 2003 @@ -0,0 +1,313 @@ +/* + * Glue audio driver for the Stork & Philips UDA1341 codec. + * + * Based on bitsy-uda1341.c 99% the same in fact! + * + * Copyright (c) 2000 Nicolas Pitre + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + * This driver makes use of the UDA1341 and the sa1100-audio modules. + * + * History: + * + * 2000-05-21 Nicolas Pitre Initial UDA1341 driver release. + * + * 2000-07-?? George France Bitsy support. + * + * 2000-12-13 Deborah Wallach Fixed power handling for iPAQ/bitsy + * + * 2001-06-03 Nicolas Pitre Made this file a separate module, based on + * the former sa1100-uda1341.c driver. + * + * 2001-07-13 Nicolas Pitre Fixes for all supported samplerates. + * + * 2001-09-18 Ken Gordon Support Stork + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "sa1100-audio.h" + + +#define DEBUG 1 +#ifdef DEBUG +#define DPRINTK( x... ) printk( ##x ) +#else +#define DPRINTK( x... ) +#endif + + +#define AUDIO_NAME "Stork_UDA1341" + +#define AUDIO_FMT_MASK (AFMT_S16_LE) +#define AUDIO_FMT_DEFAULT (AFMT_S16_LE) +#define AUDIO_CHANNELS_DEFAULT 2 +#define AUDIO_RATE_DEFAULT 44100 + + + +static struct l3_client uda1341; + +static int +mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) +{ + /* + * We only accept mixer (type 'M') ioctls. + */ + if (_IOC_TYPE(cmd) != 'M') + return -EINVAL; + + return l3_command(&uda1341, cmd, (void *)arg); +} + + +static struct file_operations stork_mixer_fops = { + ioctl: mixer_ioctl, + owner: THIS_MODULE +}; + + +/* + * Audio interface + */ + +static long audio_samplerate = AUDIO_RATE_DEFAULT; + +static void stork_set_samplerate(long val) +{ + struct uda1341_cfg cfg; + int clk_div = 0; + + /* We don't want to mess with clocks when frames are in flight */ + Ser4SSCR0 &= ~SSCR0_SSE; + /* wait for any frame to complete */ + udelay(125); + + + val = 44100; + storkSetLatchB(STORK_AUDIO_CLOCK_SEL0); + storkClearLatchB(STORK_AUDIO_CLOCK_SEL1); + clk_div = SSCR0_SerClkDiv(4); + cfg.fs = 256; + cfg.format = FMT_LSB16; + l3_command(&uda1341, L3_UDA1341_CONFIGURE, &cfg); + + Ser4SSCR0 = (Ser4SSCR0 & ~0xff00) + clk_div + SSCR0_SSE; + audio_samplerate = val; +} + +static void stork_audio_init(void *data) +{ + storkSetLatchA(STORK_AUDIO_POWER_ON); + storkClearLatchA(STORK_AUDIO_AMP_ON); + storkClearLatchB(STORK_CODEC_QMUTE); + + /* Setup the uarts */ + GAFR |= (GPIO_SSP_CLK); + GPDR &= ~(GPIO_SSP_CLK); + Ser4SSCR0 = 0; + Ser4SSCR0 = SSCR0_DataSize(16) + SSCR0_TI + SSCR0_SerClkDiv(8); + Ser4SSCR1 = SSCR1_SClkIactL + SSCR1_SClk1P + SSCR1_ExtClk; + Ser4SSCR0 |= SSCR0_SSE; + + /* Setup L3 bus */ +/* L3_init(); - not any more */ + +/* this is not really a reset it puts the cpld into a wild mode - watch out in case the + channels end up the wrong way round */ + + storkSetLatchB(STORK_AUDIO_CODEC_RESET); + + stork_set_samplerate(audio_samplerate); + + /* Wait for the UDA1341 to wake up */ + mdelay(1); + + l3_open(&uda1341); +} + +static void stork_audio_shutdown(void *data) +{ + l3_close(&uda1341); + /* disable the audio power and all signals leading to the audio chip */ + Ser4SSCR0 = 0; + storkSetLatchB(STORK_CODEC_QMUTE); + storkClearLatchA(STORK_AUDIO_POWER_ON); + storkSetLatchA(STORK_AUDIO_AMP_ON); +} + +static int stork_audio_ioctl(struct inode *inode, struct file *file, + uint cmd, ulong arg) +{ + long val; + int ret = 0; + + /* + * These are platform dependent ioctls which are not handled by the + * generic sa1100-audio module. + */ + switch (cmd) { + case SNDCTL_DSP_STEREO: + ret = get_user(val, (int *) arg); + if (ret) + return ret; + /* the UDA1341 is stereo only */ + ret = (val == 0) ? -EINVAL : 1; + return put_user(ret, (int *) arg); + + case SNDCTL_DSP_CHANNELS: + case SOUND_PCM_READ_CHANNELS: + /* the UDA1341 is stereo only */ + return put_user(2, (long *) arg); + + case SNDCTL_DSP_SPEED: + ret = get_user(val, (long *) arg); + if (ret) break; + stork_set_samplerate(val); + /* fall through */ + + case SOUND_PCM_READ_RATE: + return put_user(audio_samplerate, (long *) arg); + + case SNDCTL_DSP_SETFMT: + case SNDCTL_DSP_GETFMTS: + /* we can do 16-bit only */ + return put_user(AFMT_S16_LE, (long *) arg); + + default: + /* Maybe this is meant for the mixer (As per OSS Docs) */ + return mixer_ioctl(inode, file, cmd, arg); + } + + return ret; +} + + +static audio_stream_t output_stream = { + id: "UDA1341 out", + dma_dev: DMA_Ser4SSPWr, +}; + +static audio_stream_t input_stream = { + id: "UDA1341 in", + dma_dev: DMA_Ser4SSPRd, +}; + +static audio_state_t audio_state = { + output_stream: &output_stream, + input_stream: &input_stream, + need_tx_for_rx: 1, + hw_init: stork_audio_init, + hw_shutdown: stork_audio_shutdown, + client_ioctl: stork_audio_ioctl, + sem: __MUTEX_INITIALIZER(audio_state.sem), +}; + +static int stork_audio_open(struct inode *inode, struct file *file) +{ + return sa1100_audio_attach(inode, file, &audio_state); +} + +/* + * Missing fields of this structure will be patched with the call + * to sa1100_audio_instance() + */ +static struct file_operations stork_audio_fops = { + open: stork_audio_open, + owner: THIS_MODULE +}; + + +static int audio_dev_id, mixer_dev_id; + +#ifdef CONFIG_PM +static int stork_audio_suspend(struct device *_dev, u32 state, u32 level) +{ + return sa1100_audio_suspend(&audio_state, state, level); +} + +static int stork_audio_resume(struct device *_dev, u32 level) +{ + return sa1100_audio_resume(&audio_state, level); +} +#else +#define stork_audio_suspend NULL +#define stork_audio_resume NULL +#endif + +static int stork_audio_probe(struct device *_dev) +{ + int ret; + + printk("Stork audio driver\n" ); + + ret = l3_attach_client(&uda1341, "l3-bit-sa1100-gpio", "uda1341"); + if (ret) + goto out; + + /* register devices */ + audio_dev_id = register_sound_dsp(&stork_audio_fops, -1); + mixer_dev_id = register_sound_mixer(&stork_mixer_fops, -1); + + printk("Stork audio support initialized\n" ); + + out: + return ret; +} + +static int stork_audio_remove(struct device *_dev) +{ + unregister_sound_dsp(audio_dev_id); + unregister_sound_mixer(mixer_dev_id); + l3_detach_client(&uda1341); + + return 0; +} + +static struct device_driver stork_audio_driver = { + .name = "stork-uda1341", + .bus = &system_bus_type, + .probe = stork_audio_probe, + .remove = stork_audio_remove, + .suspend = stork_audio_suspend, + .resume = stork_audio_resume, +}; + +static int __init stork_uda1341_init(void) +{ + int ret = -ENODEV; + + if (machine_is_stork()) + ret = driver_register(&stork_audio_driver); + + return ret; +} + +static void __exit stork_uda1341_exit(void) +{ + driver_unregister(&stork_audio_driver); +} + +module_init(stork_uda1341_init); +module_exit(stork_uda1341_exit); + +MODULE_AUTHOR("Nicolas Pitre, George France"); +MODULE_DESCRIPTION("Glue audio driver for the Stork & Philips UDA1341 codec."); diff -urN orig/sound/oss/uda1341.c linux/sound/oss/uda1341.c --- orig/sound/oss/uda1341.c Thu Jan 1 01:00:00 1970 +++ linux/sound/oss/uda1341.c Tue Jun 18 19:07:56 2002 @@ -0,0 +1,511 @@ +/* + * Philips UDA1341 mixer device driver + * + * Copyright (c) 2000 Nicolas Pitre + * + * Portions are Copyright (C) 2000 Lernout & Hauspie Speech Products, N.V. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + * History: + * + * 2000-05-21 Nicolas Pitre Initial release. + * + * 2000-08-19 Erik Bunce More inline w/ OSS API and UDA1341 docs + * including fixed AGC and audio source handling + * + * 2000-11-30 Nicolas Pitre - More mixer functionalities. + * + * 2001-06-03 Nicolas Pitre Made this file a separate module, based on + * the former sa1100-uda1341.c driver. + * + * 2001-08-13 Russell King Re-written as part of the L3 interface + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DEF_VOLUME 65 + +/* + * UDA1341 L3 address and command types + */ +#define UDA1341_L3ADDR 5 +#define UDA1341_DATA0 (UDA1341_L3ADDR << 2 | 0) +#define UDA1341_DATA1 (UDA1341_L3ADDR << 2 | 1) +#define UDA1341_STATUS (UDA1341_L3ADDR << 2 | 2) + +struct uda1341_regs { + unsigned char stat0; +#define STAT0 0x00 +#define STAT0_RST (1 << 6) +#define STAT0_SC_MASK (3 << 4) +#define STAT0_SC_512FS (0 << 4) +#define STAT0_SC_384FS (1 << 4) +#define STAT0_SC_256FS (2 << 4) +#define STAT0_IF_MASK (7 << 1) +#define STAT0_IF_I2S (0 << 1) +#define STAT0_IF_LSB16 (1 << 1) +#define STAT0_IF_LSB18 (2 << 1) +#define STAT0_IF_LSB20 (3 << 1) +#define STAT0_IF_MSB (4 << 1) +#define STAT0_IF_LSB16MSB (5 << 1) +#define STAT0_IF_LSB18MSB (6 << 1) +#define STAT0_IF_LSB20MSB (7 << 1) +#define STAT0_DC_FILTER (1 << 0) + + unsigned char stat1; +#define STAT1 0x80 +#define STAT1_DAC_GAIN (1 << 6) /* gain of DAC */ +#define STAT1_ADC_GAIN (1 << 5) /* gain of ADC */ +#define STAT1_ADC_POL (1 << 4) /* polarity of ADC */ +#define STAT1_DAC_POL (1 << 3) /* polarity of DAC */ +#define STAT1_DBL_SPD (1 << 2) /* double speed playback */ +#define STAT1_ADC_ON (1 << 1) /* ADC powered */ +#define STAT1_DAC_ON (1 << 0) /* DAC powered */ + + unsigned char data0_0; +#define DATA0 0x00 +#define DATA0_VOLUME_MASK 0x3f +#define DATA0_VOLUME(x) (x) + + unsigned char data0_1; +#define DATA1 0x40 +#define DATA1_BASS(x) ((x) << 2) +#define DATA1_BASS_MASK (15 << 2) +#define DATA1_TREBLE(x) ((x)) +#define DATA1_TREBLE_MASK (3) + + unsigned char data0_2; +#define DATA2 0x80 +#define DATA2_PEAKAFTER (1 << 5) +#define DATA2_DEEMP_NONE (0 << 3) +#define DATA2_DEEMP_32KHz (1 << 3) +#define DATA2_DEEMP_44KHz (2 << 3) +#define DATA2_DEEMP_48KHz (3 << 3) +#define DATA2_MUTE (1 << 2) +#define DATA2_FILTER_FLAT (0 << 0) +#define DATA2_FILTER_MIN (1 << 0) +#define DATA2_FILTER_MAX (3 << 0) + +#define EXTADDR(n) (0xc0 | (n)) +#define EXTDATA(d) (0xe0 | (d)) + + unsigned char ext0; +#define EXT0 0 +#define EXT0_CH1_GAIN(x) (x) + + unsigned char ext1; +#define EXT1 1 +#define EXT1_CH2_GAIN(x) (x) + + unsigned char ext2; +#define EXT2 2 +#define EXT2_MIC_GAIN_MASK (7 << 2) +#define EXT2_MIC_GAIN(x) ((x) << 2) +#define EXT2_MIXMODE_DOUBLEDIFF (0) +#define EXT2_MIXMODE_CH1 (1) +#define EXT2_MIXMODE_CH2 (2) +#define EXT2_MIXMODE_MIX (3) + + unsigned char ext4; +#define EXT4 4 +#define EXT4_AGC_ENABLE (1 << 4) +#define EXT4_INPUT_GAIN_MASK (3) +#define EXT4_INPUT_GAIN(x) ((x) & 3) + + unsigned char ext5; +#define EXT5 5 +#define EXT5_INPUT_GAIN(x) ((x) >> 2) + + unsigned char ext6; +#define EXT6 6 +#define EXT6_AGC_CONSTANT_MASK (7 << 2) +#define EXT6_AGC_CONSTANT(x) ((x) << 2) +#define EXT6_AGC_LEVEL_MASK (3) +#define EXT6_AGC_LEVEL(x) (x) +}; + +#define REC_MASK (SOUND_MASK_LINE | SOUND_MASK_MIC) +#define DEV_MASK (REC_MASK | SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE) + +struct uda1341 { + struct uda1341_regs regs; + int active; + unsigned short volume; + unsigned short bass; + unsigned short treble; + unsigned short line; + unsigned short mic; + int mod_cnt; +}; + +#define ADD_FIELD(reg,field) \ + *p++ = reg | uda->regs.field + +#define ADD_EXTFIELD(reg,field) \ + *p++ = EXTADDR(reg); \ + *p++ = EXTDATA(uda->regs.field); + +static void uda1341_sync(struct l3_client *clnt) +{ + struct uda1341 *uda = clnt->driver_data; + char buf[24], *p = buf; + + ADD_FIELD(STAT0, stat0); + ADD_FIELD(STAT1, stat1); + + if (p != buf) + l3_write(clnt, UDA1341_STATUS, buf, p - buf); + + p = buf; + ADD_FIELD(DATA0, data0_0); + ADD_FIELD(DATA1, data0_1); + ADD_FIELD(DATA2, data0_2); + ADD_EXTFIELD(EXT0, ext0); + ADD_EXTFIELD(EXT1, ext1); + ADD_EXTFIELD(EXT2, ext2); + ADD_EXTFIELD(EXT4, ext4); + ADD_EXTFIELD(EXT5, ext5); + ADD_EXTFIELD(EXT6, ext6); + + if (p != buf) + l3_write(clnt, UDA1341_DATA0, buf, p - buf); +} + +static void uda1341_cmd_init(struct l3_client *clnt) +{ + struct uda1341 *uda = clnt->driver_data; + char buf[2]; + + uda->active = 1; + + buf[0] = uda->regs.stat0 | STAT0_RST; + buf[1] = uda->regs.stat0; + + l3_write(clnt, UDA1341_STATUS, buf, 2); + + /* resend all parameters */ + uda1341_sync(clnt); +} + +static int uda1341_configure(struct l3_client *clnt, struct uda1341_cfg *conf) +{ + struct uda1341 *uda = clnt->driver_data; + int ret = 0; + + uda->regs.stat0 &= ~(STAT0_SC_MASK | STAT0_IF_MASK); + + switch (conf->fs) { + case 512: uda->regs.stat0 |= STAT0_SC_512FS; break; + case 384: uda->regs.stat0 |= STAT0_SC_384FS; break; + case 256: uda->regs.stat0 |= STAT0_SC_256FS; break; + default: ret = -EINVAL; break; + } + + switch (conf->format) { + case FMT_I2S: uda->regs.stat0 |= STAT0_IF_I2S; break; + case FMT_LSB16: uda->regs.stat0 |= STAT0_IF_LSB16; break; + case FMT_LSB18: uda->regs.stat0 |= STAT0_IF_LSB18; break; + case FMT_LSB20: uda->regs.stat0 |= STAT0_IF_LSB20; break; + case FMT_MSB: uda->regs.stat0 |= STAT0_IF_MSB; break; + case FMT_LSB16MSB: uda->regs.stat0 |= STAT0_IF_LSB16MSB; break; + case FMT_LSB18MSB: uda->regs.stat0 |= STAT0_IF_LSB18MSB; break; + case FMT_LSB20MSB: uda->regs.stat0 |= STAT0_IF_LSB20MSB; break; + } + + if (ret == 0 && uda->active) { + char buf = uda->regs.stat0 | STAT0; + l3_write(clnt, UDA1341_STATUS, &buf, 1); + } + return ret; +} + +static int uda1341_update_direct(struct l3_client *clnt, int cmd, void *arg) +{ + struct uda1341 *uda = clnt->driver_data; + struct l3_gain *v = arg; + char newreg; + int val; + + switch (cmd) { + case L3_SET_VOLUME: /* set volume. val = 0 to 100 => 62 to 1 */ + uda->regs.data0_0 = DATA0_VOLUME(62 - ((v->left * 61) / 100)); + newreg = uda->regs.data0_0 | DATA0; + break; + + case L3_SET_BASS: /* set bass. val = 50 to 100 => 0 to 12 */ + val = v->left - 50; + if (val < 0) + val = 0; + uda->regs.data0_1 &= ~DATA1_BASS_MASK; + uda->regs.data0_1 |= DATA1_BASS((val * 12) / 50); + newreg = uda->regs.data0_1 | DATA1; + break; + + case L3_SET_TREBLE: /* set treble. val = 50 to 100 => 0 to 3 */ + val = v->left - 50; + if (val < 0) + val = 0; + uda->regs.data0_1 &= ~DATA1_TREBLE_MASK; + uda->regs.data0_1 |= DATA1_TREBLE((val * 3) / 50); + newreg = uda->regs.data0_1 | DATA1; + break; + + default: + return -EINVAL; + } + + if (uda->active) + l3_write(clnt, UDA1341_DATA0, &newreg, 1); + return 0; +} + +static int uda1341_update_indirect(struct l3_client *clnt, int cmd, void *arg) +{ + struct uda1341 *uda = clnt->driver_data; + struct l3_gain *gain = arg; + struct l3_agc *agc = arg; + char buf[8], *p = buf; + int val, ret = 0; + + switch (cmd) { + case L3_SET_GAIN: + val = 31 - (gain->left * 31 / 100); + switch (gain->channel) { + case 1: + uda->regs.ext0 = EXT0_CH1_GAIN(val); + ADD_EXTFIELD(EXT0, ext0); + break; + + case 2: + uda->regs.ext1 = EXT1_CH2_GAIN(val); + ADD_EXTFIELD(EXT1, ext1); + break; + + default: + ret = -EINVAL; + } + break; + + case L3_INPUT_AGC: + if (agc->channel == 2) { + if (agc->enable) + uda->regs.ext4 |= EXT4_AGC_ENABLE; + else + uda->regs.ext4 &= ~EXT4_AGC_ENABLE; +#if 0 + agc->level + agc->attack + agc->decay +#endif + ADD_EXTFIELD(EXT4, ext4); + } else + ret = -EINVAL; + break; + + default: + ret = -EINVAL; + break; + } + + if (ret == 0 && uda->active) + l3_write(clnt, UDA1341_DATA0, buf, p - buf); + + return ret; +} + +static int uda1341_mixer_ioctl(struct l3_client *clnt, int cmd, void *arg) +{ + struct uda1341 *uda = clnt->driver_data; + struct l3_gain gain; + int val, nr = _IOC_NR(cmd), ret = 0; + + if (cmd == SOUND_MIXER_INFO) { + struct mixer_info mi; + + strncpy(mi.id, "UDA1341", sizeof(mi.id)); + strncpy(mi.name, "Philips UDA1341", sizeof(mi.name)); + mi.modify_counter = uda->mod_cnt; + return copy_to_user(arg, &mi, sizeof(mi)) ? -EFAULT : 0; + } + + if (_IOC_DIR(cmd) & _IOC_WRITE) { + ret = get_user(val, (int *)arg); + if (ret) + goto out; + + gain.left = val & 255; + gain.right = val >> 8; + gain.channel = 0; + + switch (nr) { + case SOUND_MIXER_VOLUME: + uda->volume = val; + uda->mod_cnt++; + uda1341_update_direct(clnt, L3_SET_VOLUME, &gain); + break; + + case SOUND_MIXER_BASS: + uda->bass = val; + uda->mod_cnt++; + uda1341_update_direct(clnt, L3_SET_BASS, &gain); + break; + + case SOUND_MIXER_TREBLE: + uda->treble = val; + uda->mod_cnt++; + uda1341_update_direct(clnt, L3_SET_TREBLE, &gain); + break; + + case SOUND_MIXER_LINE: + uda->line = val; + gain.channel = 1; + uda->mod_cnt++; + uda1341_update_indirect(clnt, L3_SET_GAIN, &gain); + break; + + case SOUND_MIXER_MIC: + uda->mic = val; + gain.channel = 2; + uda->mod_cnt++; + uda1341_update_indirect(clnt, L3_SET_GAIN, &gain); + break; + + case SOUND_MIXER_RECSRC: + break; + + default: + ret = -EINVAL; + } + } + + if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) { + int nr = _IOC_NR(cmd); + ret = 0; + + switch (nr) { + case SOUND_MIXER_VOLUME: val = uda->volume; break; + case SOUND_MIXER_BASS: val = uda->bass; break; + case SOUND_MIXER_TREBLE: val = uda->treble; break; + case SOUND_MIXER_LINE: val = uda->line; break; + case SOUND_MIXER_MIC: val = uda->mic; break; + case SOUND_MIXER_RECSRC: val = REC_MASK; break; + case SOUND_MIXER_RECMASK: val = REC_MASK; break; + case SOUND_MIXER_DEVMASK: val = DEV_MASK; break; + case SOUND_MIXER_CAPS: val = 0; break; + case SOUND_MIXER_STEREODEVS: val = 0; break; + default: val = 0; ret = -EINVAL; break; + } + + if (ret == 0) + ret = put_user(val, (int *)arg); + } +out: + return ret; +} + +static int uda1341_attach(struct l3_client *clnt) +{ + struct uda1341 *uda; + + uda = kmalloc(sizeof(*uda), GFP_KERNEL); + if (!uda) + return -ENOMEM; + + memset(uda, 0, sizeof(*uda)); + + uda->volume = DEF_VOLUME | DEF_VOLUME << 8; + uda->bass = 50 | 50 << 8; + uda->treble = 50 | 50 << 8; + uda->line = 88 | 88 << 8; + uda->mic = 88 | 88 << 8; + + uda->regs.stat0 = STAT0_SC_256FS | STAT0_IF_LSB16; + uda->regs.stat1 = STAT1_DAC_GAIN | STAT1_ADC_GAIN | + STAT1_ADC_ON | STAT1_DAC_ON; + uda->regs.data0_0 = DATA0_VOLUME(62 - ((DEF_VOLUME * 61) / 100)); + uda->regs.data0_1 = DATA1_BASS(0) | DATA1_TREBLE(0); + uda->regs.data0_2 = DATA2_PEAKAFTER | DATA2_DEEMP_NONE | + DATA2_FILTER_MAX; + uda->regs.ext0 = EXT0_CH1_GAIN(4); + uda->regs.ext1 = EXT1_CH2_GAIN(4); + uda->regs.ext2 = EXT2_MIXMODE_MIX | EXT2_MIC_GAIN(4); + uda->regs.ext4 = EXT4_AGC_ENABLE | EXT4_INPUT_GAIN(0); + uda->regs.ext5 = EXT5_INPUT_GAIN(0); + uda->regs.ext6 = EXT6_AGC_CONSTANT(3) | EXT6_AGC_LEVEL(0); + + clnt->driver_data = uda; + + return 0; +} + +static void uda1341_detach(struct l3_client *clnt) +{ + kfree(clnt->driver_data); +} + +static int +uda1341_command(struct l3_client *clnt, int cmd, void *arg) +{ + int ret = -EINVAL; + + if (_IOC_TYPE(cmd) == 'M') + ret = uda1341_mixer_ioctl(clnt, cmd, arg); + else if (cmd == L3_UDA1341_CONFIGURE) + ret = uda1341_configure(clnt, arg); + + return ret; +} + +static int uda1341_open(struct l3_client *clnt) +{ + uda1341_cmd_init(clnt); + return 0; +} + +static void uda1341_close(struct l3_client *clnt) +{ + struct uda1341 *uda = clnt->driver_data; + uda->active = 0; +} + +static struct l3_ops uda1341_ops = { + open: uda1341_open, + command: uda1341_command, + close: uda1341_close, +}; + +static struct l3_driver uda1341 = { + name: UDA1341_NAME, + attach_client: uda1341_attach, + detach_client: uda1341_detach, + ops: &uda1341_ops, + owner: THIS_MODULE, +}; + +static int __init uda1341_init(void) +{ + return l3_add_driver(&uda1341); +} + +static void __exit uda1341_exit(void) +{ + l3_del_driver(&uda1341); +} + +module_init(uda1341_init); +module_exit(uda1341_exit); + +MODULE_AUTHOR("Nicolas Pitre"); +MODULE_DESCRIPTION("Philips UDA1341 CODEC driver"); diff -urN orig/sound/oss/vidc.c linux/sound/oss/vidc.c --- orig/sound/oss/vidc.c Sun Apr 20 16:32:50 2003 +++ linux/sound/oss/vidc.c Mon May 5 23:12:30 2003 @@ -308,9 +308,10 @@ return -EINVAL; } -static void vidc_audio_dma_interrupt(void) +static irqreturn_t vidc_audio_dma_interrupt(void) { DMAbuf_outputintr(vidc_adev, 1); + return IRQ_HANDLED; } /* diff -urN orig/sound/oss/vidc.h linux/sound/oss/vidc.h --- orig/sound/oss/vidc.h Mon May 5 17:40:34 2003 +++ linux/sound/oss/vidc.h Mon May 5 23:12:55 2003 @@ -50,7 +50,7 @@ * Virtual DMA buffer exhausted */ -extern void (*dma_interrupt) (void); +extern irqreturn_t (*dma_interrupt) (void); /* * Virtual DMA buffer addresses diff -urN orig/sound/oss/vidc_fill.S linux/sound/oss/vidc_fill.S --- orig/sound/oss/vidc_fill.S Mon May 5 17:40:34 2003 +++ linux/sound/oss/vidc_fill.S Mon May 5 23:11:56 2003 @@ -133,10 +133,6 @@ * ip = corrupted */ -%%%%%%%%%%%%%%%%%%% -fixme! This funtion needs to return IRQ_HANDLED -%%%%%%%%%%%%%%%%%%% - ENTRY(vidc_sound_dma_irq) stmfd sp!, {r4 - r8, lr} ldr r8, =dma_start @@ -189,6 +185,7 @@ mov r0, #0x10 strneb r0, [ip, #IOMD_SD0CR] ldmfd sp!, {r4 - r8, lr} + mov r0, #1 @ IRQ_HANDLED teq r1, #0 @ If we have no more movne pc, lr teq r3, #0